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

package org.bouncycastle.pqc.crypto.mayo;

import org.bouncycastle.util.Longs;
import org.bouncycastle.util.Pack;
import org.bouncycastle.util.Bytes;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.GF16;
import org.bouncycastle.crypto.digests.SHAKEDigest;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.CipherParameters;
import java.security.SecureRandom;
import org.bouncycastle.pqc.crypto.MessageSigner;

public class MayoSigner implements MessageSigner
{
    private SecureRandom random;
    private MayoParameters params;
    private MayoPublicKeyParameters pubKey;
    private MayoPrivateKeyParameters privKey;
    private static final int F_TAIL_LEN = 4;
    private static final long EVEN_BYTES = 71777214294589695L;
    private static final long EVEN_2BYTES = 281470681808895L;
    
    @Override
    public void init(final boolean b, final CipherParameters cipherParameters) {
        if (b) {
            this.pubKey = null;
            if (cipherParameters instanceof ParametersWithRandom) {
                final ParametersWithRandom parametersWithRandom = (ParametersWithRandom)cipherParameters;
                this.privKey = (MayoPrivateKeyParameters)parametersWithRandom.getParameters();
                this.random = parametersWithRandom.getRandom();
            }
            else {
                this.privKey = (MayoPrivateKeyParameters)cipherParameters;
                this.random = CryptoServicesRegistrar.getSecureRandom();
            }
            this.params = this.privKey.getParameters();
        }
        else {
            this.pubKey = (MayoPublicKeyParameters)cipherParameters;
            this.params = this.pubKey.getParameters();
            this.privKey = null;
            this.random = null;
        }
    }
    
