// 
// Decompiled by Procyon v0.6.0
// 

package org.bouncycastle.pqc.crypto.cmce;

import org.bouncycastle.util.Arrays;
import java.security.SecureRandom;
import org.bouncycastle.crypto.digests.SHAKEDigest;

class CMCEEngine
{
    private int SYS_N;
    private int SYS_T;
    private int GFBITS;
    private int IRR_BYTES;
    private int COND_BYTES;
    private int PK_NROWS;
    private int PK_NCOLS;
    private int PK_ROW_BYTES;
    private int SYND_BYTES;
    private int GFMASK;
    private int[] poly;
    private final int defaultKeySize;
    private GF gf;
    private BENES benes;
    private boolean usePadding;
    private boolean countErrorIndices;
    private boolean usePivots;
    
    public int getIrrBytes() {
        return this.IRR_BYTES;
    }
    
    public int getCondBytes() {
        return this.COND_BYTES;
    }
    
    public int getPrivateKeySize() {
        return this.COND_BYTES + this.IRR_BYTES + this.SYS_N / 8 + 40;
    }
    
    public int getPublicKeySize() {
        if (this.usePadding) {
            return this.PK_NROWS * (this.SYS_N / 8 - (this.PK_NROWS - 1) / 8);
        }
        return this.PK_NROWS * this.PK_NCOLS / 8;
    }
    
    public int getCipherTextSize() {
        return this.SYND_BYTES;
    }
    
    public CMCEEngine(final int gfbits, final int sys_N, final int sys_T, final int[] poly, final boolean usePivots, final int defaultKeySize) {
        this.usePivots = usePivots;
        this.SYS_N = sys_N;
        this.SYS_T = sys_T;
        this.GFBITS = gfbits;
        this.poly = poly;
        this.defaultKeySize = defaultKeySize;
        this.IRR_BYTES = this.SYS_T * 2;
        this.COND_BYTES = (1 << this.GFBITS - 4) * (2 * this.GFBITS - 1);
        this.PK_NROWS = this.SYS_T * this.GFBITS;
        this.PK_NCOLS = this.SYS_N - this.PK_NROWS;
        this.PK_ROW_BYTES = (this.PK_NCOLS + 7) / 8;
        this.SYND_BYTES = (this.PK_NROWS + 7) / 8;
        this.GFMASK = (1 << this.GFBITS) - 1;
        if (this.GFBITS == 12) {
            this.gf = new GF12();
            this.benes = new BENES12(this.SYS_N, this.SYS_T, this.GFBITS);
        }
        else {
            this.gf = new GF13();
            this.benes = new BENES13(this.SYS_N, this.SYS_T, this.GFBITS);
        }
        this.usePadding = (this.SYS_T % 8 != 0);
        this.countErrorIndices = (1 << this.GFBITS > this.SYS_N);
    }
    
    public byte[] generate_public_key_from_private_key(final byte[] array) {
        final byte[] array2 = new byte[this.getPublicKeySize()];
        final short[] array3 = new short[1 << this.GFBITS];
        final long[] array4 = { 0L };
        final int[] array5 = new int[1 << this.GFBITS];
        final byte[] array6 = new byte[this.SYS_N / 8 + (1 << this.GFBITS) * 4];
        final int n = array6.length - 32 - this.IRR_BYTES - (1 << this.GFBITS) * 4;
        final SHAKEDigest shakeDigest = new SHAKEDigest(256);
        shakeDigest.update((byte)64);
        shakeDigest.update(array, 0, 32);
        shakeDigest.doFinal(array6, 0, array6.length);
        for (int i = 0; i < 1 << this.GFBITS; ++i) {
            array5[i] = Utils.load4(array6, n + i * 4);
        }
        this.pk_gen(array2, array, array5, array3, array4);
        return array2;
    }
    
    public byte[] decompress_private_key(final byte[] array) {
        final byte[] array2 = new byte[this.getPrivateKeySize()];
        System.arraycopy(array, 0, array2, 0, array.length);
        final byte[] array3 = new byte[this.SYS_N / 8 + (1 << this.GFBITS) * 4 + this.IRR_BYTES + 32];
        final SHAKEDigest shakeDigest = new SHAKEDigest(256);
        shakeDigest.update((byte)64);
        shakeDigest.update(array, 0, 32);
        shakeDigest.doFinal(array3, 0, array3.length);
        if (array.length <= 40) {
            final short[] array4 = new short[this.SYS_T];
            final byte[] array5 = new byte[this.IRR_BYTES];
            final int n = array3.length - 32 - this.IRR_BYTES;
            for (int i = 0; i < this.SYS_T; ++i) {
                array4[i] = Utils.load_gf(array3, n + i * 2, this.GFMASK);
            }
            this.generate_irr_poly(array4);
            for (int j = 0; j < this.SYS_T; ++j) {
                Utils.store_gf(array5, j * 2, array4[j]);
            }
            System.arraycopy(array5, 0, array2, 40, this.IRR_BYTES);
        }
        if (array.length <= 40 + this.IRR_BYTES) {
            final int[] array6 = new int[1 << this.GFBITS];
            final short[] array7 = new short[1 << this.GFBITS];
            final int n2 = array3.length - 32 - this.IRR_BYTES - (1 << this.GFBITS) * 4;
            for (int k = 0; k < 1 << this.GFBITS; ++k) {
                array6[k] = Utils.load4(array3, n2 + k * 4);
            }
            if (this.usePivots) {
                this.pk_gen(null, array2, array6, array7, new long[] { 0L });
            }
            else {
                final long[] array8 = new long[1 << this.GFBITS];
                for (int l = 0; l < 1 << this.GFBITS; ++l) {
                    array8[l] = array6[l];
                    final long[] array9 = array8;
                    final int n3 = l;
                    array9[n3] <<= 31;
                    final long[] array10 = array8;
                    final int n4 = l;
                    array10[n4] |= l;
                    final long[] array11 = array8;
                    final int n5 = l;
                    array11[n5] &= Long.MAX_VALUE;
                }
                sort64(array8, 0, array8.length);
                for (int n6 = 0; n6 < 1 << this.GFBITS; ++n6) {
                    array7[n6] = (short)(array8[n6] & (long)this.GFMASK);
                }
            }
            final byte[] array12 = new byte[this.COND_BYTES];
            controlbitsfrompermutation(array12, array7, this.GFBITS, 1 << this.GFBITS);
            System.arraycopy(array12, 0, array2, this.IRR_BYTES + 40, array12.length);
        }
        System.arraycopy(array3, 0, array2, this.getPrivateKeySize() - this.SYS_N / 8, this.SYS_N / 8);
        return array2;
    }
    
