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

package org.bouncycastle.pqc.crypto.bike;

import org.bouncycastle.math.raw.Interleave;
import org.bouncycastle.math.raw.Mod;
import org.bouncycastle.math.raw.Nat;
import org.bouncycastle.util.Integers;
import org.bouncycastle.util.Pack;
import java.util.HashMap;
import java.util.Map;

class BIKERing
{
    private static final int PERMUTATION_CUTOFF = 64;
    private final int bits;
    private final int size;
    private final int sizeExt;
    private final Map<Integer, Integer> halfPowers;
    
    BIKERing(final int bits) {
        this.halfPowers = new HashMap<Integer, Integer>();
        if ((bits & 0xFFFF0001) != 0x1) {
            throw new IllegalArgumentException();
        }
        this.bits = bits;
        this.size = bits + 63 >>> 6;
        this.sizeExt = this.size * 2;
        generateHalfPowersInv(this.halfPowers, bits);
    }
    
    void add(final long[] array, final long[] array2, final long[] array3) {
        for (int i = 0; i < this.size; ++i) {
            array3[i] = (array[i] ^ array2[i]);
        }
    }
    
    void addTo(final long[] array, final long[] array2) {
        for (int i = 0; i < this.size; ++i) {
            final int n = i;
            array2[n] ^= array[i];
        }
    }
    
    void copy(final long[] array, final long[] array2) {
        for (int i = 0; i < this.size; ++i) {
            array2[i] = array[i];
        }
    }
    
    long[] create() {
        return new long[this.size];
    }
    
    long[] createExt() {
        return new long[this.sizeExt];
    }
    
    void decodeBytes(final byte[] array, final long[] array2) {
        final int n = this.bits & 0x3F;
        Pack.littleEndianToLong(array, 0, array2, 0, this.size - 1);
        final byte[] array3 = new byte[8];
        System.arraycopy(array, this.size - 1 << 3, array3, 0, n + 7 >>> 3);
        array2[this.size - 1] = Pack.littleEndianToLong(array3, 0);
    }
    
    byte[] encodeBitsTransposed(final long[] array) {
        final byte[] array2 = new byte[this.bits];
        array2[0] = (byte)(array[0] & 0x1L);
        for (int i = 1; i < this.bits; ++i) {
            array2[this.bits - i] = (byte)(array[i >>> 6] >>> (i & 0x3F) & 0x1L);
        }
        return array2;
    }
    
    void encodeBytes(final long[] array, final byte[] array2) {
        final int n = this.bits & 0x3F;
        Pack.longToLittleEndian(array, 0, this.size - 1, array2, 0);
        final byte[] array3 = new byte[8];
        Pack.longToLittleEndian(array[this.size - 1], array3, 0);
        System.arraycopy(array3, 0, array2, this.size - 1 << 3, n + 7 >>> 3);
    }
    
    void inv(final long[] array, final long[] array2) {
        final long[] create = this.create();
        final long[] create2 = this.create();
        final long[] create3 = this.create();
        this.copy(array, create);
        this.copy(array, create3);
        final int n = this.bits - 2;
        for (int n2 = 32 - Integers.numberOfLeadingZeros(n), i = 1; i < n2; ++i) {
            this.squareN(create, 1 << i - 1, create2);
            this.multiply(create, create2, create);
            if ((n & 1 << i) != 0x0) {
                this.squareN(create, n & (1 << i) - 1, create2);
                this.multiply(create3, create2, create3);
            }
        }
        this.square(create3, array2);
    }
    
    void multiply(final long[] array, final long[] array2, final long[] array3) {
        final long[] ext = this.createExt();
        this.implMultiplyAcc(array, array2, ext);
        this.reduce(ext, array3);
    }
    
    void reduce(final long[] array, final long[] array2) {
        final int n = 64 - (this.bits & 0x3F);
        final long n2 = -1L >>> n;
        Nat.shiftUpBits64(this.size, array, this.size, n, array[this.size - 1], array2, 0);
        this.addTo(array, array2);
        final int n3 = this.size - 1;
        array2[n3] &= n2;
    }
    
    int getSize() {
        return this.size;
    }
    
    int getSizeExt() {
        return this.sizeExt;
    }
    
    void square(final long[] array, final long[] array2) {
        final long[] ext = this.createExt();
        this.implSquare(array, ext);
        this.reduce(ext, array2);
    }
    
    void squareN(final long[] array, int n, final long[] array2) {
        if (n >= 64) {
            this.implPermute(array, n, array2);
            return;
        }
        final long[] ext = this.createExt();
        this.implSquare(array, ext);
        this.reduce(ext, array2);
        while (--n > 0) {
            this.implSquare(array2, ext);
            this.reduce(ext, array2);
        }
    }
    
    private static int implModAdd(final int n, final int n2, final int n3) {
        final int n4 = n2 + n3 - n;
        return n4 + (n4 >> 31 & n);
    }
    
    protected void implMultiplyAcc(final long[] array, final long[] array2, final long[] array3) {
        final long[] array4 = new long[16];
        for (int i = 0; i < this.size; ++i) {
            implMulwAcc(array4, array[i], array2[i], array3, i << 1);
        }
        long n = array3[0];
        long n2 = array3[1];
        for (int j = 1; j < this.size; ++j) {
            n ^= array3[j << 1];
            array3[j] = (n ^ n2);
            n2 ^= array3[(j << 1) + 1];
        }
        final long n3 = n ^ n2;
        for (int k = 0; k < this.size; ++k) {
            array3[this.size + k] = (array3[k] ^ n3);
        }
        for (int a = this.size - 1, l = 1; l < a * 2; ++l) {
            for (int min = Math.min(a, l), n4 = l - min; n4 < min; ++n4, --min) {
                implMulwAcc(array4, array[n4] ^ array[min], array2[n4] ^ array2[min], array3, l);
            }
        }
    }
    