    @Override
    public byte[] generateSignature(final byte[] array) {
        final int k = this.params.getK();
        final int v = this.params.getV();
        final int o = this.params.getO();
        final int n = this.params.getN();
        final int m = this.params.getM();
        final int vBytes = this.params.getVBytes();
        final int oBytes = this.params.getOBytes();
        final int saltBytes = this.params.getSaltBytes();
        final int mVecLimbs = this.params.getMVecLimbs();
        final int p1Limbs = this.params.getP1Limbs();
        final int pkSeedBytes = this.params.getPkSeedBytes();
        final int digestBytes = this.params.getDigestBytes();
        final int skSeedBytes = this.params.getSkSeedBytes();
        final byte[] array2 = new byte[this.params.getMBytes()];
        final byte[] array3 = new byte[m];
        final byte[] array4 = new byte[m];
        final byte[] bytes = new byte[saltBytes];
        final byte[] array5 = new byte[k * vBytes + this.params.getRBytes()];
        final byte[] array6 = new byte[v * k];
        final int n2 = k * o;
        final int n3 = k * n;
        final byte[] array7 = new byte[(m + 7) / 8 * 8 * (n2 + 1)];
        final byte[] array8 = new byte[n3];
        final byte[] array9 = new byte[n2 + 1];
        final byte[] array10 = new byte[n3];
        final byte[] array11 = new byte[digestBytes + saltBytes + skSeedBytes + 1];
        final byte[] array12 = new byte[this.params.getSigBytes()];
        final long[] array13 = new long[p1Limbs + this.params.getP2Limbs()];
        final byte[] array14 = new byte[v * o];
        final long[] array15 = new long[n2 * mVecLimbs];
        final long[] array16 = new long[k * k * mVecLimbs];
        final SHAKEDigest shakeDigest = new SHAKEDigest(256);
        try {
            final byte[] seedSk = this.privKey.getSeedSk();
            final int n4 = pkSeedBytes + oBytes;
            final byte[] array17 = new byte[n4];
            shakeDigest.update(seedSk, 0, seedSk.length);
            shakeDigest.doFinal(array17, 0, n4);
            GF16.decode(array17, pkSeedBytes, array14, 0, array14.length);
            Utils.expandP1P2(this.params, array13, array17);
            int n5 = 0;
            for (int n6 = o * mVecLimbs, i = 0, n7 = 0, n8 = 0; i < v; ++i, n7 += o, n8 += n6) {
                for (int j = i, n9 = n7, n10 = n8; j < v; ++j, n9 += o, n10 += n6) {
                    if (j == i) {
                        n5 += mVecLimbs;
                    }
                    else {
                        for (int l = 0, n11 = p1Limbs; l < o; ++l, n11 += mVecLimbs) {
                            GF16Utils.mVecMulAdd(mVecLimbs, array13, n5, array14[n9 + l], array13, n8 + n11);
                            GF16Utils.mVecMulAdd(mVecLimbs, array13, n5, array14[n7 + l], array13, n10 + n11);
                        }
                        n5 += mVecLimbs;
                    }
                }
            }
            Arrays.fill(array17, (byte)0);
            shakeDigest.update(array, 0, array.length);
            shakeDigest.doFinal(array11, 0, digestBytes);
            this.random.nextBytes(bytes);
            System.arraycopy(bytes, 0, array11, digestBytes, bytes.length);
            System.arraycopy(seedSk, 0, array11, digestBytes + saltBytes, skSeedBytes);
            shakeDigest.update(array11, 0, digestBytes + saltBytes + skSeedBytes);
            shakeDigest.doFinal(bytes, 0, saltBytes);
            System.arraycopy(bytes, 0, array11, digestBytes, saltBytes);
            shakeDigest.update(array11, 0, digestBytes + saltBytes);
            shakeDigest.doFinal(array2, 0, this.params.getMBytes());
            GF16.decode(array2, array3, m);
            final long[] array18 = new long[v * k * mVecLimbs];
            final byte[] array19 = new byte[v];
            for (int n12 = 0; n12 <= 255; ++n12) {
                array11[array11.length - 1] = (byte)n12;
                shakeDigest.update(array11, 0, array11.length);
                shakeDigest.doFinal(array5, 0, array5.length);
                for (int n13 = 0; n13 < k; ++n13) {
                    GF16.decode(array5, n13 * vBytes, array6, n13 * v, v);
                }
                GF16Utils.mulAddMatXMMat(mVecLimbs, array6, array13, p1Limbs, array15, k, v, o);
                GF16Utils.mulAddMUpperTriangularMatXMatTrans(mVecLimbs, array13, array6, array18, v, k);
                GF16Utils.mulAddMatXMMat(mVecLimbs, array6, array18, array16, k, v);
                this.computeRHS(array16, array3, array4);
                this.computeA(array15, array7);
                GF16.decode(array5, k * vBytes, array9, 0, n2);
                if (this.sampleSolution(array7, array4, array9, array8)) {
                    break;
                }
                Arrays.fill(array15, 0L);
                Arrays.fill(array16, 0L);
            }
            for (int n14 = 0, n15 = 0, n16 = 0, n17 = 0; n14 < k; ++n14, n15 += o, n16 += n, n17 += v) {
                GF16Utils.matMul(array14, array8, n15, array19, o, v);
                Bytes.xor(v, array6, n17, array19, array10, n16);
                System.arraycopy(array8, n15, array10, n16 + v, o);
            }
            GF16.encode(array10, array12, n3);
            System.arraycopy(bytes, 0, array12, array12.length - saltBytes, saltBytes);
            return Arrays.concatenate(array12, array);
        }
        finally {
            Arrays.fill(array2, (byte)0);
            Arrays.fill(array3, (byte)0);
            Arrays.fill(array4, (byte)0);
            Arrays.fill(bytes, (byte)0);
            Arrays.fill(array5, (byte)0);
            Arrays.fill(array6, (byte)0);
            Arrays.fill(array7, (byte)0);
            Arrays.fill(array8, (byte)0);
            Arrays.fill(array9, (byte)0);
            Arrays.fill(array10, (byte)0);
            Arrays.fill(array11, (byte)0);
        }
    }
    