    public void kem_keypair(final byte[] array, final byte[] array2, final SecureRandom secureRandom) {
        final byte[] array3 = { 0 };
        byte[] copyOfRange = new byte[32];
        array3[0] = 64;
        secureRandom.nextBytes(copyOfRange);
        final byte[] array4 = new byte[this.SYS_N / 8 + (1 << this.GFBITS) * 4 + this.SYS_T * 2 + 32];
        byte[] copyOfRange2 = copyOfRange;
        final long[] array5 = { 0L };
        final SHAKEDigest shakeDigest = new SHAKEDigest(256);
        int n4;
        short[] array8;
        while (true) {
            shakeDigest.update(array3, 0, array3.length);
            shakeDigest.update(copyOfRange, 0, copyOfRange.length);
            shakeDigest.doFinal(array4, 0, array4.length);
            final int n = array4.length - 32;
            copyOfRange = Arrays.copyOfRange(array4, n, n + 32);
            System.arraycopy(copyOfRange2, 0, array2, 0, 32);
            copyOfRange2 = Arrays.copyOfRange(copyOfRange, 0, 32);
            final short[] array6 = new short[this.SYS_T];
            final int n2 = array4.length - 32 - 2 * this.SYS_T;
            for (int i = 0; i < this.SYS_T; ++i) {
                array6[i] = Utils.load_gf(array4, n2 + i * 2, this.GFMASK);
            }
            if (this.generate_irr_poly(array6) == -1) {
                continue;
            }
            final int n3 = 40;
            for (int j = 0; j < this.SYS_T; ++j) {
                Utils.store_gf(array2, n3 + j * 2, array6[j]);
            }
            final int[] array7 = new int[1 << this.GFBITS];
            n4 = n2 - (1 << this.GFBITS) * 4;
            for (int k = 0; k < 1 << this.GFBITS; ++k) {
                array7[k] = Utils.load4(array4, n4 + k * 4);
            }
            array8 = new short[1 << this.GFBITS];
            if (this.pk_gen(array, array2, array7, array8, array5) == -1) {
                continue;
            }
            break;
        }
        final byte[] array9 = new byte[this.COND_BYTES];
        controlbitsfrompermutation(array9, array8, this.GFBITS, 1 << this.GFBITS);
        System.arraycopy(array9, 0, array2, this.IRR_BYTES + 40, array9.length);
        System.arraycopy(array4, n4 - this.SYS_N / 8, array2, array2.length - this.SYS_N / 8, this.SYS_N / 8);
        if (!this.usePivots) {
            Utils.store8(array2, 32, 4294967295L);
        }
        else {
            Utils.store8(array2, 32, array5[0]);
        }
    }
    
    private void syndrome(final byte[] array, final byte[] array2, final byte[] array3) {
        final short[] array4 = new short[this.SYS_N / 8];
        int n = 0;
        final int n2 = this.PK_NROWS % 8;
        for (int i = 0; i < this.SYND_BYTES; ++i) {
            array[i] = 0;
        }
        for (int j = 0; j < this.PK_NROWS; ++j) {
            for (int k = 0; k < this.SYS_N / 8; ++k) {
                array4[k] = 0;
            }
            for (int l = 0; l < this.PK_ROW_BYTES; ++l) {
                array4[this.SYS_N / 8 - this.PK_ROW_BYTES + l] = array2[n + l];
            }
            if (this.usePadding) {
                for (int n3 = this.SYS_N / 8 - 1; n3 >= this.SYS_N / 8 - this.PK_ROW_BYTES; --n3) {
                    array4[n3] = (short)(((array4[n3] & 0xFF) << n2 | (array4[n3 - 1] & 0xFF) >>> 8 - n2) & 0xFF);
                }
            }
            final short[] array5 = array4;
            final int n4 = j / 8;
            array5[n4] |= (short)(1 << j % 8);
            int n5 = 0;
            for (int n6 = 0; n6 < this.SYS_N / 8; ++n6) {
                n5 = (byte)(n5 ^ (array4[n6] & array3[n6]));
            }
            final byte b = (byte)(n5 ^ n5 >>> 4);
            final byte b2 = (byte)(b ^ b >>> 2);
            final byte b3 = (byte)((byte)(b2 ^ b2 >>> 1) & 0x1);
            final int n7 = j / 8;
            array[n7] |= (byte)(b3 << j % 8);
            n += this.PK_ROW_BYTES;
        }
    }
    
