# -*- coding: utf-8 -*-

from . import op


Jh_BlockSize = 64
Jh_StateSize = 32


JH_HX = 8
JH_HY = 4


IV512 = [
    (0x6fd14b96), (0x3e00aa17), (0x636a2e05), (0x7a15d543),
    (0x8a225e8d), (0x0c97ef0b), (0xe9341259), (0xf2b3c361),
    (0x891da0c1), (0x536f801e), (0x2aa9056b), (0xea2b6d80),
    (0x588eccdb), (0x2075baa6), (0xa90f3a76), (0xbaf83bf7),
    (0x0169e605), (0x41e34a69), (0x46b58a8e), (0x2e6fe65a),
    (0x1047a7d0), (0xc1843c24), (0x3b6e71b1), (0x2d5ac199),
    (0xcf57f6ec), (0x9db1f856), (0xa706887c), (0x5716b156),
    (0xe3c2fcdf), (0xe68517fb), (0x545a4678), (0xcc8cdd4b),
]


C = [
    0xa2ded572, 0x67f815df, 0x0a15847b, 0x571523b7, 0x90d6ab81, 0xf6875a4d,
    0xc54f9f4e, 0x402bd1c3, 0xe03a98ea, 0x9cfa455c, 0x99d2c503, 0x9a99b266,
    0xb4960266, 0x8a53bbf2, 0x1a1456b5, 0x31a2db88, 0x5c5aa303, 0xdb0e199a,
    0x0ab23f40, 0x1044c187, 0x8019051c, 0x1d959e84, 0xadeb336f, 0xdccde75e,
    0x9213ba10, 0x416bbf02, 0x156578dc, 0xd027bbf7, 0x39812c0a, 0x5078aa37,
    0xd2bf1a3f, 0xd3910041, 0x0d5a2d42, 0x907eccf6, 0x9c9f62dd, 0xce97c092,
    0x0ba75c18, 0xac442bc7, 0xd665dfd1, 0x23fcc663, 0x036c6e97, 0x1ab8e09e,
    0x7e450521, 0xa8ec6c44, 0xbb03f1ee, 0xfa618e5d, 0xb29796fd, 0x97818394,
    0x37858e4a, 0x2f3003db, 0x2d8d672a, 0x956a9ffb, 0x8173fe8a, 0x6c69b8f8,
    0x4672c78a, 0x14427fc0, 0x8f15f4c5, 0xc45ec7bd, 0xa76f4475, 0x80bb118f,
    0xb775de52, 0xbc88e4ae, 0x1e00b882, 0xf4a3a698, 0x338ff48e, 0x1563a3a9,
    0x24565faa, 0x89f9b7d5, 0x20edf1b6, 0xfde05a7c, 0x5ae9ca36, 0x362c4206,
    0x433529ce, 0x3d98fe4e, 0x74f93a53, 0xa74b9a73, 0x591ff5d0, 0x86814e6f,
    0x81ad9d0e, 0x9f5ad8af, 0x670605a7, 0x6a6234ee, 0xbe280b8b, 0x2717b96e,
    0x26077447, 0x3f1080c6, 0x6f7ea0e0, 0x7b487ec6, 0xa50a550d, 0xc0a4f84a,
    0x9fe7e391, 0x9ef18e97, 0x81727686, 0xd48d6050, 0x415a9e7e, 0x62b0e5f3,
    0xec1f9ffc, 0x7a205440, 0x001ae4e3, 0x84c9f4ce, 0xf594d74f, 0xd895fa9d,
    0x117e2e55, 0xa554c324, 0x2872df5b, 0x286efebd, 0xe27ff578, 0xb2c4a50f,
    0xef7c8905, 0x2ed349ee, 0x85937e44, 0x7f5928eb, 0x37695f70, 0x4a3124b3,
    0xf128865e, 0x65e4d61d, 0x04771bc7, 0xe720b951, 0xe843fe74, 0x8a87d423,
    0xa3e8297d, 0xf2947692, 0x097acbdd, 0xc1d9309b, 0xfb301b1d, 0xe01bdc5b,
    0x4f4924da, 0xbf829cf2, 0x31bae7a4, 0xffbf70b4, 0x0544320d, 0x48bcf8de,
    0x32fcae3b, 0x39d3bb53, 0xc1c39f45, 0xa08b29e0, 0xfd05c9e5, 0x0f09aef7,
    0x12347094, 0x34f19042, 0x01b771a2, 0x95ed44e3, 0x368e3be9, 0x4a982f4f,
    0x631d4088, 0x15f66ca0, 0x4b44c147, 0xffaf5287, 0xf14abb7e, 0x30c60ae2,
    0xc5b67046, 0xe68c6ecc, 0x56a4d5a4, 0x00ca4fbd, 0x4b849dda, 0xae183ec8,
    0x45ce5773, 0xadd16430, 0x68cea6e8, 0x67255c14, 0xf28cdaa3, 0x16e10ecb,
    0x5806e933, 0x9a99949a, 0x20b2601f, 0x7b846fc2, 0x7facced1, 0x1885d1a0,
    0xa15b5932, 0xd319dd8d, 0xc01c9a50, 0x46b4a5aa, 0x67633d9f, 0xba6b04e4,
    0xab19caf6, 0x7eee560b, 0xea79b11f, 0x742128a9, 0x35f7bde9, 0xee51363b,
    0x5aac571d, 0x76d35075, 0xfec2463a, 0x01707da3, 0xafc135f7, 0x42d8a498,
    0x20eced78, 0x79676b9e, 0x15638341, 0xa8db3aea, 0x4d3bc3fa, 0x832c8332,
    0x1f3b40a7, 0xf347271c, 0x34f04059, 0x9a762db7, 0x6c4e3ee7, 0xfd4f21d2,
    0x398dfdb8, 0xef5957dc, 0x490c9b8d, 0xdaeb492b, 0x49d7a25b, 0x0d70f368,
    0xd0ae3b7d, 0x84558d7a, 0xf0e9a5f5, 0x658ef8e4, 0xf4a2b8a0, 0x533b1036,
    0x9e07a80c, 0x5aec3e75, 0x92946891, 0x4f88e856, 0x555cb05b, 0x4cbcbaf8,
    0x993bbbe3, 0x7b9487f3, 0xd6f4da75, 0x5d1c6b72, 0x28acae64, 0x6db334dc,
    0x50a5346c, 0x71db28b8, 0xf2e261f8, 0x2a518d10, 0x3364dbe3, 0xfc75dd59,
    0xf1bcac1c, 0xa23fce43, 0x3cd1bb67, 0xb043e802, 0xca5b0a33, 0x75a12988,
    0x4d19347f, 0x5c5316b4, 0xc3943b92, 0x1e4d790e, 0xd7757479, 0x3fafeeb6,
    0xf7d4a8ea, 0x21391abe, 0x097ef45c, 0x5127234c, 0x5324a326, 0xd23c32ba,
    0x4a17a344, 0xadd5a66d, 0xa63e1db5, 0x08c9f2af, 0x983d5983, 0x563c6b91,
    0xa17cf84c, 0x4d608672, 0xcc3ee246, 0xf6c76e08, 0xb333982f, 0x5e76bcb1,
    0xa566d62b, 0x2ae6c4ef, 0xe8b6f406, 0x36d4c1be, 0x1582ee74, 0x6321efbc,
    0x0d4ec1fd, 0x69c953f4, 0xc45a7da7, 0x26585806, 0x1614c17e, 0x16fae006,
    0x3daf907e, 0x3f9d6328, 0xe3f2c9d2, 0x0cd29b00, 0x30ceaa5f, 0x300cd4b7,
    0x16512a74, 0x9832e0f2, 0xd830eb0d, 0x9af8cee3, 0x7b9ec54b, 0x9279f1b5,
    0x6ee651ff, 0xd3688604, 0x574d239b, 0x316796e6, 0xf3a6e6cc, 0x05750a17,
    0xd98176b1, 0xce6c3213, 0x8452173c, 0x62a205f8, 0xb3cb2bf4, 0x47154778,
    0x825446ff, 0x486a9323, 0x0758df38, 0x65655e4e, 0x897cfcf2, 0x8e5086fc,
    0x442e7031, 0x86ca0bd0, 0xa20940f0, 0x4e477830, 0x39eea065, 0x8338f7d1,
    0x37e95ef7, 0xbd3a2ce4, 0x26b29721, 0x6ff81301, 0xd1ed44a3, 0xe7de9fef,
    0x15dfa08b, 0xd9922576, 0xf6f7853c, 0xbe42dc12, 0x7ceca7d8, 0x7eb027ab,
    0xda7d8d53, 0xdea83eaa, 0x93ce25aa, 0xd86902bd, 0xfd43f65a, 0xf908731a,
    0xdaef5fc0, 0xa5194a17, 0x33664d97, 0x6a21fd4c, 0x3198b435, 0x701541db,
    0xbb0f1eea, 0x9b54cded, 0xa163d09a, 0x72409751, 0xbf9d75f6, 0xe26f4791,
]


def Sb(x, c):
    x[3] = ~x[3]
    x[0] ^= (c) & ~x[2]
    tmp = (c) ^ (x[0] & x[1])
    x[0] ^= x[2] & x[3]
    x[3] ^= ~x[1] & x[2]
    x[1] ^= x[0] & x[2]
    x[2] ^= x[0] & ~x[3]
    x[0] ^= x[1] | x[3]
    x[3] ^= x[1] & x[2]
    x[1] ^= tmp & x[0]
    x[2] ^= tmp
    return x


def Lb(x):
    x[4] ^= x[1]
    x[5] ^= x[2]
    x[6] ^= x[3] ^ x[0]
    x[7] ^= x[0]
    x[0] ^= x[5]
    x[1] ^= x[6]
    x[2] ^= x[7] ^ x[4]
    x[3] ^= x[4]
    return x


def Ceven(n, r):
    return C[((r) << 3) + 3 - n]


def Codd(n, r):
    return C[((r) << 3) + 7 - n]