    @Override
    public boolean verifySignature(final byte[] array, final byte[] array2) {
        final int m = this.params.getM();
        final int n = this.params.getN();
        final int k = this.params.getK();
        final int n2 = k * n;
        final int p1Limbs = this.params.getP1Limbs();
        final int p2Limbs = this.params.getP2Limbs();
        final int p3Limbs = this.params.getP3Limbs();
        final int mBytes = this.params.getMBytes();
        final int sigBytes = this.params.getSigBytes();
        final int digestBytes = this.params.getDigestBytes();
        final int saltBytes = this.params.getSaltBytes();
        final int mVecLimbs = this.params.getMVecLimbs();
        final byte[] array3 = new byte[mBytes];
        final byte[] array4 = new byte[m];
        final byte[] array5 = new byte[m << 1];
        final byte[] array6 = new byte[n2];
        final long[] array7 = new long[p1Limbs + p2Limbs + p3Limbs];
        final byte[] array8 = new byte[digestBytes + saltBytes];
        final byte[] encoded = this.pubKey.getEncoded();
        Utils.expandP1P2(this.params, array7, encoded);
        Utils.unpackMVecs(encoded, this.params.getPkSeedBytes(), array7, p1Limbs + p2Limbs, p3Limbs / mVecLimbs, m);
        final SHAKEDigest shakeDigest = new SHAKEDigest(256);
        shakeDigest.update(array, 0, array.length);
        shakeDigest.doFinal(array8, 0, digestBytes);
        shakeDigest.update(array8, 0, digestBytes);
        shakeDigest.update(array2, sigBytes - saltBytes, saltBytes);
        shakeDigest.doFinal(array3, 0, mBytes);
        GF16.decode(array3, array4, m);
        GF16.decode(array2, array6, n2);
        final long[] array9 = new long[k * k * mVecLimbs];
        final long[] array10 = new long[n2 * mVecLimbs];
        mayoGenericMCalculatePS(this.params, array7, p1Limbs, p1Limbs + p2Limbs, array6, this.params.getV(), this.params.getO(), k, array10);
        mayoGenericMCalculateSPS(array10, array6, mVecLimbs, k, n, array9);
        this.computeRHS(array9, new byte[m], array5);
        return Arrays.constantTimeAreEqual(m, array5, 0, array4, 0);
    }
    
    void computeRHS(final long[] array, final byte[] array2, final byte[] array3) {
        final int m = this.params.getM();
        final int mVecLimbs = this.params.getMVecLimbs();
        final int k = this.params.getK();
        final int[] fTail = this.params.getFTail();
        final int n = (m - 1 & 0xF) << 2;
        if ((m & 0xF) != 0x0) {
            final long n2 = (1L << ((m & 0xF) << 2)) - 1L;
            for (int n3 = k * k, i = 0, n4 = mVecLimbs - 1; i < n3; ++i, n4 += mVecLimbs) {
                final int n5 = n4;
                array[n5] &= n2;
            }
        }
        final long[] array4 = new long[mVecLimbs];
        final byte[] array5 = new byte[mVecLimbs << 3];
        for (int n6 = k * mVecLimbs, j = k - 1, n7 = j * mVecLimbs, n8 = n7 * k; j >= 0; --j, n7 -= mVecLimbs, n8 -= n6) {
            for (int l = j, n9 = n7, n10 = n8; l < k; ++l, n9 += mVecLimbs, n10 += n6) {
                final int n11 = (int)(array4[mVecLimbs - 1] >>> n & 0xFL);
                final long[] array6 = array4;
                final int n12 = mVecLimbs - 1;
                array6[n12] <<= 4;
                for (int n13 = mVecLimbs - 2; n13 >= 0; --n13) {
                    final long[] array7 = array4;
                    final int n14 = n13 + 1;
                    array7[n14] ^= array4[n13] >>> 60;
                    final long[] array8 = array4;
                    final int n15 = n13;
                    array8[n15] <<= 4;
                }
                Pack.longToLittleEndian(array4, array5, 0);
                for (int n16 = 0; n16 < 4; ++n16) {
                    final int n17 = fTail[n16];
                    if (n17 != 0) {
                        final long n18 = GF16.mul(n11, n17);
                        if ((n16 & 0x1) == 0x0) {
                            final byte[] array9 = array5;
                            final int n19 = n16 >> 1;
                            array9[n19] ^= (byte)(n18 & 0xFL);
                        }
                        else {
                            final byte[] array10 = array5;
                            final int n20 = n16 >> 1;
                            array10[n20] ^= (byte)((n18 & 0xFL) << 4);
                        }
                    }
                }
                Pack.littleEndianToLong(array5, 0, array4);
                final int n21 = n8 + n9;
                final int n22 = n10 + n7;
                final boolean b = j == l;
                for (int n23 = 0; n23 < mVecLimbs; ++n23) {
                    long n24 = array[n21 + n23];
                    if (!b) {
                        n24 ^= array[n22 + n23];
                    }
                    final long[] array11 = array4;
                    final int n25 = n23;
                    array11[n25] ^= n24;
                }
            }
        }
        Pack.longToLittleEndian(array4, array5, 0);
        for (int n26 = 0; n26 < m; n26 += 2) {
            final int n27 = n26 >> 1;
            array3[n26] = (byte)(array2[n26] ^ (array5[n27] & 0xF));
            array3[n26 + 1] = (byte)(array2[n26 + 1] ^ (array5[n27] >>> 4 & 0xF));
        }
    }
    