    private void generate_error_vector(final byte[] array, final SecureRandom secureRandom) {
        final short[] array2 = new short[this.SYS_T * 2];
        final short[] array3 = new short[this.SYS_T];
        final byte[] array4 = new byte[this.SYS_T];
        while (true) {
            if (this.countErrorIndices) {
                final byte[] bytes = new byte[this.SYS_T * 4];
                secureRandom.nextBytes(bytes);
                for (int i = 0; i < this.SYS_T * 2; ++i) {
                    array2[i] = Utils.load_gf(bytes, i * 2, this.GFMASK);
                }
                int n = 0;
                for (int n2 = 0; n2 < this.SYS_T * 2 && n < this.SYS_T; ++n2) {
                    if (array2[n2] < this.SYS_N) {
                        array3[n++] = array2[n2];
                    }
                }
                if (n < this.SYS_T) {
                    continue;
                }
            }
            else {
                final byte[] bytes2 = new byte[this.SYS_T * 2];
                secureRandom.nextBytes(bytes2);
                for (int j = 0; j < this.SYS_T; ++j) {
                    array3[j] = Utils.load_gf(bytes2, j * 2, this.GFMASK);
                }
            }
            int n3 = 0;
            for (int n4 = 1; n4 < this.SYS_T && n3 != 1; ++n4) {
                for (int k = 0; k < n4; ++k) {
                    if (array3[n4] == array3[k]) {
                        n3 = 1;
                        break;
                    }
                }
            }
            if (n3 == 0) {
                break;
            }
        }
        for (int l = 0; l < this.SYS_T; ++l) {
            array4[l] = (byte)(1 << (array3[l] & 0x7));
        }
        for (short n5 = 0; n5 < this.SYS_N / 8; ++n5) {
            array[n5] = 0;
            for (int n6 = 0; n6 < this.SYS_T; ++n6) {
                final short n7 = (short)(same_mask32(n5, (short)(array3[n6] >> 3)) & 0xFF);
                final short n8 = n5;
                array[n8] |= (byte)(array4[n6] & n7);
            }
        }
    }
    
    private void encrypt(final byte[] array, final byte[] array2, final byte[] array3, final SecureRandom secureRandom) {
        this.generate_error_vector(array3, secureRandom);
        this.syndrome(array, array2, array3);
    }
    
    public int kem_enc(final byte[] array, final byte[] array2, final byte[] array3, final SecureRandom secureRandom) {
        final byte[] array4 = new byte[this.SYS_N / 8];
        int check_pk_padding = 0;
        if (this.usePadding) {
            check_pk_padding = this.check_pk_padding(array3);
        }
        this.encrypt(array, array3, array4, secureRandom);
        final SHAKEDigest shakeDigest = new SHAKEDigest(256);
        shakeDigest.update((byte)1);
        shakeDigest.update(array4, 0, array4.length);
        shakeDigest.update(array, 0, array.length);
        shakeDigest.doFinal(array2, 0, array2.length);
        if (this.usePadding) {
            final byte b = (byte)((byte)check_pk_padding ^ 0xFF);
            for (int i = 0; i < this.SYND_BYTES; ++i) {
                final int n = i;
                array[n] &= b;
            }
            for (int j = 0; j < 32; ++j) {
                final int n2 = j;
                array2[n2] &= b;
            }
            return check_pk_padding;
        }
        return 0;
    }
    
    public int kem_dec(final byte[] array, final byte[] array2, final byte[] array3) {
        final byte[] array4 = new byte[this.SYS_N / 8];
        final byte[] array5 = new byte[1 + this.SYS_N / 8 + this.SYND_BYTES];
        int check_c_padding = 0;
        if (this.usePadding) {
            check_c_padding = this.check_c_padding(array2);
        }
        final short n = (short)((short)((short)((byte)this.decrypt(array4, array3, array2) - 1) >> 8) & 0xFF);
        array5[0] = (byte)(n & 0x1);
        for (int i = 0; i < this.SYS_N / 8; ++i) {
            array5[1 + i] = (byte)((~n & array3[i + 40 + this.IRR_BYTES + this.COND_BYTES]) | (n & array4[i]));
        }
        for (int j = 0; j < this.SYND_BYTES; ++j) {
            array5[1 + this.SYS_N / 8 + j] = array2[j];
        }
        final SHAKEDigest shakeDigest = new SHAKEDigest(256);
        shakeDigest.update(array5, 0, array5.length);
        shakeDigest.doFinal(array, 0, array.length);
        if (this.usePadding) {
            final byte b = (byte)check_c_padding;
            for (int k = 0; k < array.length; ++k) {
                final int n2 = k;
                array[n2] |= b;
            }
            return check_c_padding;
        }
        return 0;
    }
    
    private int decrypt(final byte[] array, final byte[] array2, final byte[] array3) {
        final short[] array4 = new short[this.SYS_T + 1];
        final short[] array5 = new short[this.SYS_N];
        final short[] array6 = new short[this.SYS_T * 2];
        final short[] array7 = new short[this.SYS_T * 2];
        final short[] array8 = new short[this.SYS_T + 1];
        final short[] array9 = new short[this.SYS_N];
        final byte[] array10 = new byte[this.SYS_N / 8];
        for (int i = 0; i < this.SYND_BYTES; ++i) {
            array10[i] = array3[i];
        }
        for (int j = this.SYND_BYTES; j < this.SYS_N / 8; ++j) {
            array10[j] = 0;
        }
        for (int k = 0; k < this.SYS_T; ++k) {
            array4[k] = Utils.load_gf(array2, 40 + k * 2, this.GFMASK);
        }
        array4[this.SYS_T] = 1;
        this.benes.support_gen(array5, array2);
        this.synd(array6, array4, array5, array10);
        this.bm(array8, array6);
        this.root(array9, array8, array5);
        for (int l = 0; l < this.SYS_N / 8; ++l) {
            array[l] = 0;
        }
        int n = 0;
        for (int n2 = 0; n2 < this.SYS_N; ++n2) {
            final short n3 = (short)(this.gf.gf_iszero(array9[n2]) & 0x1);
            final int n4 = n2 / 8;
            array[n4] |= (byte)(n3 << n2 % 8);
            n += n3;
        }
        this.synd(array7, array4, array5, array);
        int n5 = n ^ this.SYS_T;
        for (int n6 = 0; n6 < this.SYS_T * 2; ++n6) {
            n5 |= (array6[n6] ^ array7[n6]);
        }
        final int n7 = --n5 >> 15 & 0x1;
        if ((n7 ^ 0x1) != 0x0) {}
        return n7 ^ 0x1;
    }
    
    private static int min(final short n, final int n2) {
        if (n < n2) {
            return n;
        }
        return n2;
    }
    
