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

package com.google.crypto.tink.internal;

import java.util.Arrays;
import com.google.crypto.tink.annotations.Alpha;

@Alpha
public final class Field25519
{
    public static final int FIELD_LEN = 32;
    public static final int LIMB_CNT = 10;
    private static final long TWO_TO_25 = 33554432L;
    private static final long TWO_TO_26 = 67108864L;
    private static final int[] expandStart;
    private static final int[] expandShift;
    private static final int[] mask;
    private static final int[] shift;
    
    static void sum(final long[] output, final long[] in1, final long[] in2) {
        for (int i = 0; i < 10; ++i) {
            output[i] = in1[i] + in2[i];
        }
    }
    
    static void sum(final long[] output, final long[] in) {
        sum(output, output, in);
    }
    
    static void sub(final long[] output, final long[] in1, final long[] in2) {
        for (int i = 0; i < 10; ++i) {
            output[i] = in1[i] - in2[i];
        }
    }
    
    static void sub(final long[] output, final long[] in) {
        sub(output, in, output);
    }
    
    static void scalarProduct(final long[] output, final long[] in, final long scalar) {
        for (int i = 0; i < 10; ++i) {
            output[i] = in[i] * scalar;
        }
    }
    
    static void product(final long[] out, final long[] in2, final long[] in) {
        out[0] = in2[0] * in[0];
        out[1] = in2[0] * in[1] + in2[1] * in[0];
        out[2] = 2L * in2[1] * in[1] + in2[0] * in[2] + in2[2] * in[0];
        out[3] = in2[1] * in[2] + in2[2] * in[1] + in2[0] * in[3] + in2[3] * in[0];
        out[4] = in2[2] * in[2] + 2L * (in2[1] * in[3] + in2[3] * in[1]) + in2[0] * in[4] + in2[4] * in[0];
        out[5] = in2[2] * in[3] + in2[3] * in[2] + in2[1] * in[4] + in2[4] * in[1] + in2[0] * in[5] + in2[5] * in[0];
        out[6] = 2L * (in2[3] * in[3] + in2[1] * in[5] + in2[5] * in[1]) + in2[2] * in[4] + in2[4] * in[2] + in2[0] * in[6] + in2[6] * in[0];
        out[7] = in2[3] * in[4] + in2[4] * in[3] + in2[2] * in[5] + in2[5] * in[2] + in2[1] * in[6] + in2[6] * in[1] + in2[0] * in[7] + in2[7] * in[0];
        out[8] = in2[4] * in[4] + 2L * (in2[3] * in[5] + in2[5] * in[3] + in2[1] * in[7] + in2[7] * in[1]) + in2[2] * in[6] + in2[6] * in[2] + in2[0] * in[8] + in2[8] * in[0];
        out[9] = in2[4] * in[5] + in2[5] * in[4] + in2[3] * in[6] + in2[6] * in[3] + in2[2] * in[7] + in2[7] * in[2] + in2[1] * in[8] + in2[8] * in[1] + in2[0] * in[9] + in2[9] * in[0];
        out[10] = 2L * (in2[5] * in[5] + in2[3] * in[7] + in2[7] * in[3] + in2[1] * in[9] + in2[9] * in[1]) + in2[4] * in[6] + in2[6] * in[4] + in2[2] * in[8] + in2[8] * in[2];
        out[11] = in2[5] * in[6] + in2[6] * in[5] + in2[4] * in[7] + in2[7] * in[4] + in2[3] * in[8] + in2[8] * in[3] + in2[2] * in[9] + in2[9] * in[2];
        out[12] = in2[6] * in[6] + 2L * (in2[5] * in[7] + in2[7] * in[5] + in2[3] * in[9] + in2[9] * in[3]) + in2[4] * in[8] + in2[8] * in[4];
        out[13] = in2[6] * in[7] + in2[7] * in[6] + in2[5] * in[8] + in2[8] * in[5] + in2[4] * in[9] + in2[9] * in[4];
        out[14] = 2L * (in2[7] * in[7] + in2[5] * in[9] + in2[9] * in[5]) + in2[6] * in[8] + in2[8] * in[6];
        out[15] = in2[7] * in[8] + in2[8] * in[7] + in2[6] * in[9] + in2[9] * in[6];
        out[16] = in2[8] * in[8] + 2L * (in2[7] * in[9] + in2[9] * in[7]);
        out[17] = in2[8] * in[9] + in2[9] * in[8];
        out[18] = 2L * in2[9] * in[9];
    }
    