    void computeA(final long[] array, final byte[] array2) {
        final int k = this.params.getK();
        final int o = this.params.getO();
        final int m = this.params.getM();
        final int mVecLimbs = this.params.getMVecLimbs();
        final int aCols = this.params.getACols();
        final int[] fTail = this.params.getFTail();
        int n = 0;
        int n2 = 0;
        final int n3 = m + 7 >>> 3;
        final int n4 = o * k;
        final int n5 = o * mVecLimbs;
        final int n6 = n4 + 15 >> 4 << 4;
        final long[] array3 = new long[n6 * n3 << 4];
        if ((m & 0xF) != 0x0) {
            final long n7 = (1L << ((m & 0xF) << 2)) - 1L;
            for (int i = 0, n8 = mVecLimbs - 1; i < n4; ++i, n8 += mVecLimbs) {
                final int n9 = n8;
                array[n9] &= n7;
            }
        }
        for (int j = 0, n10 = 0, n11 = 0; j < k; ++j, n10 += o, n11 += n5) {
            for (int l = k - 1, n12 = l * n5, n13 = l * o; l >= j; --l, n12 -= n5, n13 -= o) {
                for (int n14 = 0, n15 = 0; n14 < o; ++n14, n15 += mVecLimbs) {
                    for (int n16 = 0, n17 = 0; n16 < mVecLimbs; ++n16, n17 += n6) {
                        final long n18 = array[n12 + n16 + n15];
                        final int n19 = n10 + n14 + n2 + n17;
                        final long[] array4 = array3;
                        final int n20 = n19;
                        array4[n20] ^= n18 << n;
                        if (n > 0) {
                            final long[] array5 = array3;
                            final int n21 = n19 + n6;
                            array5[n21] ^= n18 >>> 64 - n;
                        }
                    }
                }
                if (j != l) {
                    for (int n22 = 0, n23 = 0; n22 < o; ++n22, n23 += mVecLimbs) {
                        for (int n24 = 0, n25 = 0; n24 < mVecLimbs; ++n24, n25 += n6) {
                            final long n26 = array[n11 + n24 + n23];
                            final int n27 = n13 + n22 + n2 + n25;
                            final long[] array6 = array3;
                            final int n28 = n27;
                            array6[n28] ^= n26 << n;
                            if (n > 0) {
                                final long[] array7 = array3;
                                final int n29 = n27 + n6;
                                array7[n29] ^= n26 >>> 64 - n;
                            }
                        }
                    }
                }
                n += 4;
                if (n == 64) {
                    n2 += n6;
                    n = 0;
                }
            }
        }
        for (int n30 = 0; n30 < n6 * (m + ((k + 1) * k >> 1) + 15 >>> 4); n30 += 16) {
            transpose16x16Nibbles(array3, n30);
        }
        final byte[] array8 = new byte[16];
        int n31 = 0;
        int n32 = 0;
        while (n31 < 4) {
            final int n33 = fTail[n31];
            array8[n32++] = (byte)GF16.mul(n33, 1);
            array8[n32++] = (byte)GF16.mul(n33, 2);
            array8[n32++] = (byte)GF16.mul(n33, 4);
            array8[n32++] = (byte)GF16.mul(n33, 8);
            ++n31;
        }
        for (int n34 = 0; n34 < n6; n34 += 16) {
            for (int n35 = m; n35 < m + ((k + 1) * k >>> 1); ++n35) {
                final int n36 = (n35 >>> 4) * n6 + n34 + (n35 & 0xF);
                final long n37 = array3[n36] & 0x1111111111111111L;
                final long n38 = array3[n36] >>> 1 & 0x1111111111111111L;
                final long n39 = array3[n36] >>> 2 & 0x1111111111111111L;
                final long n40 = array3[n36] >>> 3 & 0x1111111111111111L;
                for (int n41 = 0, n42 = 0; n41 < 4; ++n41, n42 += 4) {
                    final int n43 = n35 + n41 - m;
                    final int n44 = (n43 >> 4) * n6 + n34 + (n43 & 0xF);
                    final long[] array9 = array3;
                    final int n45 = n44;
                    array9[n45] ^= (n37 * array8[n42] ^ n38 * array8[n42 + 1] ^ n39 * array8[n42 + 2] ^ n40 * array8[n42 + 3]);
                }
            }
        }
        final byte[] longToLittleEndian = Pack.longToLittleEndian(array3);
        for (int n46 = 0; n46 < m; n46 += 16) {
            for (int n47 = 0; n47 < aCols - 1; n47 += 16) {
                for (int n48 = 0; n48 + n46 < m; ++n48) {
                    GF16.decode(longToLittleEndian, (n46 * n6 >> 4) + n47 + n48 << 3, array2, (n46 + n48) * aCols + n47, Math.min(16, aCols - 1 - n47));
                }
            }
        }
    }
    