def S(x0, x1, x2, x3, cb, r):
    x = Sb([x0[3], x1[3], x2[3], x3[3]], cb(3, r))
    x0[3] = x[0]
    x1[3] = x[1]
    x2[3] = x[2]
    x3[3] = x[3]
    x = Sb([x0[2], x1[2], x2[2], x3[2]], cb(2, r))
    x0[2] = x[0]
    x1[2] = x[1]
    x2[2] = x[2]
    x3[2] = x[3]
    x = Sb([x0[1], x1[1], x2[1], x3[1]], cb(1, r))
    x0[1] = x[0]
    x1[1] = x[1]
    x2[1] = x[2]
    x3[1] = x[3]
    x = Sb([x0[0], x1[0], x2[0], x3[0]], cb(0, r))
    x0[0] = x[0]
    x1[0] = x[1]
    x2[0] = x[2]
    x3[0] = x[3]


def L(x0, x1, x2, x3, x4, x5, x6, x7):
    x = Lb([x0[3], x1[3], x2[3], x3[3], x4[3], x5[3], x6[3], x7[3]])
    x0[3] = x[0]
    x1[3] = x[1]
    x2[3] = x[2]
    x3[3] = x[3]
    x4[3] = x[4]
    x5[3] = x[5]
    x6[3] = x[6]
    x7[3] = x[7]
    x = Lb([x0[2], x1[2], x2[2], x3[2], x4[2], x5[2], x6[2], x7[2]])
    x0[2] = x[0]
    x1[2] = x[1]
    x2[2] = x[2]
    x3[2] = x[3]
    x4[2] = x[4]
    x5[2] = x[5]
    x6[2] = x[6]
    x7[2] = x[7]
    x = Lb([x0[1], x1[1], x2[1], x3[1], x4[1], x5[1], x6[1], x7[1]])
    x0[1] = x[0]
    x1[1] = x[1]
    x2[1] = x[2]
    x3[1] = x[3]
    x4[1] = x[4]
    x5[1] = x[5]
    x6[1] = x[6]
    x7[1] = x[7]
    x = Lb([x0[0], x1[0], x2[0], x3[0], x4[0], x5[0], x6[0], x7[0]])
    x0[0] = x[0]
    x1[0] = x[1]
    x2[0] = x[2]
    x3[0] = x[3]
    x4[0] = x[4]
    x5[0] = x[5]
    x6[0] = x[6]
    x7[0] = x[7]


def Wz(x, c, n):
    t = (x[3] & (c)) << (n)
    x[3] = ((x[3] >> (n)) & (c)) | t
    t = (x[2] & (c)) << (n)
    x[2] = ((x[2] >> (n)) & (c)) | t
    t = (x[1] & (c)) << (n)
    x[1] = ((x[1] >> (n)) & (c)) | t
    t = (x[0] & (c)) << (n)
    x[0] = ((x[0] >> (n)) & (c)) | t


def W(ro, x):
    if ro == 0:
        return Wz(x, (0x55555555), 1)
    elif ro == 1:
        return Wz(x, (0x33333333), 2)
    elif ro == 2:
        return Wz(x, (0x0F0F0F0F), 4)
    elif ro == 3:
        return Wz(x, (0x00FF00FF), 8)
    elif ro == 4:
        return Wz(x, (0x0000FFFF), 16)
    elif ro == 5:
        t = x[3]
        x[3] = x[2]
        x[2] = t
        t = x[1]
        x[1] = x[0]
        x[0] = t
        return
    elif ro == 6:
        t = x[3]
        x[3] = x[1]
        x[1] = t
        t = x[2]
        x[2] = x[0]
        x[0] = t


def SL(h, r, ro):
    S(h[0], h[2], h[4], h[6], Ceven, r)
    S(h[1], h[3], h[5], h[7], Codd, r)
    L(h[0], h[2], h[4], h[6], h[1], h[3], h[5], h[7])
    W(ro, h[1])
    W(ro, h[3])
    W(ro, h[5])
    W(ro, h[7])


def READ_STATE(h, state):
    h[0][3] = state[0]
    h[0][2] = state[1]
    h[0][1] = state[2]
    h[0][0] = state[3]
    h[1][3] = state[4]
    h[1][2] = state[5]
    h[1][1] = state[6]
    h[1][0] = state[7]
    h[2][3] = state[8]
    h[2][2] = state[9]
    h[2][1] = state[10]
    h[2][0] = state[11]
    h[3][3] = state[12]
    h[3][2] = state[13]
    h[3][1] = state[14]
    h[3][0] = state[15]
    h[4][3] = state[16]
    h[4][2] = state[17]
    h[4][1] = state[18]
    h[4][0] = state[19]
    h[5][3] = state[20]
    h[5][2] = state[21]
    h[5][1] = state[22]
    h[5][0] = state[23]
    h[6][3] = state[24]
    h[6][2] = state[25]
    h[6][1] = state[26]
    h[6][0] = state[27]
    h[7][3] = state[28]
    h[7][2] = state[29]
    h[7][1] = state[30]
    h[7][0] = state[31]


def WRITE_STATE(h, state):
    state[0] = h[0][3]
    state[1] = h[0][2]
    state[2] = h[0][1]
    state[3] = h[0][0]
    state[4] = h[1][3]
    state[5] = h[1][2]
    state[6] = h[1][1]
    state[7] = h[1][0]
    state[8] = h[2][3]
    state[9] = h[2][2]
    state[10] = h[2][1]
    state[11] = h[2][0]
    state[12] = h[3][3]
    state[13] = h[3][2]
    state[14] = h[3][1]
    state[15] = h[3][0]
    state[16] = h[4][3]
    state[17] = h[4][2]
    state[18] = h[4][1]
    state[19] = h[4][0]
    state[20] = h[5][3]
    state[21] = h[5][2]
    state[22] = h[5][1]
    state[23] = h[5][0]
    state[24] = h[6][3]
    state[25] = h[6][2]
    state[26] = h[6][1]
    state[27] = h[6][0]
    state[28] = h[7][3]
    state[29] = h[7][2]
    state[30] = h[7][1]
    state[31] = h[7][0]


def E8(h):
    for r in range(0, 42, 7):
        SL(h, r + 0, 0)
        SL(h, r + 1, 1)
        SL(h, r + 2, 2)
        SL(h, r + 3, 3)
        SL(h, r + 4, 4)
        SL(h, r + 5, 5)
        SL(h, r + 6, 6)


def bufferXORInsertBackwards(buf, data, x, y, bufferOffsetX=0, bufferOffsetY=0):
    for i in range(x):
        for j in range(x):
            m = i + bufferOffsetX
            n = bufferOffsetY + y - 1 - j
            buf[m][n] = buf[m][n] ^ data[i * 4 + j]


def jh_update(ctx, msg, msg_len=None):
    buf = ctx['buffer']
    buf_len = len(buf)
    ptr = ctx['ptr']
    if msg_len is None:
        msg_len = len(msg)
    if msg_len < buf_len - ptr:
        op.buffer_insert(buf, ptr, msg, msg_len)
        ptr += msg_len
        ctx['ptr'] = ptr
        return

    V = [None] * JH_HX
    for i in range(JH_HX):
        V[i] = [None] * JH_HY

    READ_STATE(V, ctx['state'])
    while msg_len > 0:
        clen = buf_len - ptr
        if clen > msg_len:
            clen = msg_len
        op.buffer_insert(buf, ptr, msg, clen)
        ptr += clen
        msg = msg[clen:]
        msg_len -= clen
        if ptr == buf_len:
            buf32 = op.swap32_list(op.bytes_to_i32_list(buf))
            bufferXORInsertBackwards(V, buf32, 4, 4)
            E8(V)
            bufferXORInsertBackwards(V, buf32, 4, 4, 4, 0)
            blockCountLow = ctx['blockCountLow']
            blockCountLow = op.t32(blockCountLow + 1)
            ctx['blockCountLow'] = blockCountLow 
            if blockCountLow == 0:
                ctx['blockCountHigh'] += 1
            ptr = 0
    WRITE_STATE(V, ctx['state'])
    ctx['ptr'] = ptr


def jh_close(ctx):
    buf = bytearray(128)
    l = [None] * 4
    buf[0] = 0x80
    ptr = ctx['ptr']
    if ptr is 0:
        numz = 47
    else:
        numz = 111 - ptr
    buf[1:1+numz] = [0] * numz
    blockCountLow = ctx['blockCountLow']
    blockCountHigh = ctx['blockCountHigh']
    l[0] = op.t32(blockCountLow << 9) + (ptr << 3)
    l[1] = op.t32(blockCountLow >> 23) + op.t32(blockCountHigh << 9)
    l[2] = op.t32(blockCountHigh >> 23)
    l[3] = 0
    lBytes = op.bytes_from_i32_list(op.swap32_list(l))
    op.buffer_insert(buf, 1 + numz, lBytes[::-1], 16)
    jh_update(ctx, buf, numz + 17)
    out = [None] * 16
    state = ctx['state']
    for u in range(16):
        out[u] = op.swap32(state[u + 16])
    return out


def jh(msg, out_array=False, in_array=False):
    ctx = {}
    ctx['state'] = op.swap32_list(IV512)
    ctx['ptr'] = 0
    ctx['buffer'] = bytearray(Jh_BlockSize)
    ctx['blockCountHigh'] = 0
    ctx['blockCountLow'] = 0
    if in_array:
        msg = op.bytes_from_i32_list(msg)
    jh_update(ctx, msg)
    res = jh_close(ctx)
    if not out_array:
        res = op.bytes_from_i32_list(res)
    return res

91b2ab9f58deae39a8a14eb3255185516311c616
<mask token>


def Codd(n, r):
    return C[(r << 3) + 7 - n]


<mask token>


def L(x0, x1, x2, x3, x4, x5, x6, x7):
    x = Lb([x0[3], x1[3], x2[3], x3[3], x4[3], x5[3], x6[3], x7[3]])
    x0[3] = x[0]
    x1[3] = x[1]
    x2[3] = x[2]
    x3[3] = x[3]
    x4[3] = x[4]
    x5[3] = x[5]
    x6[3] = x[6]
    x7[3] = x[7]
    x = Lb([x0[2], x1[2], x2[2], x3[2], x4[2], x5[2], x6[2], x7[2]])
    x0[2] = x[0]
    x1[2] = x[1]
    x2[2] = x[2]
    x3[2] = x[3]
    x4[2] = x[4]
    x5[2] = x[5]
    x6[2] = x[6]
    x7[2] = x[7]
    x = Lb([x0[1], x1[1], x2[1], x3[1], x4[1], x5[1], x6[1], x7[1]])
    x0[1] = x[0]
    x1[1] = x[1]
    x2[1] = x[2]
    x3[1] = x[3]
    x4[1] = x[4]
    x5[1] = x[5]
    x6[1] = x[6]
    x7[1] = x[7]
    x = Lb([x0[0], x1[0], x2[0], x3[0], x4[0], x5[0], x6[0], x7[0]])
    x0[0] = x[0]
    x1[0] = x[1]
    x2[0] = x[2]
    x3[0] = x[3]
    x4[0] = x[4]
    x5[0] = x[5]
    x6[0] = x[6]
    x7[0] = x[7]