    static void reduce(final long[] input, final long[] output) {
        long[] tmp;
        if (input.length == 19) {
            tmp = input;
        }
        else {
            tmp = new long[19];
            System.arraycopy(input, 0, tmp, 0, input.length);
        }
        reduceSizeByModularReduction(tmp);
        reduceCoefficients(tmp);
        System.arraycopy(tmp, 0, output, 0, 10);
    }
    
    static void reduceSizeByModularReduction(final long[] output) {
        final int n = 8;
        output[n] += output[18] << 4;
        final int n2 = 8;
        output[n2] += output[18] << 1;
        final int n3 = 8;
        output[n3] += output[18];
        final int n4 = 7;
        output[n4] += output[17] << 4;
        final int n5 = 7;
        output[n5] += output[17] << 1;
        final int n6 = 7;
        output[n6] += output[17];
        final int n7 = 6;
        output[n7] += output[16] << 4;
        final int n8 = 6;
        output[n8] += output[16] << 1;
        final int n9 = 6;
        output[n9] += output[16];
        final int n10 = 5;
        output[n10] += output[15] << 4;
        final int n11 = 5;
        output[n11] += output[15] << 1;
        final int n12 = 5;
        output[n12] += output[15];
        final int n13 = 4;
        output[n13] += output[14] << 4;
        final int n14 = 4;
        output[n14] += output[14] << 1;
        final int n15 = 4;
        output[n15] += output[14];
        final int n16 = 3;
        output[n16] += output[13] << 4;
        final int n17 = 3;
        output[n17] += output[13] << 1;
        final int n18 = 3;
        output[n18] += output[13];
        final int n19 = 2;
        output[n19] += output[12] << 4;
        final int n20 = 2;
        output[n20] += output[12] << 1;
        final int n21 = 2;
        output[n21] += output[12];
        final int n22 = 1;
        output[n22] += output[11] << 4;
        final int n23 = 1;
        output[n23] += output[11] << 1;
        final int n24 = 1;
        output[n24] += output[11];
        final int n25 = 0;
        output[n25] += output[10] << 4;
        final int n26 = 0;
        output[n26] += output[10] << 1;
        final int n27 = 0;
        output[n27] += output[10];
    }
    
    static void reduceCoefficients(final long[] output) {
        output[10] = 0L;
        for (int i = 0; i < 10; i += 2) {
            long over = output[i] / 67108864L;
            final int n = i;
            output[n] -= over << 26;
            final int n2 = i + 1;
            output[n2] += over;
            over = output[i + 1] / 33554432L;
            final int n3 = i + 1;
            output[n3] -= over << 25;
            final int n4 = i + 2;
            output[n4] += over;
        }
        final int n5 = 0;
        output[n5] += output[10] << 4;
        final int n6 = 0;
        output[n6] += output[10] << 1;
        final int n7 = 0;
        output[n7] += output[10];
        output[10] = 0L;
        final long over2 = output[0] / 67108864L;
        final int n8 = 0;
        output[n8] -= over2 << 26;
        final int n9 = 1;
        output[n9] += over2;
    }
    
    static void mult(final long[] output, final long[] in, final long[] in2) {
        final long[] t = new long[19];
        product(t, in, in2);
        reduce(t, output);
    }
    