    private static void transpose16x16Nibbles(final long[] array, final int n) {
        for (int i = 0; i < 16; i += 2) {
            final int n2 = n + i;
            final int n3 = n2 + 1;
            final long n4 = (array[n2] >>> 4 ^ array[n3]) & 0xF0F0F0F0F0F0F0FL;
            final int n5 = n2;
            array[n5] ^= n4 << 4;
            final int n6 = n3;
            array[n6] ^= n4;
        }
        int j = 0;
        int n7 = n;
        while (j < 16) {
            final long n8 = (array[n7] >>> 8 ^ array[n7 + 2]) & 0xFF00FF00FF00FFL;
            final long n9 = (array[n7 + 1] >>> 8 ^ array[n7 + 3]) & 0xFF00FF00FF00FFL;
            final int n10 = n7++;
            array[n10] ^= n8 << 8;
            final int n11 = n7++;
            array[n11] ^= n9 << 8;
            final int n12 = n7++;
            array[n12] ^= n8;
            final int n13 = n7++;
            array[n13] ^= n9;
            j += 4;
        }
        for (int k = 0; k < 4; ++k) {
            final int n14 = n + k;
            final long n15 = (array[n14] >>> 16 ^ array[n14 + 4]) & 0xFFFF0000FFFFL;
            final long n16 = (array[n14 + 8] >>> 16 ^ array[n14 + 12]) & 0xFFFF0000FFFFL;
            final int n17 = n14;
            array[n17] ^= n15 << 16;
            final int n18 = n14 + 8;
            array[n18] ^= n16 << 16;
            final int n19 = n14 + 4;
            array[n19] ^= n15;
            final int n20 = n14 + 12;
            array[n20] ^= n16;
        }
        for (int l = 0; l < 8; ++l) {
            final int n21 = n + l;
            final long n22 = (array[n21] >>> 32 ^ array[n21 + 8]) & 0xFFFFFFFFL;
            final int n23 = n21;
            array[n23] ^= n22 << 32;
            final int n24 = n21 + 8;
            array[n24] ^= n22;
        }
    }
    