    private void implPermute(final long[] array, final int n, final long[] array2) {
        final int bits = this.bits;
        final int intValue = this.halfPowers.get(Integers.valueOf(n));
        final int implModAdd = implModAdd(bits, intValue, intValue);
        final int implModAdd2 = implModAdd(bits, implModAdd, implModAdd);
        final int implModAdd3 = implModAdd(bits, implModAdd2, implModAdd2);
        int implModAdd4 = bits - implModAdd3;
        int n2 = implModAdd(bits, implModAdd4, intValue);
        int n3 = implModAdd(bits, implModAdd4, implModAdd);
        int n4 = implModAdd(bits, n2, implModAdd);
        int n5 = implModAdd(bits, implModAdd4, implModAdd2);
        int n6 = implModAdd(bits, n2, implModAdd2);
        int n7 = implModAdd(bits, n3, implModAdd2);
        int n8 = implModAdd(bits, n4, implModAdd2);
        for (int i = 0; i < this.size; ++i) {
            long n9 = 0L;
            for (int j = 0; j < 64; j += 8) {
                implModAdd4 = implModAdd(bits, implModAdd4, implModAdd3);
                n2 = implModAdd(bits, n2, implModAdd3);
                n3 = implModAdd(bits, n3, implModAdd3);
                n4 = implModAdd(bits, n4, implModAdd3);
                n5 = implModAdd(bits, n5, implModAdd3);
                n6 = implModAdd(bits, n6, implModAdd3);
                n7 = implModAdd(bits, n7, implModAdd3);
                n8 = implModAdd(bits, n8, implModAdd3);
                n9 = (n9 | (array[implModAdd4 >>> 6] >>> implModAdd4 & 0x1L) << j + 0 | (array[n2 >>> 6] >>> n2 & 0x1L) << j + 1 | (array[n3 >>> 6] >>> n3 & 0x1L) << j + 2 | (array[n4 >>> 6] >>> n4 & 0x1L) << j + 3 | (array[n5 >>> 6] >>> n5 & 0x1L) << j + 4 | (array[n6 >>> 6] >>> n6 & 0x1L) << j + 5 | (array[n7 >>> 6] >>> n7 & 0x1L) << j + 6 | (array[n8 >>> 6] >>> n8 & 0x1L) << j + 7);
            }
            array2[i] = n9;
        }
        final int n10 = this.size - 1;
        array2[n10] &= -1L >>> -bits;
    }
    
    private static int generateHalfPower(final int n, final int n2, final int n3) {
        int n4 = 1;
        int i;
        for (i = n3; i >= 32; i -= 32) {
            n4 = (int)(((long)(n2 * n4) & 0xFFFFFFFFL) * n + n4 >>> 32);
        }
        if (i > 0) {
            n4 = (int)(((long)(n2 * n4 & -1 >>> -i) & 0xFFFFFFFFL) * n + n4 >>> i);
        }
        return n4;
    }
    
    private static void generateHalfPowersInv(final Map<Integer, Integer> map, final int n) {
        final int n2 = n - 2;
        final int n3 = 32 - Integers.numberOfLeadingZeros(n2);
        final int inverse32 = Mod.inverse32(-n);
        for (int i = 1; i < n3; ++i) {
            final int n4 = 1 << i - 1;
            if (n4 >= 64 && !map.containsKey(Integers.valueOf(n4))) {
                map.put(Integers.valueOf(n4), Integers.valueOf(generateHalfPower(n, inverse32, n4)));
            }
            if ((n2 & 1 << i) != 0x0) {
                final int n5 = n2 & (1 << i) - 1;
                if (n5 >= 64 && !map.containsKey(Integers.valueOf(n5))) {
                    map.put(Integers.valueOf(n5), Integers.valueOf(generateHalfPower(n, inverse32, n5)));
                }
            }
        }
    }
    
    private static void implMulwAcc(final long[] array, long n, final long n2, final long[] array2, final int n3) {
        array[1] = n2;
        for (int i = 2; i < 16; i += 2) {
            array[i] = array[i >>> 1] << 1;
            array[i + 1] = (array[i] ^ n2);
        }
        final int n4 = (int)n;
        long n5 = 0L;
        long n6 = array[n4 & 0xF] ^ array[n4 >>> 4 & 0xF] << 4;
        int j = 56;
        do {
            final int n7 = (int)(n >>> j);
            final long n8 = array[n7 & 0xF] ^ array[n7 >>> 4 & 0xF] << 4;
            n6 ^= n8 << j;
            n5 ^= n8 >>> -j;
            j -= 8;
        } while (j > 0);
        for (int k = 0; k < 7; ++k) {
            n = (n & 0xFEFEFEFEFEFEFEFEL) >>> 1;
            n5 ^= (n & n2 << k >> 63);
        }
        array2[n3] ^= n6;
        final int n9 = n3 + 1;
        array2[n9] ^= n5;
    }
    
    private void implSquare(final long[] array, final long[] array2) {
        Interleave.expand64To128(array, 0, this.size, array2, 0);
    }
}