def Wz(x, c, n):
    t = (x[3] & c) << n
    x[3] = x[3] >> n & c | t
    t = (x[2] & c) << n
    x[2] = x[2] >> n & c | t
    t = (x[1] & c) << n
    x[1] = x[1] >> n & c | t
    t = (x[0] & c) << n
    x[0] = x[0] >> n & c | t


def W(ro, x):
    if ro == 0:
        return Wz(x, 1431655765, 1)
    elif ro == 1:
        return Wz(x, 858993459, 2)
    elif ro == 2:
        return Wz(x, 252645135, 4)
    elif ro == 3:
        return Wz(x, 16711935, 8)
    elif ro == 4:
        return Wz(x, 65535, 16)
    elif ro == 5:
        t = x[3]
        x[3] = x[2]
        x[2] = t
        t = x[1]
        x[1] = x[0]
        x[0] = t
        return
    elif ro == 6:
        t = x[3]
        x[3] = x[1]
        x[1] = t
        t = x[2]
        x[2] = x[0]
        x[0] = t


def SL(h, r, ro):
    S(h[0], h[2], h[4], h[6], Ceven, r)
    S(h[1], h[3], h[5], h[7], Codd, r)
    L(h[0], h[2], h[4], h[6], h[1], h[3], h[5], h[7])
    W(ro, h[1])
    W(ro, h[3])
    W(ro, h[5])
    W(ro, h[7])


def READ_STATE(h, state):
    h[0][3] = state[0]
    h[0][2] = state[1]
    h[0][1] = state[2]
    h[0][0] = state[3]
    h[1][3] = state[4]
    h[1][2] = state[5]
    h[1][1] = state[6]
    h[1][0] = state[7]
    h[2][3] = state[8]
    h[2][2] = state[9]
    h[2][1] = state[10]
    h[2][0] = state[11]
    h[3][3] = state[12]
    h[3][2] = state[13]
    h[3][1] = state[14]
    h[3][0] = state[15]
    h[4][3] = state[16]
    h[4][2] = state[17]
    h[4][1] = state[18]
    h[4][0] = state[19]
    h[5][3] = state[20]
    h[5][2] = state[21]
    h[5][1] = state[22]
    h[5][0] = state[23]
    h[6][3] = state[24]
    h[6][2] = state[25]
    h[6][1] = state[26]
    h[6][0] = state[27]
    h[7][3] = state[28]
    h[7][2] = state[29]
    h[7][1] = state[30]
    h[7][0] = state[31]


<mask token>


def bufferXORInsertBackwards(buf, data, x, y, bufferOffsetX=0, bufferOffsetY=0
    ):
    for i in range(x):
        for j in range(x):
            m = i + bufferOffsetX
            n = bufferOffsetY + y - 1 - j
            buf[m][n] = buf[m][n] ^ data[i * 4 + j]


def jh_update(ctx, msg, msg_len=None):
    buf = ctx['buffer']
    buf_len = len(buf)
    ptr = ctx['ptr']
    if msg_len is None:
        msg_len = len(msg)
    if msg_len < buf_len - ptr:
        op.buffer_insert(buf, ptr, msg, msg_len)
        ptr += msg_len
        ctx['ptr'] = ptr
        return
    V = [None] * JH_HX
    for i in range(JH_HX):
        V[i] = [None] * JH_HY
    READ_STATE(V, ctx['state'])
    while msg_len > 0:
        clen = buf_len - ptr
        if clen > msg_len:
            clen = msg_len
        op.buffer_insert(buf, ptr, msg, clen)
        ptr += clen
        msg = msg[clen:]
        msg_len -= clen
        if ptr == buf_len:
            buf32 = op.swap32_list(op.bytes_to_i32_list(buf))
            bufferXORInsertBackwards(V, buf32, 4, 4)
            E8(V)
            bufferXORInsertBackwards(V, buf32, 4, 4, 4, 0)
            blockCountLow = ctx['blockCountLow']
            blockCountLow = op.t32(blockCountLow + 1)
            ctx['blockCountLow'] = blockCountLow
            if blockCountLow == 0:
                ctx['blockCountHigh'] += 1
            ptr = 0
    WRITE_STATE(V, ctx['state'])
    ctx['ptr'] = ptr


<mask token>


def jh(msg, out_array=False, in_array=False):
    ctx = {}
    ctx['state'] = op.swap32_list(IV512)
    ctx['ptr'] = 0
    ctx['buffer'] = bytearray(Jh_BlockSize)
    ctx['blockCountHigh'] = 0
    ctx['blockCountLow'] = 0
    if in_array:
        msg = op.bytes_from_i32_list(msg)
    jh_update(ctx, msg)
    res = jh_close(ctx)
    if not out_array:
        res = op.bytes_from_i32_list(res)
    return res

<mask token>


def Sb(x, c):
    x[3] = ~x[3]
    x[0] ^= c & ~x[2]
    tmp = c ^ x[0] & x[1]
    x[0] ^= x[2] & x[3]
    x[3] ^= ~x[1] & x[2]
    x[1] ^= x[0] & x[2]
    x[2] ^= x[0] & ~x[3]
    x[0] ^= x[1] | x[3]
    x[3] ^= x[1] & x[2]
    x[1] ^= tmp & x[0]
    x[2] ^= tmp
    return x


def Lb(x):
    x[4] ^= x[1]
    x[5] ^= x[2]
    x[6] ^= x[3] ^ x[0]
    x[7] ^= x[0]
    x[0] ^= x[5]
    x[1] ^= x[6]
    x[2] ^= x[7] ^ x[4]
    x[3] ^= x[4]
    return x


<mask token>


def Codd(n, r):
    return C[(r << 3) + 7 - n]


def S(x0, x1, x2, x3, cb, r):
    x = Sb([x0[3], x1[3], x2[3], x3[3]], cb(3, r))
    x0[3] = x[0]
    x1[3] = x[1]
    x2[3] = x[2]
    x3[3] = x[3]
    x = Sb([x0[2], x1[2], x2[2], x3[2]], cb(2, r))
    x0[2] = x[0]
    x1[2] = x[1]
    x2[2] = x[2]
    x3[2] = x[3]
    x = Sb([x0[1], x1[1], x2[1], x3[1]], cb(1, r))
    x0[1] = x[0]
    x1[1] = x[1]
    x2[1] = x[2]
    x3[1] = x[3]
    x = Sb([x0[0], x1[0], x2[0], x3[0]], cb(0, r))
    x0[0] = x[0]
    x1[0] = x[1]
    x2[0] = x[2]
    x3[0] = x[3]


def L(x0, x1, x2, x3, x4, x5, x6, x7):
    x = Lb([x0[3], x1[3], x2[3], x3[3], x4[3], x5[3], x6[3], x7[3]])
    x0[3] = x[0]
    x1[3] = x[1]
    x2[3] = x[2]
    x3[3] = x[3]
    x4[3] = x[4]
    x5[3] = x[5]
    x6[3] = x[6]
    x7[3] = x[7]
    x = Lb([x0[2], x1[2], x2[2], x3[2], x4[2], x5[2], x6[2], x7[2]])
    x0[2] = x[0]
    x1[2] = x[1]
    x2[2] = x[2]
    x3[2] = x[3]
    x4[2] = x[4]
    x5[2] = x[5]
    x6[2] = x[6]
    x7[2] = x[7]
    x = Lb([x0[1], x1[1], x2[1], x3[1], x4[1], x5[1], x6[1], x7[1]])
    x0[1] = x[0]
    x1[1] = x[1]
    x2[1] = x[2]
    x3[1] = x[3]
    x4[1] = x[4]
    x5[1] = x[5]
    x6[1] = x[6]
    x7[1] = x[7]
    x = Lb([x0[0], x1[0], x2[0], x3[0], x4[0], x5[0], x6[0], x7[0]])
    x0[0] = x[0]
    x1[0] = x[1]
    x2[0] = x[2]
    x3[0] = x[3]
    x4[0] = x[4]
    x5[0] = x[5]
    x6[0] = x[6]
    x7[0] = x[7]


def Wz(x, c, n):
    t = (x[3] & c) << n
    x[3] = x[3] >> n & c | t
    t = (x[2] & c) << n
    x[2] = x[2] >> n & c | t
    t = (x[1] & c) << n
    x[1] = x[1] >> n & c | t
    t = (x[0] & c) << n
    x[0] = x[0] >> n & c | t


def W(ro, x):
    if ro == 0:
        return Wz(x, 1431655765, 1)
    elif ro == 1:
        return Wz(x, 858993459, 2)
    elif ro == 2:
        return Wz(x, 252645135, 4)
    elif ro == 3:
        return Wz(x, 16711935, 8)
    elif ro == 4:
        return Wz(x, 65535, 16)
    elif ro == 5:
        t = x[3]
        x[3] = x[2]
        x[2] = t
        t = x[1]
        x[1] = x[0]
        x[0] = t
        return
    elif ro == 6:
        t = x[3]
        x[3] = x[1]
        x[1] = t
        t = x[2]
        x[2] = x[0]
        x[0] = t


def SL(h, r, ro):
    S(h[0], h[2], h[4], h[6], Ceven, r)
    S(h[1], h[3], h[5], h[7], Codd, r)
    L(h[0], h[2], h[4], h[6], h[1], h[3], h[5], h[7])
    W(ro, h[1])
    W(ro, h[3])
    W(ro, h[5])
    W(ro, h[7])