    boolean sampleSolution(final byte[] array, final byte[] array2, final byte[] array3, final byte[] array4) {
        final int k = this.params.getK();
        final int o = this.params.getO();
        final int m = this.params.getM();
        final int aCols = this.params.getACols();
        final int b = k * o;
        System.arraycopy(array3, 0, array4, 0, b);
        final byte[] array5 = new byte[m];
        GF16Utils.matMul(array, array3, 0, array5, b + 1, m);
        for (int i = 0, n = b; i < m; ++i, n += b + 1) {
            array[n] = (byte)(array2[i] ^ array5[i]);
        }
        this.ef(array, m, aCols);
        boolean b2 = false;
        for (int j = 0, n2 = (m - 1) * aCols; j < aCols - 1; ++j, ++n2) {
            b2 |= (array[n2] != 0);
        }
        if (!b2) {
            return false;
        }
        for (int l = m - 1, n3 = l * aCols; l >= 0; --l, n3 -= aCols) {
            byte b3 = 0;
            for (int min = Math.min(l + 32 / (m - l), b), n4 = l; n4 <= min; ++n4) {
                final byte b4 = (byte)(-(array[n3 + n4] & 0xFF) >> 31);
                final byte b5 = (byte)(b4 & ~b3 & array[n3 + aCols - 1]);
                final int n5 = n4;
                array4[n5] ^= b5;
                for (int n6 = 0, n7 = n4, n8 = aCols - 1; n6 < l; n6 += 8, n7 += aCols << 3, n8 += aCols << 3) {
                    long n9 = 0L;
                    for (int n10 = 0, n11 = 0; n10 < 8; ++n10, n11 += aCols) {
                        n9 ^= (long)(array[n7 + n11] & 0xFF) << (n10 << 3);
                    }
                    final long mulFx8 = GF16Utils.mulFx8(b5, n9);
                    for (int n12 = 0, n13 = 0; n12 < 8; ++n12, n13 += aCols) {
                        final int n14 = n8 + n13;
                        array[n14] ^= (byte)(mulFx8 >> (n12 << 3) & 0xFL);
                    }
                }
                b3 |= b4;
            }
        }
        return true;
    }
    
    void ef(final byte[] array, final int n, final int n2) {
        final int n3 = n2 + 15 >> 4;
        final long[] array2 = new long[n3];
        final long[] array3 = new long[n3];
        final long[] array4 = new long[n * n3];
        final int n4 = this.params.getO() * this.params.getK() + 16;
        final byte[] array5 = new byte[n4 >> 1];
        final int n5 = n4 >> 4;
        for (int i = 0, n6 = 0, n7 = 0; i < n; ++i, n6 += n2, n7 += n3) {
            for (int j = 0; j < n3; ++j) {
                long n8 = 0L;
                for (int k = 0; k < 16; ++k) {
                    final int n9 = (j << 4) + k;
                    if (n9 < n2) {
                        n8 |= ((long)array[n6 + n9] & 0xFL) << (k << 2);
                    }
                }
                array4[j + n7] = n8;
            }
        }
        int n10 = 0;
        for (int l = 0; l < n2; ++l) {
            final int max = Math.max(0, l + n - n2);
            final int min = Math.min(n - 1, l);
            Arrays.clear(array2);
            Arrays.clear(array3);
            int n11 = 0;
            long n12 = -1L;
            for (int min2 = Math.min(n - 1, min + 32), n13 = max, n14 = max * n3; n13 <= min2; ++n13, n14 += n3) {
                final long n15 = ~ctCompare64(n13, n10);
                final long n16 = n10 - (long)n13 >> 63;
                for (int n17 = 0; n17 < n3; ++n17) {
                    final long[] array6 = array2;
                    final int n18 = n17;
                    array6[n18] ^= ((n15 | (n16 & n12)) & array4[n14 + n17]);
                }
                n11 = (int)(array2[l >>> 4] >>> ((l & 0xF) << 2) & 0xFL);
                n12 = ~(-n11 >> 63);
            }
            vecMulAddU64(n3, array2, GF16.inv((byte)n11), array3);
            for (int n19 = max, n20 = max * n3; n19 <= min; ++n19, n20 += n3) {
                final long n21 = ~ctCompare64(n19, n10) & ~n12;
                final long n22 = ~n21;
                for (int n23 = 0, n24 = n20; n23 < n3; ++n23, ++n24) {
                    array4[n24] = ((n22 & array4[n24]) | (n21 & array3[n23]));
                }
            }
            for (int n25 = max, n26 = max * n3; n25 < n; ++n25, n26 += n3) {
                vecMulAddU64(n3, array3, (byte)(((n25 > n10) ? -1 : 0) & (int)(array4[n26 + (l >>> 4)] >>> ((l & 0xF) << 2) & 0xFL)), array4, n26);
            }
            if (n11 != 0) {
                ++n10;
            }
        }
        int n27 = 0;
        for (int n28 = 0, n29 = 0; n28 < n; ++n28, n29 += n3) {
            Pack.longToLittleEndian(array4, n29, n5, array5, 0);
            GF16.decode(array5, 0, array, n27, n2);
            n27 += n2;
        }
    }
    
    private static long ctCompare64(final int n, final int n2) {
        return -(n ^ n2) >> 63;
    }
    