    private void bm(final short[] array, final short[] array2) {
        int n = 0;
        final short[] array3 = new short[this.SYS_T + 1];
        final short[] array4 = new short[this.SYS_T + 1];
        final short[] array5 = new short[this.SYS_T + 1];
        short n2 = 1;
        for (int i = 0; i < this.SYS_T + 1; ++i) {
            array4[i] = (array5[i] = 0);
        }
        array5[1] = (array4[0] = 1);
        for (short n3 = 0; n3 < 2 * this.SYS_T; ++n3) {
            int n4 = 0;
            for (short n5 = 0; n5 <= min(n3, this.SYS_T); ++n5) {
                n4 ^= this.gf.gf_mul_ext(array4[n5], array2[n3 - n5]);
            }
            final short gf_reduce = this.gf.gf_reduce(n4);
            final short n6 = (short)((short)((short)((short)(gf_reduce - 1) >> 15) & 0x1) - 1);
            final short n7 = (short)((short)((short)((short)((short)(n3 - 2 * n) >> 15) & 0x1) - 1) & n6);
            for (int j = 0; j <= this.SYS_T; ++j) {
                array3[j] = array4[j];
            }
            final short gf_frac = this.gf.gf_frac(n2, gf_reduce);
            for (int k = 0; k <= this.SYS_T; ++k) {
                final short[] array6 = array4;
                final int n8 = k;
                array6[n8] ^= (short)(this.gf.gf_mul(gf_frac, array5[k]) & n6);
            }
            n = (short)((n & ~n7) | (n3 + 1 - n & n7));
            for (int l = this.SYS_T - 1; l >= 0; --l) {
                array5[l + 1] = (short)((array5[l] & ~n7) | (array3[l] & n7));
            }
            array5[0] = 0;
            n2 = (short)((n2 & ~n7) | (gf_reduce & n7));
        }
        for (int n9 = 0; n9 <= this.SYS_T; ++n9) {
            array[n9] = array4[this.SYS_T - n9];
        }
    }
    
    private void synd(final short[] array, final short[] array2, final short[] array3, final byte[] array4) {
        final short n = (short)(array4[0] & 0x1);
        final short n2 = array3[0];
        short gf_mul = (short)(this.gf.gf_inv(this.gf.gf_sq(this.eval(array2, n2))) & -n);
        array[0] = gf_mul;
        for (int i = 1; i < 2 * this.SYS_T; ++i) {
            gf_mul = this.gf.gf_mul(gf_mul, n2);
            array[i] = gf_mul;
        }
        for (int j = 1; j < this.SYS_N; ++j) {
            final short n3 = (short)(array4[j / 8] >> j % 8 & 0x1);
            final short n4 = array3[j];
            short n5 = this.gf.gf_mul(this.gf.gf_inv(this.gf.gf_sq(this.eval(array2, n4))), n3);
            final int n6 = 0;
            array[n6] ^= n5;
            for (int k = 1; k < 2 * this.SYS_T; ++k) {
                n5 = this.gf.gf_mul(n5, n4);
                final int n7 = k;
                array[n7] ^= n5;
            }
        }
    }
    
    private int mov_columns(final byte[][] array, final short[] array2, final long[] array3) {
        final long[] array4 = new long[64];
        final long[] array5 = new long[32];
        final long n = 1L;
        final byte[] array6 = new byte[9];
        final int n2 = this.PK_NROWS - 32;
        final int n3 = n2 / 8;
        final int n4 = n2 % 8;
        if (this.usePadding) {
            for (int i = 0; i < 32; ++i) {
                for (int j = 0; j < 9; ++j) {
                    array6[j] = array[n2 + i][n3 + j];
                }
                for (int k = 0; k < 8; ++k) {
                    array6[k] = (byte)((array6[k] & 0xFF) >> n4 | array6[k + 1] << 8 - n4);
                }
                array4[i] = Utils.load8(array6, 0);
            }
        }
        else {
            for (int l = 0; l < 32; ++l) {
                array4[l] = Utils.load8(array[n2 + l], n3);
            }
        }
        array3[0] = 0L;
        for (int n5 = 0; n5 < 32; ++n5) {
            long n6 = array4[n5];
            for (int n7 = n5 + 1; n7 < 32; ++n7) {
                n6 |= array4[n7];
            }
            if (n6 == 0L) {
                return -1;
            }
            final int ctz;
            array5[n5] = (ctz = ctz(n6));
            final int n8 = 0;
            array3[n8] |= n << (int)array5[n5];
            for (int n9 = n5 + 1; n9 < 32; ++n9) {
                final long n10 = (array4[n5] >> ctz & 0x1L) - 1L;
                final long[] array7 = array4;
                final int n11 = n5;
                array7[n11] ^= (array4[n9] & n10);
            }
            for (int n12 = n5 + 1; n12 < 32; ++n12) {
                final long n13 = -(array4[n12] >> ctz & 0x1L);
                final long[] array8 = array4;
                final int n14 = n12;
                array8[n14] ^= (array4[n5] & n13);
            }
        }
        for (int n15 = 0; n15 < 32; ++n15) {
            for (int n16 = n15 + 1; n16 < 64; ++n16) {
                final long n17 = (long)(array2[n2 + n15] ^ array2[n2 + n16]) & same_mask64((short)n16, (short)array5[n15]);
                final int n18 = n2 + n15;
                array2[n18] = (short)((long)array2[n18] ^ n17);
                final int n19 = n2 + n16;
                array2[n19] = (short)((long)array2[n19] ^ n17);
            }
        }
        for (int n20 = 0; n20 < this.PK_NROWS; ++n20) {
            long n23;
            if (this.usePadding) {
                for (int n21 = 0; n21 < 9; ++n21) {
                    array6[n21] = array[n20][n3 + n21];
                }
                for (int n22 = 0; n22 < 8; ++n22) {
                    array6[n22] = (byte)((array6[n22] & 0xFF) >> n4 | array6[n22 + 1] << 8 - n4);
                }
                n23 = Utils.load8(array6, 0);
            }
            else {
                n23 = Utils.load8(array[n20], n3);
            }
            for (int n24 = 0; n24 < 32; ++n24) {
                final long n25 = (n23 >> n24 ^ n23 >> (int)array5[n24]) & 0x1L;
                n23 = (n23 ^ n25 << (int)array5[n24] ^ n25 << n24);
            }
            if (this.usePadding) {
                Utils.store8(array6, 0, n23);
                array[n20][n3 + 8] = (byte)((array[n20][n3 + 8] & 0xFF) >>> n4 << n4 | (array6[7] & 0xFF) >>> 8 - n4);
                array[n20][n3 + 0] = (byte)((array6[0] & 0xFF) << n4 | (array[n20][n3] & 0xFF) << 8 - n4 >>> 8 - n4);
                for (int n26 = 7; n26 >= 1; --n26) {
                    array[n20][n3 + n26] = (byte)((array6[n26] & 0xFF) << n4 | (array6[n26 - 1] & 0xFF) >>> 8 - n4);
                }
            }
            else {
                Utils.store8(array[n20], n3, n23);
            }
        }
        return 0;
    }
    