def READ_STATE(h, state):
    h[0][3] = state[0]
    h[0][2] = state[1]
    h[0][1] = state[2]
    h[0][0] = state[3]
    h[1][3] = state[4]
    h[1][2] = state[5]
    h[1][1] = state[6]
    h[1][0] = state[7]
    h[2][3] = state[8]
    h[2][2] = state[9]
    h[2][1] = state[10]
    h[2][0] = state[11]
    h[3][3] = state[12]
    h[3][2] = state[13]
    h[3][1] = state[14]
    h[3][0] = state[15]
    h[4][3] = state[16]
    h[4][2] = state[17]
    h[4][1] = state[18]
    h[4][0] = state[19]
    h[5][3] = state[20]
    h[5][2] = state[21]
    h[5][1] = state[22]
    h[5][0] = state[23]
    h[6][3] = state[24]
    h[6][2] = state[25]
    h[6][1] = state[26]
    h[6][0] = state[27]
    h[7][3] = state[28]
    h[7][2] = state[29]
    h[7][1] = state[30]
    h[7][0] = state[31]


def WRITE_STATE(h, state):
    state[0] = h[0][3]
    state[1] = h[0][2]
    state[2] = h[0][1]
    state[3] = h[0][0]
    state[4] = h[1][3]
    state[5] = h[1][2]
    state[6] = h[1][1]
    state[7] = h[1][0]
    state[8] = h[2][3]
    state[9] = h[2][2]
    state[10] = h[2][1]
    state[11] = h[2][0]
    state[12] = h[3][3]
    state[13] = h[3][2]
    state[14] = h[3][1]
    state[15] = h[3][0]
    state[16] = h[4][3]
    state[17] = h[4][2]
    state[18] = h[4][1]
    state[19] = h[4][0]
    state[20] = h[5][3]
    state[21] = h[5][2]
    state[22] = h[5][1]
    state[23] = h[5][0]
    state[24] = h[6][3]
    state[25] = h[6][2]
    state[26] = h[6][1]
    state[27] = h[6][0]
    state[28] = h[7][3]
    state[29] = h[7][2]
    state[30] = h[7][1]
    state[31] = h[7][0]


def E8(h):
    for r in range(0, 42, 7):
        SL(h, r + 0, 0)
        SL(h, r + 1, 1)
        SL(h, r + 2, 2)
        SL(h, r + 3, 3)
        SL(h, r + 4, 4)
        SL(h, r + 5, 5)
        SL(h, r + 6, 6)


def bufferXORInsertBackwards(buf, data, x, y, bufferOffsetX=0, bufferOffsetY=0
    ):
    for i in range(x):
        for j in range(x):
            m = i + bufferOffsetX
            n = bufferOffsetY + y - 1 - j
            buf[m][n] = buf[m][n] ^ data[i * 4 + j]


def jh_update(ctx, msg, msg_len=None):
    buf = ctx['buffer']
    buf_len = len(buf)
    ptr = ctx['ptr']
    if msg_len is None:
        msg_len = len(msg)
    if msg_len < buf_len - ptr:
        op.buffer_insert(buf, ptr, msg, msg_len)
        ptr += msg_len
        ctx['ptr'] = ptr
        return
    V = [None] * JH_HX
    for i in range(JH_HX):
        V[i] = [None] * JH_HY
    READ_STATE(V, ctx['state'])
    while msg_len > 0:
        clen = buf_len - ptr
        if clen > msg_len:
            clen = msg_len
        op.buffer_insert(buf, ptr, msg, clen)
        ptr += clen
        msg = msg[clen:]
        msg_len -= clen
        if ptr == buf_len:
            buf32 = op.swap32_list(op.bytes_to_i32_list(buf))
            bufferXORInsertBackwards(V, buf32, 4, 4)
            E8(V)
            bufferXORInsertBackwards(V, buf32, 4, 4, 4, 0)
            blockCountLow = ctx['blockCountLow']
            blockCountLow = op.t32(blockCountLow + 1)
            ctx['blockCountLow'] = blockCountLow
            if blockCountLow == 0:
                ctx['blockCountHigh'] += 1
            ptr = 0
    WRITE_STATE(V, ctx['state'])
    ctx['ptr'] = ptr


def jh_close(ctx):
    buf = bytearray(128)
    l = [None] * 4
    buf[0] = 128
    ptr = ctx['ptr']
    if ptr is 0:
        numz = 47
    else:
        numz = 111 - ptr
    buf[1:1 + numz] = [0] * numz
    blockCountLow = ctx['blockCountLow']
    blockCountHigh = ctx['blockCountHigh']
    l[0] = op.t32(blockCountLow << 9) + (ptr << 3)
    l[1] = op.t32(blockCountLow >> 23) + op.t32(blockCountHigh << 9)
    l[2] = op.t32(blockCountHigh >> 23)
    l[3] = 0
    lBytes = op.bytes_from_i32_list(op.swap32_list(l))
    op.buffer_insert(buf, 1 + numz, lBytes[::-1], 16)
    jh_update(ctx, buf, numz + 17)
    out = [None] * 16
    state = ctx['state']
    for u in range(16):
        out[u] = op.swap32(state[u + 16])
    return out


def jh(msg, out_array=False, in_array=False):
    ctx = {}
    ctx['state'] = op.swap32_list(IV512)
    ctx['ptr'] = 0
    ctx['buffer'] = bytearray(Jh_BlockSize)
    ctx['blockCountHigh'] = 0
    ctx['blockCountLow'] = 0
    if in_array:
        msg = op.bytes_from_i32_list(msg)
    jh_update(ctx, msg)
    res = jh_close(ctx)
    if not out_array:
        res = op.bytes_from_i32_list(res)
    return res

<mask token>


def Sb(x, c):
    x[3] = ~x[3]
    x[0] ^= c & ~x[2]
    tmp = c ^ x[0] & x[1]
    x[0] ^= x[2] & x[3]
    x[3] ^= ~x[1] & x[2]
    x[1] ^= x[0] & x[2]
    x[2] ^= x[0] & ~x[3]
    x[0] ^= x[1] | x[3]
    x[3] ^= x[1] & x[2]
    x[1] ^= tmp & x[0]
    x[2] ^= tmp
    return x


def Lb(x):
    x[4] ^= x[1]
    x[5] ^= x[2]
    x[6] ^= x[3] ^ x[0]
    x[7] ^= x[0]
    x[0] ^= x[5]
    x[1] ^= x[6]
    x[2] ^= x[7] ^ x[4]
    x[3] ^= x[4]
    return x


def Ceven(n, r):
    return C[(r << 3) + 3 - n]


def Codd(n, r):
    return C[(r << 3) + 7 - n]


def S(x0, x1, x2, x3, cb, r):
    x = Sb([x0[3], x1[3], x2[3], x3[3]], cb(3, r))
    x0[3] = x[0]
    x1[3] = x[1]
    x2[3] = x[2]
    x3[3] = x[3]
    x = Sb([x0[2], x1[2], x2[2], x3[2]], cb(2, r))
    x0[2] = x[0]
    x1[2] = x[1]
    x2[2] = x[2]
    x3[2] = x[3]
    x = Sb([x0[1], x1[1], x2[1], x3[1]], cb(1, r))
    x0[1] = x[0]
    x1[1] = x[1]
    x2[1] = x[2]
    x3[1] = x[3]
    x = Sb([x0[0], x1[0], x2[0], x3[0]], cb(0, r))
    x0[0] = x[0]
    x1[0] = x[1]
    x2[0] = x[2]
    x3[0] = x[3]


def L(x0, x1, x2, x3, x4, x5, x6, x7):
    x = Lb([x0[3], x1[3], x2[3], x3[3], x4[3], x5[3], x6[3], x7[3]])
    x0[3] = x[0]
    x1[3] = x[1]
    x2[3] = x[2]
    x3[3] = x[3]
    x4[3] = x[4]
    x5[3] = x[5]
    x6[3] = x[6]
    x7[3] = x[7]
    x = Lb([x0[2], x1[2], x2[2], x3[2], x4[2], x5[2], x6[2], x7[2]])
    x0[2] = x[0]
    x1[2] = x[1]
    x2[2] = x[2]
    x3[2] = x[3]
    x4[2] = x[4]
    x5[2] = x[5]
    x6[2] = x[6]
    x7[2] = x[7]
    x = Lb([x0[1], x1[1], x2[1], x3[1], x4[1], x5[1], x6[1], x7[1]])
    x0[1] = x[0]
    x1[1] = x[1]
    x2[1] = x[2]
    x3[1] = x[3]
    x4[1] = x[4]
    x5[1] = x[5]
    x6[1] = x[6]
    x7[1] = x[7]
    x = Lb([x0[0], x1[0], x2[0], x3[0], x4[0], x5[0], x6[0], x7[0]])
    x0[0] = x[0]
    x1[0] = x[1]
    x2[0] = x[2]
    x3[0] = x[3]
    x4[0] = x[4]
    x5[0] = x[5]
    x6[0] = x[6]
    x7[0] = x[7]


def Wz(x, c, n):
    t = (x[3] & c) << n
    x[3] = x[3] >> n & c | t
    t = (x[2] & c) << n
    x[2] = x[2] >> n & c | t
    t = (x[1] & c) << n
    x[1] = x[1] >> n & c | t
    t = (x[0] & c) << n
    x[0] = x[0] >> n & c | t


def W(ro, x):
    if ro == 0:
        return Wz(x, 1431655765, 1)
    elif ro == 1:
        return Wz(x, 858993459, 2)
    elif ro == 2:
        return Wz(x, 252645135, 4)
    elif ro == 3:
        return Wz(x, 16711935, 8)
    elif ro == 4:
        return Wz(x, 65535, 16)
    elif ro == 5:
        t = x[3]
        x[3] = x[2]
        x[2] = t
        t = x[1]
        x[1] = x[0]
        x[0] = t
        return
    elif ro == 6:
        t = x[3]
        x[3] = x[1]
        x[1] = t
        t = x[2]
        x[2] = x[0]
        x[0] = t


def SL(h, r, ro):
    S(h[0], h[2], h[4], h[6], Ceven, r)
    S(h[1], h[3], h[5], h[7], Codd, r)
    L(h[0], h[2], h[4], h[6], h[1], h[3], h[5], h[7])
    W(ro, h[1])
    W(ro, h[3])
    W(ro, h[5])
    W(ro, h[7])