    private static void vecMulAddU64(final int n, final long[] array, final byte b, final long[] array2) {
        final int mulTable = mulTable(b & 0xFF);
        for (int i = 0; i < n; ++i) {
            final long n2 = (array[i] & 0x1111111111111111L) * (mulTable & 0xFF) ^ (array[i] >>> 1 & 0x1111111111111111L) * (mulTable >>> 8 & 0xF) ^ (array[i] >>> 2 & 0x1111111111111111L) * (mulTable >>> 16 & 0xF) ^ (array[i] >>> 3 & 0x1111111111111111L) * (mulTable >>> 24 & 0xF);
            final int n3 = i;
            array2[n3] ^= n2;
        }
    }
    
    private static void vecMulAddU64(final int n, final long[] array, final byte b, final long[] array2, final int n2) {
        final int mulTable = mulTable(b & 0xFF);
        for (int i = 0; i < n; ++i) {
            final long n3 = (array[i] & 0x1111111111111111L) * (mulTable & 0xFF) ^ (array[i] >>> 1 & 0x1111111111111111L) * (mulTable >>> 8 & 0xF) ^ (array[i] >>> 2 & 0x1111111111111111L) * (mulTable >>> 16 & 0xF) ^ (array[i] >>> 3 & 0x1111111111111111L) * (mulTable >>> 24 & 0xF);
            final int n4 = n2 + i;
            array2[n4] ^= n3;
        }
    }
    
    private static int mulTable(final int n) {
        final int n2 = n * 134480385;
        final int n3 = n2 & 0xF0F0F0F0;
        return n2 ^ n3 >>> 4 ^ n3 >>> 3;
    }
    
    private static void mayoGenericMCalculatePS(final MayoParameters mayoParameters, final long[] array, final int n, final int n2, final byte[] array2, final int n3, final int n4, final int n5, final long[] array3) {
        final int n6 = n4 + n3;
        final int mVecLimbs = mayoParameters.getMVecLimbs();
        final long[] array4 = new long[mVecLimbs * mayoParameters.getK() * mayoParameters.getN() * mVecLimbs << 4];
        final int n7 = n4 * mVecLimbs;
        int n8 = 0;
        for (int i = 0, n9 = 0, n10 = 0; i < n3; ++i, n9 += n5, n10 += n7) {
            for (int j = i; j < n3; ++j) {
                for (int k = 0, n11 = 0; k < n5; ++k, n11 += n6) {
                    Longs.xorTo(mVecLimbs, array, n8, array4, ((n9 + k << 4) + (array2[n11 + j] & 0xFF)) * mVecLimbs);
                }
                n8 += mVecLimbs;
            }
            for (int l = 0, n12 = n10; l < n4; ++l, n12 += mVecLimbs) {
                for (int n13 = 0, n14 = 0; n13 < n5; ++n13, n14 += n6) {
                    Longs.xorTo(mVecLimbs, array, n + n12, array4, ((n9 + n13 << 4) + (array2[n14 + l + n3] & 0xFF)) * mVecLimbs);
                }
            }
        }
        int n15 = 0;
        for (int n16 = n3, n17 = n3 * n5; n16 < n6; ++n16, n17 += n5) {
            for (int n18 = n16; n18 < n6; ++n18) {
                for (int n19 = 0, n20 = 0; n19 < n5; ++n19, n20 += n6) {
                    Longs.xorTo(mVecLimbs, array, n2 + n15, array4, ((n17 + n19 << 4) + (array2[n20 + n18] & 0xFF)) * mVecLimbs);
                }
                n15 += mVecLimbs;
            }
        }
        mVecMultiplyBins(mVecLimbs, n6 * n5, array4, array3);
    }
    
    private static void mayoGenericMCalculateSPS(final long[] array, final byte[] array2, final int n, final int n2, final int n3, final long[] array3) {
        final int n4 = n2 * n2;
        final long[] array4 = new long[n * n4 << 4];
        for (int n5 = n2 * n, i = 0, n6 = 0, n7 = 0; i < n2; ++i, n6 += n3, n7 += n5 << 4) {
            for (int j = 0, n8 = 0; j < n3; ++j, n8 += n5) {
                final int n9 = (array2[n6 + j] & 0xFF) * n + n7;
                for (int k = 0, n10 = 0; k < n2; ++k, n10 += n) {
                    Longs.xorTo(n, array, n8 + n10, array4, n9 + (n10 << 4));
                }
            }
        }
        mVecMultiplyBins(n, n4, array4, array3);
    }
    