    private static void squareInner(final long[] out, final long[] in) {
        out[0] = in[0] * in[0];
        out[1] = 2L * in[0] * in[1];
        out[2] = 2L * (in[1] * in[1] + in[0] * in[2]);
        out[3] = 2L * (in[1] * in[2] + in[0] * in[3]);
        out[4] = in[2] * in[2] + 4L * in[1] * in[3] + 2L * in[0] * in[4];
        out[5] = 2L * (in[2] * in[3] + in[1] * in[4] + in[0] * in[5]);
        out[6] = 2L * (in[3] * in[3] + in[2] * in[4] + in[0] * in[6] + 2L * in[1] * in[5]);
        out[7] = 2L * (in[3] * in[4] + in[2] * in[5] + in[1] * in[6] + in[0] * in[7]);
        out[8] = in[4] * in[4] + 2L * (in[2] * in[6] + in[0] * in[8] + 2L * (in[1] * in[7] + in[3] * in[5]));
        out[9] = 2L * (in[4] * in[5] + in[3] * in[6] + in[2] * in[7] + in[1] * in[8] + in[0] * in[9]);
        out[10] = 2L * (in[5] * in[5] + in[4] * in[6] + in[2] * in[8] + 2L * (in[3] * in[7] + in[1] * in[9]));
        out[11] = 2L * (in[5] * in[6] + in[4] * in[7] + in[3] * in[8] + in[2] * in[9]);
        out[12] = in[6] * in[6] + 2L * (in[4] * in[8] + 2L * (in[5] * in[7] + in[3] * in[9]));
        out[13] = 2L * (in[6] * in[7] + in[5] * in[8] + in[4] * in[9]);
        out[14] = 2L * (in[7] * in[7] + in[6] * in[8] + 2L * in[5] * in[9]);
        out[15] = 2L * (in[7] * in[8] + in[6] * in[9]);
        out[16] = in[8] * in[8] + 4L * in[7] * in[9];
        out[17] = 2L * in[8] * in[9];
        out[18] = 2L * in[9] * in[9];
    }
    
    static void square(final long[] output, final long[] in) {
        final long[] t = new long[19];
        squareInner(t, in);
        reduce(t, output);
    }
    
    static long[] expand(final byte[] input) {
        final long[] output = new long[10];
        for (int i = 0; i < 10; ++i) {
            output[i] = (((long)(input[Field25519.expandStart[i]] & 0xFF) | (long)(input[Field25519.expandStart[i] + 1] & 0xFF) << 8 | (long)(input[Field25519.expandStart[i] + 2] & 0xFF) << 16 | (long)(input[Field25519.expandStart[i] + 3] & 0xFF) << 24) >> Field25519.expandShift[i] & (long)Field25519.mask[i & 0x1]);
        }
        return output;
    }
    
    public static byte[] contract(final long[] inputLimbs) {
        final long[] input = Arrays.copyOf(inputLimbs, 10);
        for (int j = 0; j < 2; ++j) {
            for (int i = 0; i < 9; ++i) {
                final int carry = -(int)((input[i] & input[i] >> 31) >> Field25519.shift[i & 0x1]);
                input[i] += carry << Field25519.shift[i & 0x1];
                final long[] array = input;
                final int n = i + 1;
                array[n] -= carry;
            }
            final int carry2 = -(int)((input[9] & input[9] >> 31) >> 25);
            final long[] array2 = input;
            final int n2 = 9;
            array2[n2] += carry2 << 25;
            final long[] array3 = input;
            final int n3 = 0;
            array3[n3] -= carry2 * 19L;
        }
        int carry3 = -(int)((input[0] & input[0] >> 31) >> 26);
        final long[] array4 = input;
        final int n4 = 0;
        array4[n4] += carry3 << 26;
        final long[] array5 = input;
        final int n5 = 1;
        array5[n5] -= carry3;
        for (int j = 0; j < 2; ++j) {
            for (int i = 0; i < 9; ++i) {
                final int carry = (int)(input[i] >> Field25519.shift[i & 0x1]);
                final long[] array6 = input;
                final int n6 = i;
                array6[n6] &= Field25519.mask[i & 0x1];
                final long[] array7 = input;
                final int n7 = i + 1;
                array7[n7] += carry;
            }
        }
        carry3 = (int)(input[9] >> 25);
        final long[] array8 = input;
        final int n8 = 9;
        array8[n8] &= 0x1FFFFFFL;
        final long[] array9 = input;
        final int n9 = 0;
        array9[n9] += 19L * carry3;
        int mask = gte((int)input[0], 67108845);
        for (int i = 1; i < 10; ++i) {
            mask &= eq((int)input[i], Field25519.mask[i & 0x1]);
        }
        final long[] array10 = input;
        final int n10 = 0;
        array10[n10] -= (mask & 0x3FFFFED);
        final long[] array11 = input;
        final int n11 = 1;
        array11[n11] -= (mask & 0x1FFFFFF);
        for (int i = 2; i < 10; i += 2) {
            final long[] array12 = input;
            final int n12 = i;
            array12[n12] -= (mask & 0x3FFFFFF);
            final long[] array13 = input;
            final int n13 = i + 1;
            array13[n13] -= (mask & 0x1FFFFFF);
        }
        for (int i = 0; i < 10; ++i) {
            final long[] array14 = input;
            final int n14 = i;
            array14[n14] <<= Field25519.expandShift[i];
        }
        final byte[] output = new byte[32];
        for (int k = 0; k < 10; ++k) {
            final byte[] array15 = output;
            final int n15 = Field25519.expandStart[k];
            array15[n15] = (byte)((long)array15[n15] | (input[k] & 0xFFL));
            final byte[] array16 = output;
            final int n16 = Field25519.expandStart[k] + 1;
            array16[n16] = (byte)((long)array16[n16] | (input[k] >> 8 & 0xFFL));
            final byte[] array17 = output;
            final int n17 = Field25519.expandStart[k] + 2;
            array17[n17] = (byte)((long)array17[n17] | (input[k] >> 16 & 0xFFL));
            final byte[] array18 = output;
            final int n18 = Field25519.expandStart[k] + 3;
            array18[n18] = (byte)((long)array18[n18] | (input[k] >> 24 & 0xFFL));
        }
        return output;
    }
    