def READ_STATE(h, state):
    h[0][3] = state[0]
    h[0][2] = state[1]
    h[0][1] = state[2]
    h[0][0] = state[3]
    h[1][3] = state[4]
    h[1][2] = state[5]
    h[1][1] = state[6]
    h[1][0] = state[7]
    h[2][3] = state[8]
    h[2][2] = state[9]
    h[2][1] = state[10]
    h[2][0] = state[11]
    h[3][3] = state[12]
    h[3][2] = state[13]
    h[3][1] = state[14]
    h[3][0] = state[15]
    h[4][3] = state[16]
    h[4][2] = state[17]
    h[4][1] = state[18]
    h[4][0] = state[19]
    h[5][3] = state[20]
    h[5][2] = state[21]
    h[5][1] = state[22]
    h[5][0] = state[23]
    h[6][3] = state[24]
    h[6][2] = state[25]
    h[6][1] = state[26]
    h[6][0] = state[27]
    h[7][3] = state[28]
    h[7][2] = state[29]
    h[7][1] = state[30]
    h[7][0] = state[31]


def WRITE_STATE(h, state):
    state[0] = h[0][3]
    state[1] = h[0][2]
    state[2] = h[0][1]
    state[3] = h[0][0]
    state[4] = h[1][3]
    state[5] = h[1][2]
    state[6] = h[1][1]
    state[7] = h[1][0]
    state[8] = h[2][3]
    state[9] = h[2][2]
    state[10] = h[2][1]
    state[11] = h[2][0]
    state[12] = h[3][3]
    state[13] = h[3][2]
    state[14] = h[3][1]
    state[15] = h[3][0]
    state[16] = h[4][3]
    state[17] = h[4][2]
    state[18] = h[4][1]
    state[19] = h[4][0]
    state[20] = h[5][3]
    state[21] = h[5][2]
    state[22] = h[5][1]
    state[23] = h[5][0]
    state[24] = h[6][3]
    state[25] = h[6][2]
    state[26] = h[6][1]
    state[27] = h[6][0]
    state[28] = h[7][3]
    state[29] = h[7][2]
    state[30] = h[7][1]
    state[31] = h[7][0]


def E8(h):
    for r in range(0, 42, 7):
        SL(h, r + 0, 0)
        SL(h, r + 1, 1)
        SL(h, r + 2, 2)
        SL(h, r + 3, 3)
        SL(h, r + 4, 4)
        SL(h, r + 5, 5)
        SL(h, r + 6, 6)


def bufferXORInsertBackwards(buf, data, x, y, bufferOffsetX=0, bufferOffsetY=0
    ):
    for i in range(x):
        for j in range(x):
            m = i + bufferOffsetX
            n = bufferOffsetY + y - 1 - j
            buf[m][n] = buf[m][n] ^ data[i * 4 + j]


def jh_update(ctx, msg, msg_len=None):
    buf = ctx['buffer']
    buf_len = len(buf)
    ptr = ctx['ptr']
    if msg_len is None:
        msg_len = len(msg)
    if msg_len < buf_len - ptr:
        op.buffer_insert(buf, ptr, msg, msg_len)
        ptr += msg_len
        ctx['ptr'] = ptr
        return
    V = [None] * JH_HX
    for i in range(JH_HX):
        V[i] = [None] * JH_HY
    READ_STATE(V, ctx['state'])
    while msg_len > 0:
        clen = buf_len - ptr
        if clen > msg_len:
            clen = msg_len
        op.buffer_insert(buf, ptr, msg, clen)
        ptr += clen
        msg = msg[clen:]
        msg_len -= clen
        if ptr == buf_len:
            buf32 = op.swap32_list(op.bytes_to_i32_list(buf))
            bufferXORInsertBackwards(V, buf32, 4, 4)
            E8(V)
            bufferXORInsertBackwards(V, buf32, 4, 4, 4, 0)
            blockCountLow = ctx['blockCountLow']
            blockCountLow = op.t32(blockCountLow + 1)
            ctx['blockCountLow'] = blockCountLow
            if blockCountLow == 0:
                ctx['blockCountHigh'] += 1
            ptr = 0
    WRITE_STATE(V, ctx['state'])
    ctx['ptr'] = ptr


def jh_close(ctx):
    buf = bytearray(128)
    l = [None] * 4
    buf[0] = 128
    ptr = ctx['ptr']
    if ptr is 0:
        numz = 47
    else:
        numz = 111 - ptr
    buf[1:1 + numz] = [0] * numz
    blockCountLow = ctx['blockCountLow']
    blockCountHigh = ctx['blockCountHigh']
    l[0] = op.t32(blockCountLow << 9) + (ptr << 3)
    l[1] = op.t32(blockCountLow >> 23) + op.t32(blockCountHigh << 9)
    l[2] = op.t32(blockCountHigh >> 23)
    l[3] = 0
    lBytes = op.bytes_from_i32_list(op.swap32_list(l))
    op.buffer_insert(buf, 1 + numz, lBytes[::-1], 16)
    jh_update(ctx, buf, numz + 17)
    out = [None] * 16
    state = ctx['state']
    for u in range(16):
        out[u] = op.swap32(state[u + 16])
    return out


def jh(msg, out_array=False, in_array=False):
    ctx = {}
    ctx['state'] = op.swap32_list(IV512)
    ctx['ptr'] = 0
    ctx['buffer'] = bytearray(Jh_BlockSize)
    ctx['blockCountHigh'] = 0
    ctx['blockCountLow'] = 0
    if in_array:
        msg = op.bytes_from_i32_list(msg)
    jh_update(ctx, msg)
    res = jh_close(ctx)
    if not out_array:
        res = op.bytes_from_i32_list(res)
    return res

<mask token>
Jh_BlockSize = 64
Jh_StateSize = 32
JH_HX = 8
JH_HY = 4
IV512 = [1875987350, 1040230935, 1667902981, 2048251203, 2317508237, 
    211283723, 3912503897, 4071867233, 2300420289, 1399816222, 715720043, 
    3928714624, 1485753563, 544586406, 2836347510, 3136830455, 23717381, 
    1105414761, 1186302606, 779085402, 273131472, 3246668836, 997093809, 
    760922521, 3478648556, 2645686358, 2802223228, 1461104982, 3821206751, 
    3867482107, 1415202424, 3431783755]
C = [2732512626, 1744311775, 169182331, 1461003191, 2429987713, 4136065613,
    3310329678, 1076613571, 3761936618, 2633647452, 2580727043, 2593763942,
    3029729894, 2320743410, 437540533, 832756616, 1549443843, 3675134362, 
    179453760, 272941447, 2149123356, 496344708, 2917872495, 3704481630, 
    2450766352, 1097580290, 358971612, 3492264951, 964766730, 1350085175, 
    3535739455, 3549495361, 224013634, 2424229110, 2627691229, 3466051730, 
    195517464, 2890148807, 3597000657, 603768419, 57437847, 448323742, 
    2118452513, 2834066500, 3137597934, 4200697437, 2996279037, 2541847444,
    931499594, 791675867, 764241706, 2506792955, 2171862666, 1818867960, 
    1181927306, 339902400, 2400580805, 3294545853, 2809087093, 2159743375, 
    3077955154, 3163088046, 503363714, 4104365720, 865072270, 358851497, 
    609640362, 2314844117, 552464822, 4259338876, 1525271094, 908870150, 
    1127557582, 1033436750, 1962490451, 2806749811, 1495266768, 2256621167,
    2175638798, 2673531055, 1728447911, 1784820974, 3190295435, 655866222, 
    638022727, 1058046150, 1870569696, 2068348614, 2768917773, 3232036938, 
    2682774417, 2666630807, 2171762310, 3566035024, 1096457854, 1655760371,
    3961495548, 2048939072, 1762531, 2227827918, 4120172367, 3633707677, 
    293482069, 2773795620, 678616923, 678362813, 3800036728, 2999231759, 
    4017916165, 785598958, 2241035844, 2136549611, 929652592, 1244734643, 
    4045964894, 1709495837, 74914759, 3877681489, 3896770164, 2324157475, 
    2749901181, 4069815954, 159042525, 3252236443, 4214233885, 3759922267, 
    1330193626, 3213008114, 834332580, 4290736308, 88355341, 1220344030, 
    855420475, 970177363, 3250822981, 2693474784, 4245015013, 252292855, 
    305426580, 888246338, 28799394, 2515354851, 915291113, 1251487567, 
    1662861448, 368471200, 1262797127, 4289680007, 4048206718, 818285282, 
    3317067846, 3867963084, 1453643172, 13258685, 1266982362, 2920824520, 
    1171150707, 2916181040, 1758373608, 1730501652, 4069317283, 383848139, 
    1476847923, 2593756314, 548560927, 2072276930, 2142031569, 411423136, 
    2707118386, 3541687693, 3223099984, 1186244010, 1734557087, 3127575780,
    2870594294, 2129548811, 3933843743, 1948330153, 905428457, 3998299707, 
    1521243933, 1993560181, 4274144826, 24149411, 2948675063, 1121494168, 
    552398200, 2036820894, 358843201, 2832939754, 1295762426, 2200732466, 
    523976871, 4081526556, 888160345, 2591436215, 1817067239, 4249821650, 
    965606840, 4015609820, 1225563021, 3672852779, 1238868571, 225506152, 
    3501079421, 2220199290, 4041844213, 1703868644, 4104304800, 1396379702,
    2651301900, 1525431925, 2459199633, 1334372438, 1432137819, 1287437048,
    2570828771, 2073331699, 3606370933, 1562143602, 682405476, 1840461020, 
    1353004140, 1910188216, 4074922488, 709987600, 862247907, 4235582809, 
    4055673884, 2722090563, 1020377959, 2957240322, 3394964019, 1973496200,
    1293497471, 1548949172, 3281271698, 508393742, 3614798969, 1068494518, 
    4157909226, 557390526, 159315036, 1361519436, 1394909990, 3527160506, 
    1243063108, 2916460141, 2789088693, 147452591, 2554157443, 1446800273, 
    2709321804, 1298171506, 3426673222, 4140264968, 3006502959, 1584839857,
    2774980139, 719766767, 3904304134, 919912894, 360902260, 1663168444, 
    223265277, 1774801908, 3294264743, 643323910, 370459006, 385540102, 
    1034915966, 1067279144, 3824339410, 215128832, 818850399, 806147255, 
    374418036, 2553471218, 3627084557, 2599997155, 2074002763, 2457465269, 
    1860588031, 3546842628, 1464673179, 828872422, 4087801548, 91556375, 
    3649140401, 3463197203, 2219972412, 1654785528, 3016436724, 1192576888,
    2186561279, 1214944035, 123264824, 1701142094, 2306669810, 2387642108, 
    1143894065, 2261388240, 2718515440, 1313306672, 971939941, 2201548753, 
    938041079, 3174706404, 649238305, 1878528769, 3521987747, 3890126831, 
    366977163, 3650233718, 4143416636, 3192052754, 2095884248, 2125473707, 
    3665661267, 3735568042, 2479760810, 3630760637, 4249089626, 4178080538,
    3673120704, 2769898007, 862342551, 1780612428, 832091189, 1880441307, 
    3138330346, 2606026221, 2707673242, 1916835665, 3214767606, 3798943633]


