import os , math , random , time
random .seed ( 42 )
# Data
if not os .path .exists ( "input.txt" ) :
import urllib .request
urllib .request .urlretrieve ( "https://r...content-available-to-author-only...t.com/karpathy/makemore/refs/heads/master/names.txt" , "input.txt" )
docs = [ l.strip ( ) for l in open ( "input.txt" ) .read ( ) .strip ( ) .split ( "\n " ) if l.strip ( ) ]
random .shuffle ( docs)
uchars = sorted ( set ( "" .join ( docs) ) )
BOS = len ( uchars)
vocab_size = len ( uchars) + 1
# Vector helpers
vadd = lambda a, b: [ ai + bi for ai, bi in zip ( a, b) ]
vdot = lambda a, b: sum ( ai * bi for ai, bi in zip ( a, b) )
vscale = lambda a, s: [ ai * s for ai in a]
vaccum = lambda dst, src: [ dst.__setitem__ ( j, dst[ j] + src[ j] ) for j in range ( len ( src) ) ]
# Config & params
n_embd, n_head, n_layer, block_size = 16 , 4 , 1 , 8
head_dim = n_embd // n_head
mat = lambda r, c, s= 0.02 : [ [ random .gauss ( 0 , s) for _ in range ( c) ] for _ in range ( r) ]
dlike = lambda d: { k: [ [ 0.0 ] *len ( r) for r in m] for k, m in d.items ( ) }
state_dict = { "wte" : mat( vocab_size, n_embd) , "wpe" : mat( block_size, n_embd) , "lm_head" : mat( vocab_size, n_embd) }
for i in range ( n_layer) :
state_dict |= { f"layer{i}.attn_wq" : mat( n_embd, n_embd) , f"layer{i}.attn_wk" : mat( n_embd, n_embd) ,
f"layer{i}.attn_wv" : mat( n_embd, n_embd) , f"layer{i}.attn_wo" : mat( n_embd, n_embd, 0 ) ,
f"layer{i}.mlp_fc1" : mat( 4 * n_embd, n_embd) , f"layer{i}.mlp_fc2" : mat( n_embd, 4 * n_embd, 0 ) }
# Ops
def linear( x, w) : return [ vdot( wr, x) for wr in w]
def softmax( z) :
mx = max ( z) ; e = [ math .exp ( v - mx) for v in z] ; s = sum ( e)
return vscale( e, 1 / s)
def rmsnorm( x) :
inv = ( sum ( v*v for v in x) / len ( x) + 1e-5 ) ** -0.5
return vscale( x, inv) , inv
def rmsnorm_b( dy, y, inv) :
s = sum ( dy[ j] * y[ j] for j in range ( len ( y) ) )
return [ inv * ( dy[ j] - y[ j] * s / len ( y) ) for j in range ( len ( y) ) ]
def linear_b( dy, w, x, dw) :
for j in range ( len ( dy) ) :
for k in range ( len ( x) ) :
dw[ j] [ k] += dy[ j] * x[ k]
return [ sum ( dy[ j] * w[ j] [ k] for j in range ( len ( dy) ) ) for k in range ( len ( x) ) ]
def softmax_b( dp, p) :
s = sum ( di * pi for di, pi in zip ( dp, p) )
return [ p[ i] * ( dp[ i] - s) for i in range ( len ( p) ) ]
# Forward pass (single position, shared by training and inference)
def forward_pos( tok, pos, keys, values, save= False ) :
x0, inv0 = rmsnorm( vadd( state_dict[ 'wte' ] [ tok] , state_dict[ 'wpe' ] [ pos] ) )
x, layers = x0, [ ]
for li in range ( n_layer) :
xn, ainv = rmsnorm( x)
q = linear( xn, state_dict[ f'layer{li}.attn_wq' ] )
keys[ li] .append ( linear( xn, state_dict[ f'layer{li}.attn_wk' ] ) )
values[ li] .append ( linear( xn, state_dict[ f'layer{li}.attn_wv' ] ) )
x_attn, attn_weights, n_ctx = [ 0.0 ] * n_embd, [ ] , len ( keys[ li] )
for h in range ( n_head) :
hs = h * head_dim
q_h = q[ hs:hs + head_dim]
k_h = [ keys[ li] [ t] [ hs:hs + head_dim] for t in range ( n_ctx) ]
v_h = [ values[ li] [ t] [ hs:hs + head_dim] for t in range ( n_ctx) ]
attn_logits = [ sum ( q_h[ j] * k_h[ t] [ j] for j in range ( head_dim) ) / head_dim**0.5 for t in range ( n_ctx) ]
attn_w = softmax( attn_logits)
attn_weights.append ( attn_w)
for j in range ( head_dim) :
x_attn[ hs + j] = sum ( attn_w[ t] * v_h[ t] [ j] for t in range ( n_ctx) )
x = vadd( linear( x_attn, state_dict[ f'layer{li}.attn_wo' ] ) , x)
x_norm, norm_inv = rmsnorm( x)
mlp_h1 = linear( x_norm, state_dict[ f'layer{li}.mlp_fc1' ] )
mlp_h1_act = [ max ( 0.0 , v) ** 2 for v in mlp_h1]
x = vadd( linear( mlp_h1_act, state_dict[ f'layer{li}.mlp_fc2' ] ) , x)
if save:
layers.append ( ( xn, ainv, q, x_attn, attn_weights, x_norm, norm_inv, mlp_h1, mlp_h1_act) )
return ( linear( x, state_dict[ 'lm_head' ] ) , ( tok, x0, inv0, layers, x) ) if save else linear( x, state_dict[ 'lm_head' ] )
# Train (Adam)
learning_rate, beta1, beta2, eps_adam = 1e-2 , 0.9 , 0.95 , 1e-8
num_steps = 5000
m_state, v_state = dlike( state_dict) , dlike( state_dict)
t0 = time .time ( )
for step in range ( num_steps) :
doc = docs[ step % len ( docs) ]
tokens = [ BOS] + [ uchars.index ( c) for c in doc] + [ BOS]
n = min ( block_size, len ( tokens) - 1 )
# Forward
keys, values, saved = [ [ ] for _ in range ( n_layer) ] , [ [ ] for _ in range ( n_layer) ] , [ ]
loss = 0.0
for pos in range ( n) :
logits, state = forward_pos( tokens[ pos] , pos, keys, values, save= True )
probs = softmax( logits)
loss -= math .log ( probs[ tokens[ pos + 1 ] ] ) / n
saved.append ( ( *state, probs) )
# Backward
dstate = dlike( state_dict)
dkeys = [ [ [ 0.0 ] * n_embd for _ in range ( n) ] for _ in range ( n_layer) ]
dvalues = [ [ [ 0.0 ] * n_embd for _ in range ( n) ] for _ in range ( n_layer) ]
for pos in range ( n - 1 , -1 , -1 ) :
tok, x0, inv0, layers, xf, probs = saved[ pos]
target_id = tokens[ pos + 1 ]
dx = linear_b( [ ( probs[ j] - ( j == target_id) ) / n for j in range ( vocab_size) ] , state_dict[ 'lm_head' ] , xf, dstate[ 'lm_head' ] )
for li in range ( n_layer - 1 , -1 , -1 ) :
xn, ainv, q, x_attn, attn_weights, x_norm, norm_inv, mlp_h1, mlp_h1_act = layers[ li]
dmlp_h1_act = linear_b( dx, state_dict[ f'layer{li}.mlp_fc2' ] , mlp_h1_act, dstate[ f'layer{li}.mlp_fc2' ] )
dxn = linear_b( [ dmlp_h1_act[ j] * 2 * mlp_h1[ j] if mlp_h1[ j] > 0 else 0 for j in range ( 4 * n_embd) ] ,
state_dict[ f'layer{li}.mlp_fc1' ] , x_norm, dstate[ f'layer{li}.mlp_fc1' ] )
dx = vadd( rmsnorm_b( dxn, x_norm, norm_inv) , dx)
dx_attn = linear_b( dx, state_dict[ f'layer{li}.attn_wo' ] , x_attn, dstate[ f'layer{li}.attn_wo' ] )
dq = [ 0.0 ] * n_embd
for h in range ( n_head) :
hs = h * head_dim
q_h, attn_w = q[ hs:hs + head_dim] , attn_weights[ h]
k_h = [ keys[ li] [ t] [ hs:hs + head_dim] for t in range ( pos + 1 ) ]
v_h = [ values[ li] [ t] [ hs:hs + head_dim] for t in range ( pos + 1 ) ]
dattn = softmax_b( [ sum ( dx_attn[ hs + j] * v_h[ t] [ j] for j in range ( head_dim) ) for t in range ( pos + 1 ) ] , attn_w)
for t in range ( pos + 1 ) :
c = dattn[ t] / head_dim**0.5
for j in range ( head_dim) :
dvalues[ li] [ t] [ hs + j] += dx_attn[ hs + j] * attn_w[ t]
dq[ hs + j] += c * k_h[ t] [ j]
dkeys[ li] [ t] [ hs + j] += c * q_h[ j]
dxn = linear_b( dq, state_dict[ f'layer{li}.attn_wq' ] , xn, dstate[ f'layer{li}.attn_wq' ] )
dxn = vadd( dxn, linear_b( dkeys[ li] [ pos] , state_dict[ f'layer{li}.attn_wk' ] , xn, dstate[ f'layer{li}.attn_wk' ] ) )
dxn = vadd( dxn, linear_b( dvalues[ li] [ pos] , state_dict[ f'layer{li}.attn_wv' ] , xn, dstate[ f'layer{li}.attn_wv' ] ) )
dx = vadd( rmsnorm_b( dxn, xn, ainv) , dx)
demb = rmsnorm_b( dx, x0, inv0)
vaccum( dstate[ 'wte' ] [ tok] , demb)
vaccum( dstate[ 'wpe' ] [ pos] , demb)
# Adam
lr_t = learning_rate * 0.5 * ( 1 + math .cos ( math .pi * step / num_steps) )
m_hat_corr, v_hat_corr = 1 - beta1**( step+1 ) , 1 - beta2**( step+1 )
for k in state_dict:
for i, row in enumerate ( state_dict[ k] ) :
for j in range ( len ( row) ) :
g = dstate[ k] [ i] [ j]
m_state[ k] [ i] [ j] = beta1 * m_state[ k] [ i] [ j] + ( 1 - beta1) * g
v_state[ k] [ i] [ j] = beta2 * v_state[ k] [ i] [ j] + ( 1 - beta2) * g**2
state_dict[ k] [ i] [ j] -= lr_t * ( m_state[ k] [ i] [ j] / m_hat_corr) / ( ( v_state[ k] [ i] [ j] / v_hat_corr) **0.5 + eps_adam)
print ( f"step {step+1}/{num_steps} loss: {loss:.4f}" )
print ( f"\n Total training time: {time.time() - t0:.2f}s" )
# Inference
temperature = 2.0
for sample_idx in range ( 20 ) :
keys, values = [ [ ] for _ in range ( n_layer) ] , [ [ ] for _ in range ( n_layer) ]
token_id, sample = BOS, [ ]
for pos in range ( block_size) :
probs = softmax( vscale( forward_pos( token_id, pos, keys, values) , temperature) )
token_id = random .choices ( range ( vocab_size) , weights= probs) [ 0 ]
if token_id == BOS:
break
sample.append ( uchars[ token_id] )
print ( f"{sample_idx+1}: {''.join(sample)}" )
# your code goes here
aW1wb3J0IG9zLCBtYXRoLCByYW5kb20sIHRpbWUKCnJhbmRvbS5zZWVkKDQyKQoKIyBEYXRhCmlmIG5vdCBvcy5wYXRoLmV4aXN0cygiaW5wdXQudHh0Iik6CiAgaW1wb3J0IHVybGxpYi5yZXF1ZXN0CiAgdXJsbGliLnJlcXVlc3QudXJscmV0cmlldmUoImh0dHBzOi8vci4uLmNvbnRlbnQtYXZhaWxhYmxlLXRvLWF1dGhvci1vbmx5Li4udC5jb20va2FycGF0aHkvbWFrZW1vcmUvcmVmcy9oZWFkcy9tYXN0ZXIvbmFtZXMudHh0IiwgImlucHV0LnR4dCIpCmRvY3MgPSBbbC5zdHJpcCgpIGZvciBsIGluIG9wZW4oImlucHV0LnR4dCIpLnJlYWQoKS5zdHJpcCgpLnNwbGl0KCJcbiIpIGlmIGwuc3RyaXAoKV0KcmFuZG9tLnNodWZmbGUoZG9jcykKdWNoYXJzID0gc29ydGVkKHNldCgiIi5qb2luKGRvY3MpKSkKQk9TID0gbGVuKHVjaGFycykKdm9jYWJfc2l6ZSA9IGxlbih1Y2hhcnMpICsgMQoKIyBWZWN0b3IgaGVscGVycwp2YWRkID0gbGFtYmRhIGEsIGI6IFthaSArIGJpIGZvciBhaSwgYmkgaW4gemlwKGEsIGIpXQp2ZG90ID0gbGFtYmRhIGEsIGI6IHN1bShhaSAqIGJpIGZvciBhaSwgYmkgaW4gemlwKGEsIGIpKQp2c2NhbGUgPSBsYW1iZGEgYSwgczogW2FpICogcyBmb3IgYWkgaW4gYV0KdmFjY3VtID0gbGFtYmRhIGRzdCwgc3JjOiBbZHN0Ll9fc2V0aXRlbV9fKGosIGRzdFtqXSArIHNyY1tqXSkgZm9yIGogaW4gcmFuZ2UobGVuKHNyYykpXQoKIyBDb25maWcgJiBwYXJhbXMKbl9lbWJkLCBuX2hlYWQsIG5fbGF5ZXIsIGJsb2NrX3NpemUgPSAxNiwgNCwgMSwgOApoZWFkX2RpbSA9IG5fZW1iZCAvLyBuX2hlYWQKbWF0ID0gbGFtYmRhIHIsIGMsIHM9MC4wMjogW1tyYW5kb20uZ2F1c3MoMCwgcykgZm9yIF8gaW4gcmFuZ2UoYyldIGZvciBfIGluIHJhbmdlKHIpXQpkbGlrZSA9IGxhbWJkYSBkOiB7azogW1swLjBdKmxlbihyKSBmb3IgciBpbiBtXSBmb3IgaywgbSBpbiBkLml0ZW1zKCl9CnN0YXRlX2RpY3QgPSB7Ind0ZSI6IG1hdCh2b2NhYl9zaXplLCBuX2VtYmQpLCAid3BlIjogbWF0KGJsb2NrX3NpemUsIG5fZW1iZCksICJsbV9oZWFkIjogbWF0KHZvY2FiX3NpemUsIG5fZW1iZCl9CmZvciBpIGluIHJhbmdlKG5fbGF5ZXIpOgogIHN0YXRlX2RpY3QgfD0ge2YibGF5ZXJ7aX0uYXR0bl93cSI6IG1hdChuX2VtYmQsIG5fZW1iZCksIGYibGF5ZXJ7aX0uYXR0bl93ayI6IG1hdChuX2VtYmQsIG5fZW1iZCksCiAgICAgICAgICAgICAgICAgZiJsYXllcntpfS5hdHRuX3d2IjogbWF0KG5fZW1iZCwgbl9lbWJkKSwgZiJsYXllcntpfS5hdHRuX3dvIjogbWF0KG5fZW1iZCwgbl9lbWJkLCAwKSwKICAgICAgICAgICAgICAgICBmImxheWVye2l9Lm1scF9mYzEiOiBtYXQoNCAqIG5fZW1iZCwgbl9lbWJkKSwgZiJsYXllcntpfS5tbHBfZmMyIjogbWF0KG5fZW1iZCwgNCAqIG5fZW1iZCwgMCl9CgojIE9wcwpkZWYgbGluZWFyKHgsIHcpOiByZXR1cm4gW3Zkb3Qod3IsIHgpIGZvciB3ciBpbiB3XQoKZGVmIHNvZnRtYXgoeik6CiAgbXggPSBtYXgoeik7IGUgPSBbbWF0aC5leHAodiAtIG14KSBmb3IgdiBpbiB6XTsgcyA9IHN1bShlKQogIHJldHVybiB2c2NhbGUoZSwgMSAvIHMpCgpkZWYgcm1zbm9ybSh4KToKICBpbnYgPSAoc3VtKHYqdiBmb3IgdiBpbiB4KSAvIGxlbih4KSArIDFlLTUpICoqIC0wLjUKICByZXR1cm4gdnNjYWxlKHgsIGludiksIGludgoKZGVmIHJtc25vcm1fYihkeSwgeSwgaW52KToKICBzID0gc3VtKGR5W2pdICogeVtqXSBmb3IgaiBpbiByYW5nZShsZW4oeSkpKQogIHJldHVybiBbaW52ICogKGR5W2pdIC0geVtqXSAqIHMgLyBsZW4oeSkpIGZvciBqIGluIHJhbmdlKGxlbih5KSldCgpkZWYgbGluZWFyX2IoZHksIHcsIHgsIGR3KToKICBmb3IgaiBpbiByYW5nZShsZW4oZHkpKToKICAgIGZvciBrIGluIHJhbmdlKGxlbih4KSk6CiAgICAgIGR3W2pdW2tdICs9IGR5W2pdICogeFtrXQogIHJldHVybiBbc3VtKGR5W2pdICogd1tqXVtrXSBmb3IgaiBpbiByYW5nZShsZW4oZHkpKSkgZm9yIGsgaW4gcmFuZ2UobGVuKHgpKV0KCmRlZiBzb2Z0bWF4X2IoZHAsIHApOgogIHMgPSBzdW0oZGkgKiBwaSBmb3IgZGksIHBpIGluIHppcChkcCwgcCkpCiAgcmV0dXJuIFtwW2ldICogKGRwW2ldIC0gcykgZm9yIGkgaW4gcmFuZ2UobGVuKHApKV0KCiMgRm9yd2FyZCBwYXNzIChzaW5nbGUgcG9zaXRpb24sIHNoYXJlZCBieSB0cmFpbmluZyBhbmQgaW5mZXJlbmNlKQpkZWYgZm9yd2FyZF9wb3ModG9rLCBwb3MsIGtleXMsIHZhbHVlcywgc2F2ZT1GYWxzZSk6CiAgeDAsIGludjAgPSBybXNub3JtKHZhZGQoc3RhdGVfZGljdFsnd3RlJ11bdG9rXSwgc3RhdGVfZGljdFsnd3BlJ11bcG9zXSkpCiAgeCwgbGF5ZXJzID0geDAsIFtdCiAgZm9yIGxpIGluIHJhbmdlKG5fbGF5ZXIpOgogICAgeG4sIGFpbnYgPSBybXNub3JtKHgpCiAgICBxID0gbGluZWFyKHhuLCBzdGF0ZV9kaWN0W2YnbGF5ZXJ7bGl9LmF0dG5fd3EnXSkKICAgIGtleXNbbGldLmFwcGVuZChsaW5lYXIoeG4sIHN0YXRlX2RpY3RbZidsYXllcntsaX0uYXR0bl93ayddKSkKICAgIHZhbHVlc1tsaV0uYXBwZW5kKGxpbmVhcih4biwgc3RhdGVfZGljdFtmJ2xheWVye2xpfS5hdHRuX3d2J10pKQogICAgeF9hdHRuLCBhdHRuX3dlaWdodHMsIG5fY3R4ID0gWzAuMF0gKiBuX2VtYmQsIFtdLCBsZW4oa2V5c1tsaV0pCiAgICBmb3IgaCBpbiByYW5nZShuX2hlYWQpOgogICAgICBocyA9IGggKiBoZWFkX2RpbQogICAgICBxX2ggPSBxW2hzOmhzICsgaGVhZF9kaW1dCiAgICAgIGtfaCA9IFtrZXlzW2xpXVt0XVtoczpocyArIGhlYWRfZGltXSBmb3IgdCBpbiByYW5nZShuX2N0eCldCiAgICAgIHZfaCA9IFt2YWx1ZXNbbGldW3RdW2hzOmhzICsgaGVhZF9kaW1dIGZvciB0IGluIHJhbmdlKG5fY3R4KV0KICAgICAgYXR0bl9sb2dpdHMgPSBbc3VtKHFfaFtqXSAqIGtfaFt0XVtqXSBmb3IgaiBpbiByYW5nZShoZWFkX2RpbSkpIC8gaGVhZF9kaW0qKjAuNSBmb3IgdCBpbiByYW5nZShuX2N0eCldCiAgICAgIGF0dG5fdyA9IHNvZnRtYXgoYXR0bl9sb2dpdHMpCiAgICAgIGF0dG5fd2VpZ2h0cy5hcHBlbmQoYXR0bl93KQogICAgICBmb3IgaiBpbiByYW5nZShoZWFkX2RpbSk6CiAgICAgICAgeF9hdHRuW2hzICsgal0gPSBzdW0oYXR0bl93W3RdICogdl9oW3RdW2pdIGZvciB0IGluIHJhbmdlKG5fY3R4KSkKICAgIHggPSB2YWRkKGxpbmVhcih4X2F0dG4sIHN0YXRlX2RpY3RbZidsYXllcntsaX0uYXR0bl93byddKSwgeCkKICAgIHhfbm9ybSwgbm9ybV9pbnYgPSBybXNub3JtKHgpCiAgICBtbHBfaDEgPSBsaW5lYXIoeF9ub3JtLCBzdGF0ZV9kaWN0W2YnbGF5ZXJ7bGl9Lm1scF9mYzEnXSkKICAgIG1scF9oMV9hY3QgPSBbbWF4KDAuMCwgdikgKiogMiBmb3IgdiBpbiBtbHBfaDFdCiAgICB4ID0gdmFkZChsaW5lYXIobWxwX2gxX2FjdCwgc3RhdGVfZGljdFtmJ2xheWVye2xpfS5tbHBfZmMyJ10pLCB4KQogICAgaWYgc2F2ZToKICAgICAgbGF5ZXJzLmFwcGVuZCgoeG4sIGFpbnYsIHEsIHhfYXR0biwgYXR0bl93ZWlnaHRzLCB4X25vcm0sIG5vcm1faW52LCBtbHBfaDEsIG1scF9oMV9hY3QpKQogIHJldHVybiAobGluZWFyKHgsIHN0YXRlX2RpY3RbJ2xtX2hlYWQnXSksICh0b2ssIHgwLCBpbnYwLCBsYXllcnMsIHgpKSBpZiBzYXZlIGVsc2UgbGluZWFyKHgsIHN0YXRlX2RpY3RbJ2xtX2hlYWQnXSkKCiMgVHJhaW4gKEFkYW0pCmxlYXJuaW5nX3JhdGUsIGJldGExLCBiZXRhMiwgZXBzX2FkYW0gPSAxZS0yLCAwLjksIDAuOTUsIDFlLTgKbnVtX3N0ZXBzID0gNTAwMAptX3N0YXRlLCB2X3N0YXRlID0gZGxpa2Uoc3RhdGVfZGljdCksIGRsaWtlKHN0YXRlX2RpY3QpCgp0MCA9IHRpbWUudGltZSgpCmZvciBzdGVwIGluIHJhbmdlKG51bV9zdGVwcyk6CiAgZG9jID0gZG9jc1tzdGVwICUgbGVuKGRvY3MpXQogIHRva2VucyA9IFtCT1NdICsgW3VjaGFycy5pbmRleChjKSBmb3IgYyBpbiBkb2NdICsgW0JPU10KICBuID0gbWluKGJsb2NrX3NpemUsIGxlbih0b2tlbnMpIC0gMSkKCiAgIyBGb3J3YXJkCiAga2V5cywgdmFsdWVzLCBzYXZlZCA9IFtbXSBmb3IgXyBpbiByYW5nZShuX2xheWVyKV0sIFtbXSBmb3IgXyBpbiByYW5nZShuX2xheWVyKV0sIFtdCiAgbG9zcyA9IDAuMAogIGZvciBwb3MgaW4gcmFuZ2Uobik6CiAgICBsb2dpdHMsIHN0YXRlID0gZm9yd2FyZF9wb3ModG9rZW5zW3Bvc10sIHBvcywga2V5cywgdmFsdWVzLCBzYXZlPVRydWUpCiAgICBwcm9icyA9IHNvZnRtYXgobG9naXRzKQogICAgbG9zcyAtPSBtYXRoLmxvZyhwcm9ic1t0b2tlbnNbcG9zICsgMV1dKSAvIG4KICAgIHNhdmVkLmFwcGVuZCgoKnN0YXRlLCBwcm9icykpCgogICMgQmFja3dhcmQKICBkc3RhdGUgPSBkbGlrZShzdGF0ZV9kaWN0KQogIGRrZXlzID0gW1tbMC4wXSAqIG5fZW1iZCBmb3IgXyBpbiByYW5nZShuKV0gZm9yIF8gaW4gcmFuZ2Uobl9sYXllcildCiAgZHZhbHVlcyA9IFtbWzAuMF0gKiBuX2VtYmQgZm9yIF8gaW4gcmFuZ2UobildIGZvciBfIGluIHJhbmdlKG5fbGF5ZXIpXQogIGZvciBwb3MgaW4gcmFuZ2UobiAtIDEsIC0xLCAtMSk6CiAgICB0b2ssIHgwLCBpbnYwLCBsYXllcnMsIHhmLCBwcm9icyA9IHNhdmVkW3Bvc10KICAgIHRhcmdldF9pZCA9IHRva2Vuc1twb3MgKyAxXQogICAgZHggPSBsaW5lYXJfYihbKHByb2JzW2pdIC0gKGogPT0gdGFyZ2V0X2lkKSkgLyBuIGZvciBqIGluIHJhbmdlKHZvY2FiX3NpemUpXSwgc3RhdGVfZGljdFsnbG1faGVhZCddLCB4ZiwgZHN0YXRlWydsbV9oZWFkJ10pCiAgICBmb3IgbGkgaW4gcmFuZ2Uobl9sYXllciAtIDEsIC0xLCAtMSk6CiAgICAgIHhuLCBhaW52LCBxLCB4X2F0dG4sIGF0dG5fd2VpZ2h0cywgeF9ub3JtLCBub3JtX2ludiwgbWxwX2gxLCBtbHBfaDFfYWN0ID0gbGF5ZXJzW2xpXQogICAgICBkbWxwX2gxX2FjdCA9IGxpbmVhcl9iKGR4LCBzdGF0ZV9kaWN0W2YnbGF5ZXJ7bGl9Lm1scF9mYzInXSwgbWxwX2gxX2FjdCwgZHN0YXRlW2YnbGF5ZXJ7bGl9Lm1scF9mYzInXSkKICAgICAgZHhuID0gbGluZWFyX2IoW2RtbHBfaDFfYWN0W2pdICogMiAqIG1scF9oMVtqXSBpZiBtbHBfaDFbal0gPiAwIGVsc2UgMCBmb3IgaiBpbiByYW5nZSg0ICogbl9lbWJkKV0sCiAgICAgICAgICAgICAgICAgICAgIHN0YXRlX2RpY3RbZidsYXllcntsaX0ubWxwX2ZjMSddLCB4X25vcm0sIGRzdGF0ZVtmJ2xheWVye2xpfS5tbHBfZmMxJ10pCiAgICAgIGR4ID0gdmFkZChybXNub3JtX2IoZHhuLCB4X25vcm0sIG5vcm1faW52KSwgZHgpCiAgICAgIGR4X2F0dG4gPSBsaW5lYXJfYihkeCwgc3RhdGVfZGljdFtmJ2xheWVye2xpfS5hdHRuX3dvJ10sIHhfYXR0biwgZHN0YXRlW2YnbGF5ZXJ7bGl9LmF0dG5fd28nXSkKICAgICAgZHEgPSBbMC4wXSAqIG5fZW1iZAogICAgICBmb3IgaCBpbiByYW5nZShuX2hlYWQpOgogICAgICAgIGhzID0gaCAqIGhlYWRfZGltCiAgICAgICAgcV9oLCBhdHRuX3cgPSBxW2hzOmhzICsgaGVhZF9kaW1dLCBhdHRuX3dlaWdodHNbaF0KICAgICAgICBrX2ggPSBba2V5c1tsaV1bdF1baHM6aHMgKyBoZWFkX2RpbV0gZm9yIHQgaW4gcmFuZ2UocG9zICsgMSldCiAgICAgICAgdl9oID0gW3ZhbHVlc1tsaV1bdF1baHM6aHMgKyBoZWFkX2RpbV0gZm9yIHQgaW4gcmFuZ2UocG9zICsgMSldCiAgICAgICAgZGF0dG4gPSBzb2Z0bWF4X2IoW3N1bShkeF9hdHRuW2hzICsgal0gKiB2X2hbdF1bal0gZm9yIGogaW4gcmFuZ2UoaGVhZF9kaW0pKSBmb3IgdCBpbiByYW5nZShwb3MgKyAxKV0sIGF0dG5fdykKICAgICAgICBmb3IgdCBpbiByYW5nZShwb3MgKyAxKToKICAgICAgICAgIGMgPSBkYXR0blt0XSAvIGhlYWRfZGltKiowLjUKICAgICAgICAgIGZvciBqIGluIHJhbmdlKGhlYWRfZGltKToKICAgICAgICAgICAgZHZhbHVlc1tsaV1bdF1baHMgKyBqXSArPSBkeF9hdHRuW2hzICsgal0gKiBhdHRuX3dbdF0KICAgICAgICAgICAgZHFbaHMgKyBqXSArPSBjICoga19oW3RdW2pdCiAgICAgICAgICAgIGRrZXlzW2xpXVt0XVtocyArIGpdICs9IGMgKiBxX2hbal0KICAgICAgZHhuID0gbGluZWFyX2IoZHEsIHN0YXRlX2RpY3RbZidsYXllcntsaX0uYXR0bl93cSddLCB4biwgZHN0YXRlW2YnbGF5ZXJ7bGl9LmF0dG5fd3EnXSkKICAgICAgZHhuID0gdmFkZChkeG4sIGxpbmVhcl9iKGRrZXlzW2xpXVtwb3NdLCBzdGF0ZV9kaWN0W2YnbGF5ZXJ7bGl9LmF0dG5fd2snXSwgeG4sIGRzdGF0ZVtmJ2xheWVye2xpfS5hdHRuX3drJ10pKQogICAgICBkeG4gPSB2YWRkKGR4biwgbGluZWFyX2IoZHZhbHVlc1tsaV1bcG9zXSwgc3RhdGVfZGljdFtmJ2xheWVye2xpfS5hdHRuX3d2J10sIHhuLCBkc3RhdGVbZidsYXllcntsaX0uYXR0bl93diddKSkKICAgICAgZHggPSB2YWRkKHJtc25vcm1fYihkeG4sIHhuLCBhaW52KSwgZHgpCiAgICBkZW1iID0gcm1zbm9ybV9iKGR4LCB4MCwgaW52MCkKICAgIHZhY2N1bShkc3RhdGVbJ3d0ZSddW3Rva10sIGRlbWIpCiAgICB2YWNjdW0oZHN0YXRlWyd3cGUnXVtwb3NdLCBkZW1iKQoKICAjIEFkYW0KICBscl90ID0gbGVhcm5pbmdfcmF0ZSAqIDAuNSAqICgxICsgbWF0aC5jb3MobWF0aC5waSAqIHN0ZXAgLyBudW1fc3RlcHMpKQogIG1faGF0X2NvcnIsIHZfaGF0X2NvcnIgPSAxIC0gYmV0YTEqKihzdGVwKzEpLCAxIC0gYmV0YTIqKihzdGVwKzEpCiAgZm9yIGsgaW4gc3RhdGVfZGljdDoKICAgIGZvciBpLCByb3cgaW4gZW51bWVyYXRlKHN0YXRlX2RpY3Rba10pOgogICAgICBmb3IgaiBpbiByYW5nZShsZW4ocm93KSk6CiAgICAgICAgZyA9IGRzdGF0ZVtrXVtpXVtqXQogICAgICAgIG1fc3RhdGVba11baV1bal0gPSBiZXRhMSAqIG1fc3RhdGVba11baV1bal0gKyAoMSAtIGJldGExKSAqIGcKICAgICAgICB2X3N0YXRlW2tdW2ldW2pdID0gYmV0YTIgKiB2X3N0YXRlW2tdW2ldW2pdICsgKDEgLSBiZXRhMikgKiBnKioyCiAgICAgICAgc3RhdGVfZGljdFtrXVtpXVtqXSAtPSBscl90ICogKG1fc3RhdGVba11baV1bal0gLyBtX2hhdF9jb3JyKSAvICgodl9zdGF0ZVtrXVtpXVtqXSAvIHZfaGF0X2NvcnIpKiowLjUgKyBlcHNfYWRhbSkKICBwcmludChmInN0ZXAge3N0ZXArMX0ve251bV9zdGVwc30gIGxvc3M6IHtsb3NzOi40Zn0iKQoKcHJpbnQoZiJcblRvdGFsIHRyYWluaW5nIHRpbWU6IHt0aW1lLnRpbWUoKSAtIHQwOi4yZn1zIikKCiMgSW5mZXJlbmNlCnRlbXBlcmF0dXJlID0gMi4wCmZvciBzYW1wbGVfaWR4IGluIHJhbmdlKDIwKToKICBrZXlzLCB2YWx1ZXMgPSBbW10gZm9yIF8gaW4gcmFuZ2Uobl9sYXllcildLCBbW10gZm9yIF8gaW4gcmFuZ2Uobl9sYXllcildCiAgdG9rZW5faWQsIHNhbXBsZSA9IEJPUywgW10KICBmb3IgcG9zIGluIHJhbmdlKGJsb2NrX3NpemUpOgogICAgcHJvYnMgPSBzb2Z0bWF4KHZzY2FsZShmb3J3YXJkX3Bvcyh0b2tlbl9pZCwgcG9zLCBrZXlzLCB2YWx1ZXMpLCB0ZW1wZXJhdHVyZSkpCiAgICB0b2tlbl9pZCA9IHJhbmRvbS5jaG9pY2VzKHJhbmdlKHZvY2FiX3NpemUpLCB3ZWlnaHRzPXByb2JzKVswXQogICAgaWYgdG9rZW5faWQgPT0gQk9TOgogICAgICBicmVhawogICAgc2FtcGxlLmFwcGVuZCh1Y2hhcnNbdG9rZW5faWRdKQogIHByaW50KGYie3NhbXBsZV9pZHgrMX06IHsnJy5qb2luKHNhbXBsZSl9IikKIyB5b3VyIGNvZGUgZ29lcyBoZXJl