# your code goes here
import numpy as np
import sys
MAX_SIZE = 25
class Latin( object ) :
def __init__ ( self , size) :
self .size = size
self .possible = np.full ( ( size, size, size) , True , dtype= bool )
self .count = np.full ( ( 3 , size, size) , size, dtype= int )
self .chosen = np.full ( ( 3 , size, size) , -1 , dtype= int )
def decision( self ) :
axis, u, v = np.unravel_index ( np.where ( self .chosen == -1 , self .count , self .size ) .argmin ( ) , self .count .shape )
assert self .count [ axis, u, v] != 0
if self .chosen [ axis, u, v] == -1 :
ws, = np.rollaxis ( self .possible , axis) [ :, u, v] .nonzero ( )
return axis, u, v, list ( ws)
else :
return None , None , None , None
def choose( self , axis, u, v, w) :
t = [ u, v]
t[ axis:axis] = [ w]
i, j, k = t
assert self .chosen [ 0 , j, k] == self .chosen [ 1 , i, k] == self .chosen [ 2 , i, j] == -1
self .count [ 1 , :, k] -= self .possible [ :, j, k]
self .count [ 2 , :, j] -= self .possible [ :, j, k]
self .count [ 0 , :, k] -= self .possible [ i, :, k]
self .count [ 2 , i, :] -= self .possible [ i, :, k]
self .count [ 0 , j, :] -= self .possible [ i, j, :]
self .count [ 1 , i, :] -= self .possible [ i, j, :]
self .count [ 0 , j, k] = self .count [ 1 , i, k] = self .count [ 2 , i, j] = 1
self .possible [ i, j, :] = self .possible [ i, :, k] = self .possible [ :, j, k] = False
self .possible [ i, j, k] = True
self .chosen [ 0 , j, k] = i
self .chosen [ 1 , i, k] = j
self .chosen [ 2 , i, j] = k
def compress( square) :
square = np.array ( square, dtype= int )
size, size = square.shape
assert 0 < size <= MAX_SIZE
latin = Latin( size)
chosen = np.stack ( np.argmax ( square[ :, :, np.newaxis ] == np.arange ( size) [ np.newaxis , np.newaxis , :] , axis= axis) for axis in range ( 3 ) )
num, denom = size - 1 , MAX_SIZE
while True :
axis, u, v, ws = latin.decision ( )
if axis is None :
break
w = chosen[ axis, u, v]
num += ws.index ( w) *denom
denom *= len ( ws)
latin.choose ( axis, u, v, w)
return '{:b}' .format ( num + 1 ) [ 1 :]
def decompress( bits) :
num = int ( '1' + bits, 2 ) - 1
size = num % MAX_SIZE + 1
num //= MAX_SIZE
latin = Latin( size)
while True :
axis, u, v, ws = latin.decision ( )
if axis is None :
break
latin.choose ( axis, u, v, ws[ num % len ( ws) ] )
num //= len ( ws)
return latin.chosen [ 2 ] .tolist ( )
total = 0
while True :
square = [ list ( map ( int , l.split ( ',' ) ) ) for l in iter ( lambda : next( sys .stdin ) , '\n ' ) ]
print square
if not square:
break
bits = compress( square)
assert set ( bits) <= { '0' , '1' }
assert square == decompress( bits)
print ( 'Square {}: {} bits' .format ( len ( square) , len ( bits) ) )
total += len ( bits)
print ( 'Total: {} bits = {} bytes' .format ( total, total/8.0 ) )
IyB5b3VyIGNvZGUgZ29lcyBoZXJlCmltcG9ydCBudW1weSBhcyBucAppbXBvcnQgc3lzCgpNQVhfU0laRSA9IDI1CgpjbGFzcyBMYXRpbihvYmplY3QpOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIHNpemUpOgogICAgICAgIHNlbGYuc2l6ZSA9IHNpemUKICAgICAgICBzZWxmLnBvc3NpYmxlID0gbnAuZnVsbCgoc2l6ZSwgc2l6ZSwgc2l6ZSksIFRydWUsIGR0eXBlPWJvb2wpCiAgICAgICAgc2VsZi5jb3VudCA9IG5wLmZ1bGwoKDMsIHNpemUsIHNpemUpLCBzaXplLCBkdHlwZT1pbnQpCiAgICAgICAgc2VsZi5jaG9zZW4gPSBucC5mdWxsKCgzLCBzaXplLCBzaXplKSwgLTEsIGR0eXBlPWludCkKCiAgICBkZWYgZGVjaXNpb24oc2VsZik6CiAgICAgICAgYXhpcywgdSwgdiA9IG5wLnVucmF2ZWxfaW5kZXgobnAud2hlcmUoc2VsZi5jaG9zZW4gPT0gLTEsIHNlbGYuY291bnQsIHNlbGYuc2l6ZSkuYXJnbWluKCksIHNlbGYuY291bnQuc2hhcGUpCiAgICAgICAgYXNzZXJ0IHNlbGYuY291bnRbYXhpcywgdSwgdl0gIT0gMAogICAgICAgIGlmIHNlbGYuY2hvc2VuW2F4aXMsIHUsIHZdID09IC0xOgogICAgICAgICAgICB3cywgPSBucC5yb2xsYXhpcyhzZWxmLnBvc3NpYmxlLCBheGlzKVs6LCB1LCB2XS5ub256ZXJvKCkKICAgICAgICAgICAgcmV0dXJuIGF4aXMsIHUsIHYsIGxpc3Qod3MpCiAgICAgICAgZWxzZToKICAgICAgICAgICAgcmV0dXJuIE5vbmUsIE5vbmUsIE5vbmUsIE5vbmUKCiAgICBkZWYgY2hvb3NlKHNlbGYsIGF4aXMsIHUsIHYsIHcpOgogICAgICAgIHQgPSBbdSwgdl0KICAgICAgICB0W2F4aXM6YXhpc10gPSBbd10KICAgICAgICBpLCBqLCBrID0gdAogICAgICAgIGFzc2VydCBzZWxmLmNob3NlblswLCBqLCBrXSA9PSBzZWxmLmNob3NlblsxLCBpLCBrXSA9PSBzZWxmLmNob3NlblsyLCBpLCBqXSA9PSAtMQoKICAgICAgICBzZWxmLmNvdW50WzEsIDosIGtdIC09IHNlbGYucG9zc2libGVbOiwgaiwga10KICAgICAgICBzZWxmLmNvdW50WzIsIDosIGpdIC09IHNlbGYucG9zc2libGVbOiwgaiwga10KICAgICAgICBzZWxmLmNvdW50WzAsIDosIGtdIC09IHNlbGYucG9zc2libGVbaSwgOiwga10KICAgICAgICBzZWxmLmNvdW50WzIsIGksIDpdIC09IHNlbGYucG9zc2libGVbaSwgOiwga10KICAgICAgICBzZWxmLmNvdW50WzAsIGosIDpdIC09IHNlbGYucG9zc2libGVbaSwgaiwgOl0KICAgICAgICBzZWxmLmNvdW50WzEsIGksIDpdIC09IHNlbGYucG9zc2libGVbaSwgaiwgOl0KICAgICAgICBzZWxmLmNvdW50WzAsIGosIGtdID0gc2VsZi5jb3VudFsxLCBpLCBrXSA9IHNlbGYuY291bnRbMiwgaSwgal0gPSAxCiAgICAgICAgc2VsZi5wb3NzaWJsZVtpLCBqLCA6XSA9IHNlbGYucG9zc2libGVbaSwgOiwga10gPSBzZWxmLnBvc3NpYmxlWzosIGosIGtdID0gRmFsc2UKICAgICAgICBzZWxmLnBvc3NpYmxlW2ksIGosIGtdID0gVHJ1ZQogICAgICAgIHNlbGYuY2hvc2VuWzAsIGosIGtdID0gaQogICAgICAgIHNlbGYuY2hvc2VuWzEsIGksIGtdID0gagogICAgICAgIHNlbGYuY2hvc2VuWzIsIGksIGpdID0gawoKZGVmIGNvbXByZXNzKHNxdWFyZSk6CiAgICBzcXVhcmUgPSBucC5hcnJheShzcXVhcmUsIGR0eXBlPWludCkKICAgIHNpemUsIHNpemUgPSBzcXVhcmUuc2hhcGUKICAgIGFzc2VydCAwIDwgc2l6ZSA8PSBNQVhfU0laRQogICAgbGF0aW4gPSBMYXRpbihzaXplKQogICAgY2hvc2VuID0gbnAuc3RhY2sobnAuYXJnbWF4KHNxdWFyZVs6LCA6LCBucC5uZXdheGlzXSA9PSBucC5hcmFuZ2Uoc2l6ZSlbbnAubmV3YXhpcywgbnAubmV3YXhpcywgOl0sIGF4aXM9YXhpcykgZm9yIGF4aXMgaW4gcmFuZ2UoMykpCiAgICBudW0sIGRlbm9tID0gc2l6ZSAtIDEsIE1BWF9TSVpFCiAgICB3aGlsZSBUcnVlOgogICAgICAgIGF4aXMsIHUsIHYsIHdzID0gbGF0aW4uZGVjaXNpb24oKQogICAgICAgIGlmIGF4aXMgaXMgTm9uZToKICAgICAgICAgICAgYnJlYWsKICAgICAgICB3ID0gY2hvc2VuW2F4aXMsIHUsIHZdCiAgICAgICAgbnVtICs9IHdzLmluZGV4KHcpKmRlbm9tCiAgICAgICAgZGVub20gKj0gbGVuKHdzKQogICAgICAgIGxhdGluLmNob29zZShheGlzLCB1LCB2LCB3KQogICAgcmV0dXJuICd7OmJ9Jy5mb3JtYXQobnVtICsgMSlbMTpdCgpkZWYgZGVjb21wcmVzcyhiaXRzKToKICAgIG51bSA9IGludCgnMScgKyBiaXRzLCAyKSAtIDEKICAgIHNpemUgPSBudW0gJSBNQVhfU0laRSArIDEKICAgIG51bSAvLz0gTUFYX1NJWkUKICAgIGxhdGluID0gTGF0aW4oc2l6ZSkKICAgIHdoaWxlIFRydWU6CiAgICAgICAgYXhpcywgdSwgdiwgd3MgPSBsYXRpbi5kZWNpc2lvbigpCiAgICAgICAgaWYgYXhpcyBpcyBOb25lOgogICAgICAgICAgICBicmVhawogICAgICAgIGxhdGluLmNob29zZShheGlzLCB1LCB2LCB3c1tudW0gJSBsZW4od3MpXSkKICAgICAgICBudW0gLy89IGxlbih3cykKICAgIHJldHVybiBsYXRpbi5jaG9zZW5bMl0udG9saXN0KCkKCnRvdGFsID0gMAoKd2hpbGUgVHJ1ZToKICAgICAgICBzcXVhcmUgPSBbbGlzdChtYXAoaW50LCBsLnNwbGl0KCcsJykpKSBmb3IgbCBpbiBpdGVyKGxhbWJkYTogbmV4dChzeXMuc3RkaW4pLCAnXG4nKV0KICAgICAgICBwcmludCBzcXVhcmUKICAgICAgICAKICAgICAgICBpZiBub3Qgc3F1YXJlOgogICAgICAgICAgICBicmVhawoKICAgICAgICBiaXRzID0gY29tcHJlc3Moc3F1YXJlKQogICAgICAgIGFzc2VydCBzZXQoYml0cykgPD0geycwJywgJzEnfQogICAgICAgIGFzc2VydCBzcXVhcmUgPT0gZGVjb21wcmVzcyhiaXRzKQogICAgICAgIHByaW50KCdTcXVhcmUge306IHt9IGJpdHMnLmZvcm1hdChsZW4oc3F1YXJlKSwgbGVuKGJpdHMpKSkKICAgICAgICB0b3RhbCArPSBsZW4oYml0cykKCnByaW50KCdUb3RhbDoge30gYml0cyA9IHt9IGJ5dGVzJy5mb3JtYXQodG90YWwsIHRvdGFsLzguMCkp