def Sb(x, c):
    x[3] = ~x[3]
    x[0] ^= c & ~x[2]
    tmp = c ^ x[0] & x[1]
    x[0] ^= x[2] & x[3]
    x[3] ^= ~x[1] & x[2]
    x[1] ^= x[0] & x[2]
    x[2] ^= x[0] & ~x[3]
    x[0] ^= x[1] | x[3]
    x[3] ^= x[1] & x[2]
    x[1] ^= tmp & x[0]
    x[2] ^= tmp
    return x


def Lb(x):
    x[4] ^= x[1]
    x[5] ^= x[2]
    x[6] ^= x[3] ^ x[0]
    x[7] ^= x[0]
    x[0] ^= x[5]
    x[1] ^= x[6]
    x[2] ^= x[7] ^ x[4]
    x[3] ^= x[4]
    return x


def Ceven(n, r):
    return C[(r << 3) + 3 - n]


def Codd(n, r):
    return C[(r << 3) + 7 - n]


def S(x0, x1, x2, x3, cb, r):
    x = Sb([x0[3], x1[3], x2[3], x3[3]], cb(3, r))
    x0[3] = x[0]
    x1[3] = x[1]
    x2[3] = x[2]
    x3[3] = x[3]
    x = Sb([x0[2], x1[2], x2[2], x3[2]], cb(2, r))
    x0[2] = x[0]
    x1[2] = x[1]
    x2[2] = x[2]
    x3[2] = x[3]
    x = Sb([x0[1], x1[1], x2[1], x3[1]], cb(1, r))
    x0[1] = x[0]
    x1[1] = x[1]
    x2[1] = x[2]
    x3[1] = x[3]
    x = Sb([x0[0], x1[0], x2[0], x3[0]], cb(0, r))
    x0[0] = x[0]
    x1[0] = x[1]
    x2[0] = x[2]
    x3[0] = x[3]


def L(x0, x1, x2, x3, x4, x5, x6, x7):
    x = Lb([x0[3], x1[3], x2[3], x3[3], x4[3], x5[3], x6[3], x7[3]])
    x0[3] = x[0]
    x1[3] = x[1]
    x2[3] = x[2]
    x3[3] = x[3]
    x4[3] = x[4]
    x5[3] = x[5]
    x6[3] = x[6]
    x7[3] = x[7]
    x = Lb([x0[2], x1[2], x2[2], x3[2], x4[2], x5[2], x6[2], x7[2]])
    x0[2] = x[0]
    x1[2] = x[1]
    x2[2] = x[2]
    x3[2] = x[3]
    x4[2] = x[4]
    x5[2] = x[5]
    x6[2] = x[6]
    x7[2] = x[7]
    x = Lb([x0[1], x1[1], x2[1], x3[1], x4[1], x5[1], x6[1], x7[1]])
    x0[1] = x[0]
    x1[1] = x[1]
    x2[1] = x[2]
    x3[1] = x[3]
    x4[1] = x[4]
    x5[1] = x[5]
    x6[1] = x[6]
    x7[1] = x[7]
    x = Lb([x0[0], x1[0], x2[0], x3[0], x4[0], x5[0], x6[0], x7[0]])
    x0[0] = x[0]
    x1[0] = x[1]
    x2[0] = x[2]
    x3[0] = x[3]
    x4[0] = x[4]
    x5[0] = x[5]
    x6[0] = x[6]
    x7[0] = x[7]


def Wz(x, c, n):
    t = (x[3] & c) << n
    x[3] = x[3] >> n & c | t
    t = (x[2] & c) << n
    x[2] = x[2] >> n & c | t
    t = (x[1] & c) << n
    x[1] = x[1] >> n & c | t
    t = (x[0] & c) << n
    x[0] = x[0] >> n & c | t


def W(ro, x):
    if ro == 0:
        return Wz(x, 1431655765, 1)
    elif ro == 1:
        return Wz(x, 858993459, 2)
    elif ro == 2:
        return Wz(x, 252645135, 4)
    elif ro == 3:
        return Wz(x, 16711935, 8)
    elif ro == 4:
        return Wz(x, 65535, 16)
    elif ro == 5:
        t = x[3]
        x[3] = x[2]
        x[2] = t
        t = x[1]
        x[1] = x[0]
        x[0] = t
        return
    elif ro == 6:
        t = x[3]
        x[3] = x[1]
        x[1] = t
        t = x[2]
        x[2] = x[0]
        x[0] = t


def SL(h, r, ro):
    S(h[0], h[2], h[4], h[6], Ceven, r)
    S(h[1], h[3], h[5], h[7], Codd, r)
    L(h[0], h[2], h[4], h[6], h[1], h[3], h[5], h[7])
    W(ro, h[1])
    W(ro, h[3])
    W(ro, h[5])
    W(ro, h[7])


def READ_STATE(h, state):
    h[0][3] = state[0]
    h[0][2] = state[1]
    h[0][1] = state[2]
    h[0][0] = state[3]
    h[1][3] = state[4]
    h[1][2] = state[5]
    h[1][1] = state[6]
    h[1][0] = state[7]
    h[2][3] = state[8]
    h[2][2] = state[9]
    h[2][1] = state[10]
    h[2][0] = state[11]
    h[3][3] = state[12]
    h[3][2] = state[13]
    h[3][1] = state[14]
    h[3][0] = state[15]
    h[4][3] = state[16]
    h[4][2] = state[17]
    h[4][1] = state[18]
    h[4][0] = state[19]
    h[5][3] = state[20]
    h[5][2] = state[21]
    h[5][1] = state[22]
    h[5][0] = state[23]
    h[6][3] = state[24]
    h[6][2] = state[25]
    h[6][1] = state[26]
    h[6][0] = state[27]
    h[7][3] = state[28]
    h[7][2] = state[29]
    h[7][1] = state[30]
    h[7][0] = state[31]


def WRITE_STATE(h, state):
    state[0] = h[0][3]
    state[1] = h[0][2]
    state[2] = h[0][1]
    state[3] = h[0][0]
    state[4] = h[1][3]
    state[5] = h[1][2]
    state[6] = h[1][1]
    state[7] = h[1][0]
    state[8] = h[2][3]
    state[9] = h[2][2]
    state[10] = h[2][1]
    state[11] = h[2][0]
    state[12] = h[3][3]
    state[13] = h[3][2]
    state[14] = h[3][1]
    state[15] = h[3][0]
    state[16] = h[4][3]
    state[17] = h[4][2]
    state[18] = h[4][1]
    state[19] = h[4][0]
    state[20] = h[5][3]
    state[21] = h[5][2]
    state[22] = h[5][1]
    state[23] = h[5][0]
    state[24] = h[6][3]
    state[25] = h[6][2]
    state[26] = h[6][1]
    state[27] = h[6][0]
    state[28] = h[7][3]
    state[29] = h[7][2]
    state[30] = h[7][1]
    state[31] = h[7][0]


def E8(h):
    for r in range(0, 42, 7):
        SL(h, r + 0, 0)
        SL(h, r + 1, 1)
        SL(h, r + 2, 2)
        SL(h, r + 3, 3)
        SL(h, r + 4, 4)
        SL(h, r + 5, 5)
        SL(h, r + 6, 6)


def bufferXORInsertBackwards(buf, data, x, y, bufferOffsetX=0, bufferOffsetY=0
    ):
    for i in range(x):
        for j in range(x):
            m = i + bufferOffsetX
            n = bufferOffsetY + y - 1 - j
            buf[m][n] = buf[m][n] ^ data[i * 4 + j]


def jh_update(ctx, msg, msg_len=None):
    buf = ctx['buffer']
    buf_len = len(buf)
    ptr = ctx['ptr']
    if msg_len is None:
        msg_len = len(msg)
    if msg_len < buf_len - ptr:
        op.buffer_insert(buf, ptr, msg, msg_len)
        ptr += msg_len
        ctx['ptr'] = ptr
        return
    V = [None] * JH_HX
    for i in range(JH_HX):
        V[i] = [None] * JH_HY
    READ_STATE(V, ctx['state'])
    while msg_len > 0:
        clen = buf_len - ptr
        if clen > msg_len:
            clen = msg_len
        op.buffer_insert(buf, ptr, msg, clen)
        ptr += clen
        msg = msg[clen:]
        msg_len -= clen
        if ptr == buf_len:
            buf32 = op.swap32_list(op.bytes_to_i32_list(buf))
            bufferXORInsertBackwards(V, buf32, 4, 4)
            E8(V)
            bufferXORInsertBackwards(V, buf32, 4, 4, 4, 0)
            blockCountLow = ctx['blockCountLow']
            blockCountLow = op.t32(blockCountLow + 1)
            ctx['blockCountLow'] = blockCountLow
            if blockCountLow == 0:
                ctx['blockCountHigh'] += 1
            ptr = 0
    WRITE_STATE(V, ctx['state'])
    ctx['ptr'] = ptr


def jh_close(ctx):
    buf = bytearray(128)
    l = [None] * 4
    buf[0] = 128
    ptr = ctx['ptr']
    if ptr is 0:
        numz = 47
    else:
        numz = 111 - ptr
    buf[1:1 + numz] = [0] * numz
    blockCountLow = ctx['blockCountLow']
    blockCountHigh = ctx['blockCountHigh']
    l[0] = op.t32(blockCountLow << 9) + (ptr << 3)
    l[1] = op.t32(blockCountLow >> 23) + op.t32(blockCountHigh << 9)
    l[2] = op.t32(blockCountHigh >> 23)
    l[3] = 0
    lBytes = op.bytes_from_i32_list(op.swap32_list(l))
    op.buffer_insert(buf, 1 + numz, lBytes[::-1], 16)
    jh_update(ctx, buf, numz + 17)
    out = [None] * 16
    state = ctx['state']
    for u in range(16):
        out[u] = op.swap32(state[u + 16])
    return out