    private static int ctz(final long n) {
        long n2 = 72340172838076673L;
        long n3 = 0L;
        final long n4 = ~n;
        for (int i = 0; i < 8; ++i) {
            n2 &= n4 >>> i;
            n3 += n2;
        }
        final long n5 = n3 & 0x808080808080808L;
        final long n6 = n5 | n5 >>> 1;
        long n7 = n6 | n6 >>> 2;
        final long n8 = n3;
        long n9 = n3 >>> 8;
        long n10 = n8 + (n9 & n7);
        for (int j = 2; j < 8; ++j) {
            n7 &= n7 >>> 8;
            n9 >>>= 8;
            n10 += (n9 & n7);
        }
        return (int)n10 & 0xFF;
    }
    
    private static long same_mask64(final short n, final short n2) {
        return -((n ^ n2) - 1L >>> 63);
    }
    
    private static byte same_mask32(final short n, final short n2) {
        int n3 = n ^ n2;
        return (byte)(-(--n3 >>> 31) & 0xFF);
    }
    
    private static void layer(final short[] array, final byte[] array2, final int n, final int n2, final int n3) {
        final int n4 = 1 << n2;
        int n5 = 0;
        for (int i = 0; i < n3; i += n4 * 2) {
            for (int j = 0; j < n4; ++j) {
                final int n6 = (array[i + j] ^ array[i + j + n4]) & -(array2[n + (n5 >> 3)] >> (n5 & 0x7) & 0x1);
                final int n7 = i + j;
                array[n7] ^= (short)n6;
                final int n8 = i + j + n4;
                array[n8] ^= (short)n6;
                ++n5;
            }
        }
    }
    
    private static void controlbitsfrompermutation(final byte[] array, final short[] array2, final long n, final long n2) {
        final int[] array3 = new int[(int)(2L * n2)];
        final short[] array4 = new short[(int)n2];
        int i;
        do {
            for (int n3 = 0; n3 < ((2L * n - 1L) * n2 / 2L + 7L) / 8L; ++n3) {
                array[n3] = 0;
            }
            cbrecursion(array, 0L, 1L, array2, 0, n, n2, array3);
            for (int n4 = 0; n4 < n2; ++n4) {
                array4[n4] = (short)n4;
            }
            int n5 = 0;
            for (int n6 = 0; n6 < n; ++n6) {
                layer(array4, array, n5, n6, (int)n2);
                n5 += (int)(n2 >> 4);
            }
            for (int j = (int)(n - 2L); j >= 0; --j) {
                layer(array4, array, n5, j, (int)n2);
                n5 += (int)(n2 >> 4);
            }
            i = 0;
            for (int n7 = 0; n7 < n2; ++n7) {
                i = (short)(i | (array2[n7] ^ array4[n7]));
            }
        } while (i != 0);
    }
    
    static short get_q_short(final int[] array, final int n) {
        final int n2 = n / 2;
        if (n % 2 == 0) {
            return (short)array[n2];
        }
        return (short)((array[n2] & 0xFFFF0000) >> 16);
    }
    