    static void inverse(final long[] out, final long[] z) {
        final long[] z2 = new long[10];
        final long[] z3 = new long[10];
        final long[] z4 = new long[10];
        final long[] z2To5Minus1 = new long[10];
        final long[] z2To10Minus1 = new long[10];
        final long[] z2To20Minus1 = new long[10];
        final long[] z2To50Minus1 = new long[10];
        final long[] z2To100Minus1 = new long[10];
        final long[] t0 = new long[10];
        final long[] t2 = new long[10];
        square(z2, z);
        square(t2, z2);
        square(t0, t2);
        mult(z3, t0, z);
        mult(z4, z3, z2);
        square(t0, z4);
        mult(z2To5Minus1, t0, z3);
        square(t0, z2To5Minus1);
        square(t2, t0);
        square(t0, t2);
        square(t2, t0);
        square(t0, t2);
        mult(z2To10Minus1, t0, z2To5Minus1);
        square(t0, z2To10Minus1);
        square(t2, t0);
        for (int i = 2; i < 10; i += 2) {
            square(t0, t2);
            square(t2, t0);
        }
        mult(z2To20Minus1, t2, z2To10Minus1);
        square(t0, z2To20Minus1);
        square(t2, t0);
        for (int i = 2; i < 20; i += 2) {
            square(t0, t2);
            square(t2, t0);
        }
        mult(t0, t2, z2To20Minus1);
        square(t2, t0);
        square(t0, t2);
        for (int i = 2; i < 10; i += 2) {
            square(t2, t0);
            square(t0, t2);
        }
        mult(z2To50Minus1, t0, z2To10Minus1);
        square(t0, z2To50Minus1);
        square(t2, t0);
        for (int i = 2; i < 50; i += 2) {
            square(t0, t2);
            square(t2, t0);
        }
        mult(z2To100Minus1, t2, z2To50Minus1);
        square(t2, z2To100Minus1);
        square(t0, t2);
        for (int i = 2; i < 100; i += 2) {
            square(t2, t0);
            square(t0, t2);
        }
        mult(t2, t0, z2To100Minus1);
        square(t0, t2);
        square(t2, t0);
        for (int i = 2; i < 50; i += 2) {
            square(t0, t2);
            square(t2, t0);
        }
        mult(t0, t2, z2To50Minus1);
        square(t2, t0);
        square(t0, t2);
        square(t2, t0);
        square(t0, t2);
        square(t2, t0);
        mult(out, t2, z4);
    }
    
    private static int eq(int a, final int b) {
        a = ~(a ^ b);
        a &= a << 16;
        a &= a << 8;
        a &= a << 4;
        a &= a << 2;
        a &= a << 1;
        return a >> 31;
    }
    
    private static int gte(int a, final int b) {
        a -= b;
        return ~(a >> 31);
    }
    
    private Field25519() {
    }
    
    static {
        expandStart = new int[] { 0, 3, 6, 9, 12, 16, 19, 22, 25, 28 };
        expandShift = new int[] { 0, 2, 3, 5, 6, 0, 1, 3, 4, 6 };
        mask = new int[] { 67108863, 33554431 };
        shift = new int[] { 26, 25 };
    }
}