    private static void mVecMultiplyBins(final int n, final int n2, final long[] array, final long[] array2) {
        final int n3 = n + n;
        final int n4 = n3 + n;
        final int n5 = n4 + n;
        final int n6 = n5 + n;
        final int n7 = n6 + n;
        final int n8 = n7 + n;
        final int n9 = n8 + n;
        final int n10 = n9 + n;
        final int n11 = n10 + n;
        final int n12 = n11 + n;
        final int n13 = n12 + n;
        final int n14 = n13 + n;
        final int n15 = n14 + n;
        final int n16 = n15 + n;
        for (int i = 0, n17 = 0; i < n2; ++i, n17 += n << 4) {
            for (int j = 0, n18 = n17; j < n; ++j, ++n18) {
                final long n19 = array[n18 + n6];
                final long n20 = n19 & 0x1111111111111111L;
                final long n21 = array[n18 + n11] ^ (n19 & 0xEEEEEEEEEEEEEEEEL) >>> 1 ^ (n20 << 3) + n20;
                final long n22 = array[n18 + n12];
                final long n23 = (n22 & 0x8888888888888888L) >>> 3;
                final long n24 = array[n18 + n13] ^ (n22 & 0x7777777777777777L) << 1 ^ (n23 << 1) + n23;
                final long n25 = n21 & 0x1111111111111111L;
                final long n26 = array[n18 + n8] ^ (n21 & 0xEEEEEEEEEEEEEEEEL) >>> 1 ^ (n25 << 3) + n25;
                final long n27 = (n24 & 0x8888888888888888L) >>> 3;
                final long n28 = array[n18 + n7] ^ (n24 & 0x7777777777777777L) << 1 ^ (n27 << 1) + n27;
                final long n29 = n26 & 0x1111111111111111L;
                final long n30 = array[n18 + n15] ^ (n26 & 0xEEEEEEEEEEEEEEEEL) >>> 1 ^ (n29 << 3) + n29;
                final long n31 = (n28 & 0x8888888888888888L) >>> 3;
                final long n32 = array[n18 + n4] ^ (n28 & 0x7777777777777777L) << 1 ^ (n31 << 1) + n31;
                final long n33 = n30 & 0x1111111111111111L;
                final long n34 = array[n18 + n16] ^ (n30 & 0xEEEEEEEEEEEEEEEEL) >>> 1 ^ (n33 << 3) + n33;
                final long n35 = (n32 & 0x8888888888888888L) >>> 3;
                final long n36 = array[n18 + n9] ^ (n32 & 0x7777777777777777L) << 1 ^ (n35 << 1) + n35;
                final long n37 = n34 & 0x1111111111111111L;
                final long n38 = array[n18 + n14] ^ (n34 & 0xEEEEEEEEEEEEEEEEL) >>> 1 ^ (n37 << 3) + n37;
                final long n39 = (n36 & 0x8888888888888888L) >>> 3;
                final long n40 = array[n18 + n5] ^ (n36 & 0x7777777777777777L) << 1 ^ (n39 << 1) + n39;
                final long n41 = n38 & 0x1111111111111111L;
                final long n42 = array[n18 + n10] ^ (n38 & 0xEEEEEEEEEEEEEEEEL) >>> 1 ^ (n41 << 3) + n41;
                final long n43 = (n40 & 0x8888888888888888L) >>> 3;
                final long n44 = array[n18 + n3] ^ (n40 & 0x7777777777777777L) << 1 ^ (n43 << 1) + n43;
                final long n45 = n42 & 0x1111111111111111L;
                final long n46 = array[n18 + n] ^ (n42 & 0xEEEEEEEEEEEEEEEEL) >>> 1 ^ (n45 << 3) + n45;
                final long n47 = (n44 & 0x8888888888888888L) >>> 3;
                array2[(n17 >> 4) + j] = (n46 ^ (n44 & 0x7777777777777777L) << 1 ^ (n47 << 1) + n47);
            }
        }
    }
}