def jh(msg, out_array=False, in_array=False):
    ctx = {}
    ctx['state'] = op.swap32_list(IV512)
    ctx['ptr'] = 0
    ctx['buffer'] = bytearray(Jh_BlockSize)
    ctx['blockCountHigh'] = 0
    ctx['blockCountLow'] = 0
    if in_array:
        msg = op.bytes_from_i32_list(msg)
    jh_update(ctx, msg)
    res = jh_close(ctx)
    if not out_array:
        res = op.bytes_from_i32_list(res)
    return res

# -*- coding: utf-8 -*-

from . import op


Jh_BlockSize = 64
Jh_StateSize = 32


JH_HX = 8
JH_HY = 4


IV512 = [
    (0x6fd14b96), (0x3e00aa17), (0x636a2e05), (0x7a15d543),
    (0x8a225e8d), (0x0c97ef0b), (0xe9341259), (0xf2b3c361),
    (0x891da0c1), (0x536f801e), (0x2aa9056b), (0xea2b6d80),
    (0x588eccdb), (0x2075baa6), (0xa90f3a76), (0xbaf83bf7),
    (0x0169e605), (0x41e34a69), (0x46b58a8e), (0x2e6fe65a),
    (0x1047a7d0), (0xc1843c24), (0x3b6e71b1), (0x2d5ac199),
    (0xcf57f6ec), (0x9db1f856), (0xa706887c), (0x5716b156),
    (0xe3c2fcdf), (0xe68517fb), (0x545a4678), (0xcc8cdd4b),
]


C = [
    0xa2ded572, 0x67f815df, 0x0a15847b, 0x571523b7, 0x90d6ab81, 0xf6875a4d,
    0xc54f9f4e, 0x402bd1c3, 0xe03a98ea, 0x9cfa455c, 0x99d2c503, 0x9a99b266,
    0xb4960266, 0x8a53bbf2, 0x1a1456b5, 0x31a2db88, 0x5c5aa303, 0xdb0e199a,
    0x0ab23f40, 0x1044c187, 0x8019051c, 0x1d959e84, 0xadeb336f, 0xdccde75e,
    0x9213ba10, 0x416bbf02, 0x156578dc, 0xd027bbf7, 0x39812c0a, 0x5078aa37,
    0xd2bf1a3f, 0xd3910041, 0x0d5a2d42, 0x907eccf6, 0x9c9f62dd, 0xce97c092,
    0x0ba75c18, 0xac442bc7, 0xd665dfd1, 0x23fcc663, 0x036c6e97, 0x1ab8e09e,
    0x7e450521, 0xa8ec6c44, 0xbb03f1ee, 0xfa618e5d, 0xb29796fd, 0x97818394,
    0x37858e4a, 0x2f3003db, 0x2d8d672a, 0x956a9ffb, 0x8173fe8a, 0x6c69b8f8,
    0x4672c78a, 0x14427fc0, 0x8f15f4c5, 0xc45ec7bd, 0xa76f4475, 0x80bb118f,
    0xb775de52, 0xbc88e4ae, 0x1e00b882, 0xf4a3a698, 0x338ff48e, 0x1563a3a9,
    0x24565faa, 0x89f9b7d5, 0x20edf1b6, 0xfde05a7c, 0x5ae9ca36, 0x362c4206,
    0x433529ce, 0x3d98fe4e, 0x74f93a53, 0xa74b9a73, 0x591ff5d0, 0x86814e6f,
    0x81ad9d0e, 0x9f5ad8af, 0x670605a7, 0x6a6234ee, 0xbe280b8b, 0x2717b96e,
    0x26077447, 0x3f1080c6, 0x6f7ea0e0, 0x7b487ec6, 0xa50a550d, 0xc0a4f84a,
    0x9fe7e391, 0x9ef18e97, 0x81727686, 0xd48d6050, 0x415a9e7e, 0x62b0e5f3,
    0xec1f9ffc, 0x7a205440, 0x001ae4e3, 0x84c9f4ce, 0xf594d74f, 0xd895fa9d,
    0x117e2e55, 0xa554c324, 0x2872df5b, 0x286efebd, 0xe27ff578, 0xb2c4a50f,
    0xef7c8905, 0x2ed349ee, 0x85937e44, 0x7f5928eb, 0x37695f70, 0x4a3124b3,
    0xf128865e, 0x65e4d61d, 0x04771bc7, 0xe720b951, 0xe843fe74, 0x8a87d423,
    0xa3e8297d, 0xf2947692, 0x097acbdd, 0xc1d9309b, 0xfb301b1d, 0xe01bdc5b,
    0x4f4924da, 0xbf829cf2, 0x31bae7a4, 0xffbf70b4, 0x0544320d, 0x48bcf8de,
    0x32fcae3b, 0x39d3bb53, 0xc1c39f45, 0xa08b29e0, 0xfd05c9e5, 0x0f09aef7,
    0x12347094, 0x34f19042, 0x01b771a2, 0x95ed44e3, 0x368e3be9, 0x4a982f4f,
    0x631d4088, 0x15f66ca0, 0x4b44c147, 0xffaf5287, 0xf14abb7e, 0x30c60ae2,
    0xc5b67046, 0xe68c6ecc, 0x56a4d5a4, 0x00ca4fbd, 0x4b849dda, 0xae183ec8,
    0x45ce5773, 0xadd16430, 0x68cea6e8, 0x67255c14, 0xf28cdaa3, 0x16e10ecb,
    0x5806e933, 0x9a99949a, 0x20b2601f, 0x7b846fc2, 0x7facced1, 0x1885d1a0,
    0xa15b5932, 0xd319dd8d, 0xc01c9a50, 0x46b4a5aa, 0x67633d9f, 0xba6b04e4,
    0xab19caf6, 0x7eee560b, 0xea79b11f, 0x742128a9, 0x35f7bde9, 0xee51363b,
    0x5aac571d, 0x76d35075, 0xfec2463a, 0x01707da3, 0xafc135f7, 0x42d8a498,
    0x20eced78, 0x79676b9e, 0x15638341, 0xa8db3aea, 0x4d3bc3fa, 0x832c8332,
    0x1f3b40a7, 0xf347271c, 0x34f04059, 0x9a762db7, 0x6c4e3ee7, 0xfd4f21d2,
    0x398dfdb8, 0xef5957dc, 0x490c9b8d, 0xdaeb492b, 0x49d7a25b, 0x0d70f368,
    0xd0ae3b7d, 0x84558d7a, 0xf0e9a5f5, 0x658ef8e4, 0xf4a2b8a0, 0x533b1036,
    0x9e07a80c, 0x5aec3e75, 0x92946891, 0x4f88e856, 0x555cb05b, 0x4cbcbaf8,
    0x993bbbe3, 0x7b9487f3, 0xd6f4da75, 0x5d1c6b72, 0x28acae64, 0x6db334dc,
    0x50a5346c, 0x71db28b8, 0xf2e261f8, 0x2a518d10, 0x3364dbe3, 0xfc75dd59,
    0xf1bcac1c, 0xa23fce43, 0x3cd1bb67, 0xb043e802, 0xca5b0a33, 0x75a12988,
    0x4d19347f, 0x5c5316b4, 0xc3943b92, 0x1e4d790e, 0xd7757479, 0x3fafeeb6,
    0xf7d4a8ea, 0x21391abe, 0x097ef45c, 0x5127234c, 0x5324a326, 0xd23c32ba,
    0x4a17a344, 0xadd5a66d, 0xa63e1db5, 0x08c9f2af, 0x983d5983, 0x563c6b91,
    0xa17cf84c, 0x4d608672, 0xcc3ee246, 0xf6c76e08, 0xb333982f, 0x5e76bcb1,
    0xa566d62b, 0x2ae6c4ef, 0xe8b6f406, 0x36d4c1be, 0x1582ee74, 0x6321efbc,
    0x0d4ec1fd, 0x69c953f4, 0xc45a7da7, 0x26585806, 0x1614c17e, 0x16fae006,
    0x3daf907e, 0x3f9d6328, 0xe3f2c9d2, 0x0cd29b00, 0x30ceaa5f, 0x300cd4b7,
    0x16512a74, 0x9832e0f2, 0xd830eb0d, 0x9af8cee3, 0x7b9ec54b, 0x9279f1b5,
    0x6ee651ff, 0xd3688604, 0x574d239b, 0x316796e6, 0xf3a6e6cc, 0x05750a17,
    0xd98176b1, 0xce6c3213, 0x8452173c, 0x62a205f8, 0xb3cb2bf4, 0x47154778,
    0x825446ff, 0x486a9323, 0x0758df38, 0x65655e4e, 0x897cfcf2, 0x8e5086fc,
    0x442e7031, 0x86ca0bd0, 0xa20940f0, 0x4e477830, 0x39eea065, 0x8338f7d1,
    0x37e95ef7, 0xbd3a2ce4, 0x26b29721, 0x6ff81301, 0xd1ed44a3, 0xe7de9fef,
    0x15dfa08b, 0xd9922576, 0xf6f7853c, 0xbe42dc12, 0x7ceca7d8, 0x7eb027ab,
    0xda7d8d53, 0xdea83eaa, 0x93ce25aa, 0xd86902bd, 0xfd43f65a, 0xf908731a,
    0xdaef5fc0, 0xa5194a17, 0x33664d97, 0x6a21fd4c, 0x3198b435, 0x701541db,
    0xbb0f1eea, 0x9b54cded, 0xa163d09a, 0x72409751, 0xbf9d75f6, 0xe26f4791,
]


def Sb(x, c):
    x[3] = ~x[3]
    x[0] ^= (c) & ~x[2]
    tmp = (c) ^ (x[0] & x[1])
    x[0] ^= x[2] & x[3]
    x[3] ^= ~x[1] & x[2]
    x[1] ^= x[0] & x[2]
    x[2] ^= x[0] & ~x[3]
    x[0] ^= x[1] | x[3]
    x[3] ^= x[1] & x[2]
    x[1] ^= tmp & x[0]
    x[2] ^= tmp
    return x