    static void cbrecursion(final byte[] array, long n, final long n2, final short[] array2, final int n3, final long n4, final long n5, final int[] array3) {
        if (n4 == 1L) {
            final int n6 = (int)(n >> 3);
            array[n6] ^= (byte)(get_q_short(array3, n3) << (int)(n & 0x7L));
            return;
        }
        if (array2 != null) {
            for (long n7 = 0L; n7 < n5; ++n7) {
                array3[(int)n7] = ((array2[(int)n7] ^ 0x1) << 16 | array2[(int)(n7 ^ 0x1L)]);
            }
        }
        else {
            for (long n8 = 0L; n8 < n5; ++n8) {
                array3[(int)n8] = ((get_q_short(array3, (int)(n3 + n8)) ^ 0x1) << 16 | get_q_short(array3, (int)(n3 + (n8 ^ 0x1L))));
            }
        }
        sort32(array3, 0, (int)n5);
        for (long n9 = 0L; n9 < n5; ++n9) {
            int n11;
            final int n10 = n11 = (array3[(int)n9] & 0xFFFF);
            if (n9 < n11) {
                n11 = (int)n9;
            }
            array3[(int)(n5 + n9)] = (n10 << 16 | n11);
        }
        for (long n12 = 0L; n12 < n5; ++n12) {
            array3[(int)n12] = (int)((long)(array3[(int)n12] << 16) | n12);
        }
        sort32(array3, 0, (int)n5);
        for (long n13 = 0L; n13 < n5; ++n13) {
            array3[(int)n13] = (array3[(int)n13] << 16) + (array3[(int)(n5 + n13)] >> 16);
        }
        sort32(array3, 0, (int)n5);
        if (n4 <= 10L) {
            for (long n14 = 0L; n14 < n5; ++n14) {
                array3[(int)(n5 + n14)] = ((array3[(int)n14] & 0xFFFF) << 10 | (array3[(int)(n5 + n14)] & 0x3FF));
            }
            for (long n15 = 1L; n15 < n4 - 1L; ++n15) {
                for (long n16 = 0L; n16 < n5; ++n16) {
                    array3[(int)n16] = (int)((long)((array3[(int)(n5 + n16)] & 0xFFFFFC00) << 6) | n16);
                }
                sort32(array3, 0, (int)n5);
                for (long n17 = 0L; n17 < n5; ++n17) {
                    array3[(int)n17] = (array3[(int)n17] << 20 | array3[(int)(n5 + n17)]);
                }
                sort32(array3, 0, (int)n5);
                for (long n18 = 0L; n18 < n5; ++n18) {
                    final int n19 = array3[(int)n18] & 0xFFFFF;
                    int n20 = (array3[(int)n18] & 0xFFC00) | (array3[(int)(n5 + n18)] & 0x3FF);
                    if (n19 < n20) {
                        n20 = n19;
                    }
                    array3[(int)(n5 + n18)] = n20;
                }
            }
            for (long n21 = 0L; n21 < n5; ++n21) {
                final int n22 = (int)(n5 + n21);
                array3[n22] &= 0x3FF;
            }
        }
        else {
            for (long n23 = 0L; n23 < n5; ++n23) {
                array3[(int)(n5 + n23)] = (array3[(int)n23] << 16 | (array3[(int)(n5 + n23)] & 0xFFFF));
            }
            for (long n24 = 1L; n24 < n4 - 1L; ++n24) {
                for (long n25 = 0L; n25 < n5; ++n25) {
                    array3[(int)n25] = (int)((long)(array3[(int)(n5 + n25)] & 0xFFFF0000) | n25);
                }
                sort32(array3, 0, (int)n5);
                for (long n26 = 0L; n26 < n5; ++n26) {
                    array3[(int)n26] = (array3[(int)n26] << 16 | (array3[(int)(n5 + n26)] & 0xFFFF));
                }
                if (n24 < n4 - 2L) {
                    for (long n27 = 0L; n27 < n5; ++n27) {
                        array3[(int)(n5 + n27)] = ((array3[(int)n27] & 0xFFFF0000) | array3[(int)(n5 + n27)] >> 16);
                    }
                    sort32(array3, (int)n5, (int)(n5 * 2L));
                    for (long n28 = 0L; n28 < n5; ++n28) {
                        array3[(int)(n5 + n28)] = (array3[(int)(n5 + n28)] << 16 | (array3[(int)n28] & 0xFFFF));
                    }
                }
                sort32(array3, 0, (int)n5);
                for (long n29 = 0L; n29 < n5; ++n29) {
                    final int n30 = (array3[(int)(n5 + n29)] & 0xFFFF0000) | (array3[(int)n29] & 0xFFFF);
                    if (n30 < array3[(int)(n5 + n29)]) {
                        array3[(int)(n5 + n29)] = n30;
                    }
                }
            }
            for (long n31 = 0L; n31 < n5; ++n31) {
                final int n32 = (int)(n5 + n31);
                array3[n32] &= 0xFFFF;
            }
        }
        if (array2 != null) {
            for (long n33 = 0L; n33 < n5; ++n33) {
                array3[(int)n33] = (int)((array2[(int)n33] << 16) + n33);
            }
        }
        else {
            for (long n34 = 0L; n34 < n5; ++n34) {
                array3[(int)n34] = (int)((get_q_short(array3, (int)(n3 + n34)) << 16) + n34);
            }
        }
        sort32(array3, 0, (int)n5);
        for (long n35 = 0L; n35 < n5 / 2L; ++n35) {
            final long n36 = 2L * n35;
            final int n37 = array3[(int)(n5 + n36)] & 0x1;
            final int n38 = (int)(n36 + n37);
            final int n39 = n38 ^ 0x1;
            final int n40 = (int)(n >> 3);
            array[n40] ^= (byte)(n37 << (int)(n & 0x7L));
            n += n2;
            array3[(int)(n5 + n36)] = (array3[(int)n36] << 16 | n38);
            array3[(int)(n5 + n36 + 1L)] = (array3[(int)(n36 + 1L)] << 16 | n39);
        }
        sort32(array3, (int)n5, (int)(n5 * 2L));
        n += (2L * n4 - 3L) * n2 * (n5 / 2L);
        for (long n41 = 0L; n41 < n5 / 2L; ++n41) {
            final long n42 = 2L * n41;
            final int n43 = array3[(int)(n5 + n42)] & 0x1;
            final int n44 = (int)(n42 + n43);
            final int n45 = n44 ^ 0x1;
            final int n46 = (int)(n >> 3);
            array[n46] ^= (byte)(n43 << (int)(n & 0x7L));
            n += n2;
            array3[(int)n42] = (n44 << 16 | (array3[(int)(n5 + n42)] & 0xFFFF));
            array3[(int)(n42 + 1L)] = (n45 << 16 | (array3[(int)(n5 + n42 + 1L)] & 0xFFFF));
        }
        sort32(array3, 0, (int)n5);
        n -= (2L * n4 - 2L) * n2 * (n5 / 2L);
        final short[] array4 = new short[(int)n5 * 4];
        for (long n47 = 0L; n47 < n5 * 2L; ++n47) {
            array4[(int)(n47 * 2L + 0L)] = (short)array3[(int)n47];
            array4[(int)(n47 * 2L + 1L)] = (short)((array3[(int)n47] & 0xFFFF0000) >> 16);
        }
        for (long n48 = 0L; n48 < n5 / 2L; ++n48) {
            array4[(int)n48] = (short)((array3[(int)(2L * n48)] & 0xFFFF) >>> 1);
            array4[(int)(n48 + n5 / 2L)] = (short)((array3[(int)(2L * n48 + 1L)] & 0xFFFF) >>> 1);
        }
        for (long n49 = 0L; n49 < n5 / 2L; ++n49) {
            array3[(int)(n5 + n5 / 4L + n49)] = (array4[(int)(n49 * 2L + 1L)] << 16 | array4[(int)(n49 * 2L)]);
        }
        cbrecursion(array, n, n2 * 2L, null, (int)(n5 + n5 / 4L) * 2, n4 - 1L, n5 / 2L, array3);
        cbrecursion(array, n + n2, n2 * 2L, null, (int)((n5 + n5 / 4L) * 2L + n5 / 2L), n4 - 1L, n5 / 2L, array3);
    }
    
    private int pk_gen(final byte[] array, final byte[] array2, final int[] array3, final short[] array4, final long[] array5) {
        final short[] array6 = new short[this.SYS_T + 1];
        array6[this.SYS_T] = 1;
        for (int i = 0; i < this.SYS_T; ++i) {
            array6[i] = Utils.load_gf(array2, 40 + i * 2, this.GFMASK);
        }
        final long[] array7 = new long[1 << this.GFBITS];
        for (int j = 0; j < 1 << this.GFBITS; ++j) {
            array7[j] = array3[j];
            final long[] array8 = array7;
            final int n = j;
            array8[n] <<= 31;
            final long[] array9 = array7;
            final int n2 = j;
            array9[n2] |= j;
            final long[] array10 = array7;
            final int n3 = j;
            array10[n3] &= Long.MAX_VALUE;
        }
        sort64(array7, 0, array7.length);
        for (int k = 1; k < 1 << this.GFBITS; ++k) {
            if (array7[k - 1] >> 31 == array7[k] >> 31) {
                return -1;
            }
        }
        final short[] array11 = new short[this.SYS_N];
        for (int l = 0; l < 1 << this.GFBITS; ++l) {
            array4[l] = (short)(array7[l] & (long)this.GFMASK);
        }
        for (int n4 = 0; n4 < this.SYS_N; ++n4) {
            array11[n4] = Utils.bitrev(array4[n4], this.GFBITS);
        }
        final short[] array12 = new short[this.SYS_N];
        this.root(array12, array6, array11);
        for (int n5 = 0; n5 < this.SYS_N; ++n5) {
            array12[n5] = this.gf.gf_inv(array12[n5]);
        }
        final byte[][] array13 = new byte[this.PK_NROWS][this.SYS_N / 8];
        for (int n6 = 0; n6 < this.PK_NROWS; ++n6) {
            for (int n7 = 0; n7 < this.SYS_N / 8; ++n7) {
                array13[n6][n7] = 0;
            }
        }
        int n8;
        for (n8 = 0; n8 < this.SYS_T; ++n8) {
            for (int n9 = 0; n9 < this.SYS_N; n9 += 8) {
                for (int n10 = 0; n10 < this.GFBITS; ++n10) {
                    array13[n8 * this.GFBITS + n10][n9 / 8] = (byte)((byte)((byte)((byte)((byte)((byte)((byte)((byte)((byte)((byte)((byte)((byte)((byte)((byte)((byte)(array12[n9 + 7] >>> n10 & 0x1) << 1) | (array12[n9 + 6] >>> n10 & 0x1)) << 1) | (array12[n9 + 5] >>> n10 & 0x1)) << 1) | (array12[n9 + 4] >>> n10 & 0x1)) << 1) | (array12[n9 + 3] >>> n10 & 0x1)) << 1) | (array12[n9 + 2] >>> n10 & 0x1)) << 1) | (array12[n9 + 1] >>> n10 & 0x1)) << 1) | (array12[n9 + 0] >>> n10 & 0x1));
                }
            }
            for (int n11 = 0; n11 < this.SYS_N; ++n11) {
                array12[n11] = this.gf.gf_mul(array12[n11], array11[n11]);
            }
        }
        for (int n12 = 0; n12 < this.PK_NROWS; ++n12) {
            n8 = n12 >>> 3;
            final int n13 = n12 & 0x7;
            if (this.usePivots && n12 == this.PK_NROWS - 32 && this.mov_columns(array13, array4, array5) != 0) {
                return -1;
            }
            for (int n14 = n12 + 1; n14 < this.PK_NROWS; ++n14) {
                final byte b = (byte)(-(byte)((byte)((byte)(array13[n12][n8] ^ array13[n14][n8]) >> n13) & 0x1));
                for (int n15 = 0; n15 < this.SYS_N / 8; ++n15) {
                    final byte[] array14 = array13[n12];
                    final int n16 = n15;
                    array14[n16] ^= (byte)(array13[n14][n15] & b);
                }
            }
            if ((array13[n12][n8] >> n13 & 0x1) == 0x0) {
                return -1;
            }
            for (int n17 = 0; n17 < this.PK_NROWS; ++n17) {
                if (n17 != n12) {
                    final byte b2 = (byte)(-(byte)((byte)(array13[n17][n8] >> n13) & 0x1));
                    for (int n18 = 0; n18 < this.SYS_N / 8; ++n18) {
                        final byte[] array15 = array13[n17];
                        final int n19 = n18;
                        array15[n19] ^= (byte)(array13[n12][n18] & b2);
                    }
                }
            }
        }
        if (array != null) {
            if (this.usePadding) {
                int n20 = 0;
                final int n21 = this.PK_NROWS % 8;
                if (n21 == 0) {
                    System.arraycopy(array13[n8], (this.PK_NROWS - 1) / 8, array, n20, this.SYS_N / 8);
                    final int n22 = n20 + this.SYS_N / 8;
                }
                else {
                    for (int n23 = 0; n23 < this.PK_NROWS; ++n23) {
                        int n24;
                        for (n24 = (this.PK_NROWS - 1) / 8; n24 < this.SYS_N / 8 - 1; ++n24) {
                            array[n20++] = (byte)((array13[n23][n24] & 0xFF) >>> n21 | array13[n23][n24 + 1] << 8 - n21);
                        }
                        array[n20++] = (byte)((array13[n23][n24] & 0xFF) >>> n21);
                    }
                }
            }
            else {
                final int n25 = (this.SYS_N - this.PK_NROWS + 7) / 8;
                for (int n26 = 0; n26 < this.PK_NROWS; ++n26) {
                    System.arraycopy(array13[n26], this.PK_NROWS / 8, array, n25 * n26, n25);
                }
            }
        }
        return 0;
    }
    