def Lb(x):
    x[4] ^= x[1]
    x[5] ^= x[2]
    x[6] ^= x[3] ^ x[0]
    x[7] ^= x[0]
    x[0] ^= x[5]
    x[1] ^= x[6]
    x[2] ^= x[7] ^ x[4]
    x[3] ^= x[4]
    return x


def Ceven(n, r):
    return C[((r) << 3) + 3 - n]


def Codd(n, r):
    return C[((r) << 3) + 7 - n]


def S(x0, x1, x2, x3, cb, r):
    x = Sb([x0[3], x1[3], x2[3], x3[3]], cb(3, r))
    x0[3] = x[0]
    x1[3] = x[1]
    x2[3] = x[2]
    x3[3] = x[3]
    x = Sb([x0[2], x1[2], x2[2], x3[2]], cb(2, r))
    x0[2] = x[0]
    x1[2] = x[1]
    x2[2] = x[2]
    x3[2] = x[3]
    x = Sb([x0[1], x1[1], x2[1], x3[1]], cb(1, r))
    x0[1] = x[0]
    x1[1] = x[1]
    x2[1] = x[2]
    x3[1] = x[3]
    x = Sb([x0[0], x1[0], x2[0], x3[0]], cb(0, r))
    x0[0] = x[0]
    x1[0] = x[1]
    x2[0] = x[2]
    x3[0] = x[3]


def L(x0, x1, x2, x3, x4, x5, x6, x7):
    x = Lb([x0[3], x1[3], x2[3], x3[3], x4[3], x5[3], x6[3], x7[3]])
    x0[3] = x[0]
    x1[3] = x[1]
    x2[3] = x[2]
    x3[3] = x[3]
    x4[3] = x[4]
    x5[3] = x[5]
    x6[3] = x[6]
    x7[3] = x[7]
    x = Lb([x0[2], x1[2], x2[2], x3[2], x4[2], x5[2], x6[2], x7[2]])
    x0[2] = x[0]
    x1[2] = x[1]
    x2[2] = x[2]
    x3[2] = x[3]
    x4[2] = x[4]
    x5[2] = x[5]
    x6[2] = x[6]
    x7[2] = x[7]
    x = Lb([x0[1], x1[1], x2[1], x3[1], x4[1], x5[1], x6[1], x7[1]])
    x0[1] = x[0]
    x1[1] = x[1]
    x2[1] = x[2]
    x3[1] = x[3]
    x4[1] = x[4]
    x5[1] = x[5]
    x6[1] = x[6]
    x7[1] = x[7]
    x = Lb([x0[0], x1[0], x2[0], x3[0], x4[0], x5[0], x6[0], x7[0]])
    x0[0] = x[0]
    x1[0] = x[1]
    x2[0] = x[2]
    x3[0] = x[3]
    x4[0] = x[4]
    x5[0] = x[5]
    x6[0] = x[6]
    x7[0] = x[7]


def Wz(x, c, n):
    t = (x[3] & (c)) << (n)
    x[3] = ((x[3] >> (n)) & (c)) | t
    t = (x[2] & (c)) << (n)
    x[2] = ((x[2] >> (n)) & (c)) | t
    t = (x[1] & (c)) << (n)
    x[1] = ((x[1] >> (n)) & (c)) | t
    t = (x[0] & (c)) << (n)
    x[0] = ((x[0] >> (n)) & (c)) | t


def W(ro, x):
    if ro == 0:
        return Wz(x, (0x55555555), 1)
    elif ro == 1:
        return Wz(x, (0x33333333), 2)
    elif ro == 2:
        return Wz(x, (0x0F0F0F0F), 4)
    elif ro == 3:
        return Wz(x, (0x00FF00FF), 8)
    elif ro == 4:
        return Wz(x, (0x0000FFFF), 16)
    elif ro == 5:
        t = x[3]
        x[3] = x[2]
        x[2] = t
        t = x[1]
        x[1] = x[0]
        x[0] = t
        return
    elif ro == 6:
        t = x[3]
        x[3] = x[1]
        x[1] = t
        t = x[2]
        x[2] = x[0]
        x[0] = t


def SL(h, r, ro):
    S(h[0], h[2], h[4], h[6], Ceven, r)
    S(h[1], h[3], h[5], h[7], Codd, r)
    L(h[0], h[2], h[4], h[6], h[1], h[3], h[5], h[7])
    W(ro, h[1])
    W(ro, h[3])
    W(ro, h[5])
    W(ro, h[7])


def READ_STATE(h, state):
    h[0][3] = state[0]
    h[0][2] = state[1]
    h[0][1] = state[2]
    h[0][0] = state[3]
    h[1][3] = state[4]
    h[1][2] = state[5]
    h[1][1] = state[6]
    h[1][0] = state[7]
    h[2][3] = state[8]
    h[2][2] = state[9]
    h[2][1] = state[10]
    h[2][0] = state[11]
    h[3][3] = state[12]
    h[3][2] = state[13]
    h[3][1] = state[14]
    h[3][0] = state[15]
    h[4][3] = state[16]
    h[4][2] = state[17]
    h[4][1] = state[18]
    h[4][0] = state[19]
    h[5][3] = state[20]
    h[5][2] = state[21]
    h[5][1] = state[22]
    h[5][0] = state[23]
    h[6][3] = state[24]
    h[6][2] = state[25]
    h[6][1] = state[26]
    h[6][0] = state[27]
    h[7][3] = state[28]
    h[7][2] = state[29]
    h[7][1] = state[30]
    h[7][0] = state[31]


def WRITE_STATE(h, state):
    state[0] = h[0][3]
    state[1] = h[0][2]
    state[2] = h[0][1]
    state[3] = h[0][0]
    state[4] = h[1][3]
    state[5] = h[1][2]
    state[6] = h[1][1]
    state[7] = h[1][0]
    state[8] = h[2][3]
    state[9] = h[2][2]
    state[10] = h[2][1]
    state[11] = h[2][0]
    state[12] = h[3][3]
    state[13] = h[3][2]
    state[14] = h[3][1]
    state[15] = h[3][0]
    state[16] = h[4][3]
    state[17] = h[4][2]
    state[18] = h[4][1]
    state[19] = h[4][0]
    state[20] = h[5][3]
    state[21] = h[5][2]
    state[22] = h[5][1]
    state[23] = h[5][0]
    state[24] = h[6][3]
    state[25] = h[6][2]
    state[26] = h[6][1]
    state[27] = h[6][0]
    state[28] = h[7][3]
    state[29] = h[7][2]
    state[30] = h[7][1]
    state[31] = h[7][0]


def E8(h):
    for r in range(0, 42, 7):
        SL(h, r + 0, 0)
        SL(h, r + 1, 1)
        SL(h, r + 2, 2)
        SL(h, r + 3, 3)
        SL(h, r + 4, 4)
        SL(h, r + 5, 5)
        SL(h, r + 6, 6)


def bufferXORInsertBackwards(buf, data, x, y, bufferOffsetX=0, bufferOffsetY=0):
    for i in range(x):
        for j in range(x):
            m = i + bufferOffsetX
            n = bufferOffsetY + y - 1 - j
            buf[m][n] = buf[m][n] ^ data[i * 4 + j]


def jh_update(ctx, msg, msg_len=None):
    buf = ctx['buffer']
    buf_len = len(buf)
    ptr = ctx['ptr']
    if msg_len is None:
        msg_len = len(msg)
    if msg_len < buf_len - ptr:
        op.buffer_insert(buf, ptr, msg, msg_len)
        ptr += msg_len
        ctx['ptr'] = ptr
        return

    V = [None] * JH_HX
    for i in range(JH_HX):
        V[i] = [None] * JH_HY

    READ_STATE(V, ctx['state'])
    while msg_len > 0:
        clen = buf_len - ptr
        if clen > msg_len:
            clen = msg_len
        op.buffer_insert(buf, ptr, msg, clen)
        ptr += clen
        msg = msg[clen:]
        msg_len -= clen
        if ptr == buf_len:
            buf32 = op.swap32_list(op.bytes_to_i32_list(buf))
            bufferXORInsertBackwards(V, buf32, 4, 4)
            E8(V)
            bufferXORInsertBackwards(V, buf32, 4, 4, 4, 0)
            blockCountLow = ctx['blockCountLow']
            blockCountLow = op.t32(blockCountLow + 1)
            ctx['blockCountLow'] = blockCountLow 
            if blockCountLow == 0:
                ctx['blockCountHigh'] += 1
            ptr = 0
    WRITE_STATE(V, ctx['state'])
    ctx['ptr'] = ptr


def jh_close(ctx):
    buf = bytearray(128)
    l = [None] * 4
    buf[0] = 0x80
    ptr = ctx['ptr']
    if ptr is 0:
        numz = 47
    else:
        numz = 111 - ptr
    buf[1:1+numz] = [0] * numz
    blockCountLow = ctx['blockCountLow']
    blockCountHigh = ctx['blockCountHigh']
    l[0] = op.t32(blockCountLow << 9) + (ptr << 3)
    l[1] = op.t32(blockCountLow >> 23) + op.t32(blockCountHigh << 9)
    l[2] = op.t32(blockCountHigh >> 23)
    l[3] = 0
    lBytes = op.bytes_from_i32_list(op.swap32_list(l))
    op.buffer_insert(buf, 1 + numz, lBytes[::-1], 16)
    jh_update(ctx, buf, numz + 17)
    out = [None] * 16
    state = ctx['state']
    for u in range(16):
        out[u] = op.swap32(state[u + 16])
    return out


def jh(msg, out_array=False, in_array=False):
    ctx = {}
    ctx['state'] = op.swap32_list(IV512)
    ctx['ptr'] = 0
    ctx['buffer'] = bytearray(Jh_BlockSize)
    ctx['blockCountHigh'] = 0
    ctx['blockCountLow'] = 0
    if in_array:
        msg = op.bytes_from_i32_list(msg)
    jh_update(ctx, msg)
    res = jh_close(ctx)
    if not out_array:
        res = op.bytes_from_i32_list(res)
    return res