    private short eval(final short[] array, final short n) {
        short n2 = array[this.SYS_T];
        for (int i = this.SYS_T - 1; i >= 0; --i) {
            n2 = (short)(this.gf.gf_mul(n2, n) ^ array[i]);
        }
        return n2;
    }
    
    private void root(final short[] array, final short[] array2, final short[] array3) {
        for (int i = 0; i < this.SYS_N; ++i) {
            array[i] = this.eval(array2, array3[i]);
        }
    }
    
    private int generate_irr_poly(final short[] array) {
        final short[][] array2 = new short[this.SYS_T + 1][this.SYS_T];
        array2[0][0] = 1;
        System.arraycopy(array, 0, array2[1], 0, this.SYS_T);
        final int[] array3 = new int[this.SYS_T * 2 - 1];
        int i;
        for (i = 2; i < this.SYS_T; i += 2) {
            this.gf.gf_sqr_poly(this.SYS_T, this.poly, array2[i], array2[i >>> 1], array3);
            this.gf.gf_mul_poly(this.SYS_T, this.poly, array2[i + 1], array2[i], array, array3);
        }
        if (i == this.SYS_T) {
            this.gf.gf_sqr_poly(this.SYS_T, this.poly, array2[i], array2[i >>> 1], array3);
        }
        for (int j = 0; j < this.SYS_T; ++j) {
            for (int k = j + 1; k < this.SYS_T; ++k) {
                final short gf_iszero = this.gf.gf_iszero(array2[j][j]);
                for (int l = j; l < this.SYS_T + 1; ++l) {
                    final short[] array4 = array2[l];
                    final int n = j;
                    array4[n] ^= (short)(array2[l][k] & gf_iszero);
                }
            }
            if (array2[j][j] == 0) {
                return -1;
            }
            final short gf_inv = this.gf.gf_inv(array2[j][j]);
            for (int n2 = j; n2 < this.SYS_T + 1; ++n2) {
                array2[n2][j] = this.gf.gf_mul(array2[n2][j], gf_inv);
            }
            for (int n3 = 0; n3 < this.SYS_T; ++n3) {
                if (n3 != j) {
                    final short n4 = array2[j][n3];
                    for (int n5 = j; n5 <= this.SYS_T; ++n5) {
                        final short[] array5 = array2[n5];
                        final int n6 = n3;
                        array5[n6] ^= this.gf.gf_mul(array2[n5][j], n4);
                    }
                }
            }
        }
        System.arraycopy(array2[this.SYS_T], 0, array, 0, this.SYS_T);
        return 0;
    }
    
    int check_pk_padding(final byte[] array) {
        byte b = 0;
        for (int i = 0; i < this.PK_NROWS; ++i) {
            b |= array[i * this.PK_ROW_BYTES + this.PK_ROW_BYTES - 1];
        }
        return (byte)(((byte)((byte)((b & 0xFF) >>> this.PK_NCOLS % 8) - 1) & 0xFF) >>> 7) - 1;
    }
    
    int check_c_padding(final byte[] array) {
        return (byte)(((byte)((byte)((array[this.SYND_BYTES - 1] & 0xFF) >>> this.PK_NROWS % 8) - 1) & 0xFF) >>> 7) - 1;
    }
    
    public int getDefaultSessionKeySize() {
        return this.defaultKeySize;
    }
    
    private static void sort32(final int[] array, final int n, final int n2) {
        final int n3 = n2 - n;
        if (n3 < 2) {
            return;
        }
        int i;
        for (i = 1; i < n3 - i; i += i) {}
        for (int j = i; j > 0; j >>>= 1) {
            for (int k = 0; k < n3 - j; ++k) {
                if ((k & j) == 0x0) {
                    final int n4 = array[n + k + j] ^ array[n + k];
                    final int n5 = array[n + k + j] - array[n + k];
                    final int n6 = (n5 ^ (n4 & (n5 ^ array[n + k + j]))) >> 31 & n4;
                    final int n7 = n + k;
                    array[n7] ^= n6;
                    final int n8 = n + k + j;
                    array[n8] ^= n6;
                }
            }
            int l = 0;
            for (int n9 = i; n9 > j; n9 >>>= 1) {
                while (l < n3 - n9) {
                    if ((l & j) == 0x0) {
                        int n10 = array[n + l + j];
                        for (int n11 = n9; n11 > j; n11 >>>= 1) {
                            final int n12 = array[n + l + n11] ^ n10;
                            final int n13 = array[n + l + n11] - n10;
                            final int n14 = (n13 ^ (n12 & (n13 ^ array[n + l + n11]))) >> 31 & n12;
                            n10 ^= n14;
                            final int n15 = n + l + n11;
                            array[n15] ^= n14;
                        }
                        array[n + l + j] = n10;
                    }
                    ++l;
                }
            }
        }
    }
    
    private static void sort64(final long[] array, final int n, final int n2) {
        final int n3 = n2 - n;
        if (n3 < 2) {
            return;
        }
        int i;
        for (i = 1; i < n3 - i; i += i) {}
        for (int j = i; j > 0; j >>>= 1) {
            for (int k = 0; k < n3 - j; ++k) {
                if ((k & j) == 0x0) {
                    final long n4 = -(array[n + k + j] - array[n + k] >>> 63) & (array[n + k] ^ array[n + k + j]);
                    final int n5 = n + k;
                    array[n5] ^= n4;
                    final int n6 = n + k + j;
                    array[n6] ^= n4;
                }
            }
            int l = 0;
            for (int n7 = i; n7 > j; n7 >>>= 1) {
                while (l < n3 - n7) {
                    if ((l & j) == 0x0) {
                        long n8 = array[n + l + j];
                        for (int n9 = n7; n9 > j; n9 >>>= 1) {
                            final long n10 = -(array[n + l + n9] - n8 >>> 63) & (n8 ^ array[n + l + n9]);
                            n8 ^= n10;
                            final int n11 = n + l + n9;
                            array[n11] ^= n10;
                        }
                        array[n + l + j] = n8;
                    }
                    ++l;
                }
            }
        }
    }
}
