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

package org.bouncycastle.math.raw;

import java.util.Random;
import org.bouncycastle.util.Integers;

public abstract class Mod
{
    private static final int M30 = 1073741823;
    private static final long M32L = 4294967295L;
    
    public static void checkedModOddInverse(final int[] array, final int[] array2, final int[] array3) {
        if (0 == modOddInverse(array, array2, array3)) {
            throw new ArithmeticException("Inverse does not exist.");
        }
    }
    
    public static void checkedModOddInverseVar(final int[] array, final int[] array2, final int[] array3) {
        if (!modOddInverseVar(array, array2, array3)) {
            throw new ArithmeticException("Inverse does not exist.");
        }
    }
    
    public static int inverse32(final int n) {
        final int n2 = n * (2 - n * n);
        final int n3 = n2 * (2 - n * n2);
        final int n4 = n3 * (2 - n * n3);
        return n4 * (2 - n * n4);
    }
    
    public static int modOddInverse(final int[] array, final int[] array2, final int[] array3) {
        final int length = array.length;
        final int n = (length << 5) - Integers.numberOfLeadingZeros(array[length - 1]);
        final int n2 = (n + 29) / 30;
        final int[] array4 = new int[4];
        final int[] array5 = new int[n2];
        final int[] array6 = new int[n2];
        final int[] array7 = new int[n2];
        final int[] array8 = new int[n2];
        final int[] array9 = new int[n2];
        array6[0] = 1;
        encode30(n, array2, array8);
        encode30(n, array, array9);
        System.arraycopy(array9, 0, array7, 0, n2);
        int hddivsteps30 = 0;
        final int inverse32 = inverse32(array9[0]);
        for (int maximumHDDivsteps = getMaximumHDDivsteps(n), i = 0; i < maximumHDDivsteps; i += 30) {
            hddivsteps30 = hddivsteps30(hddivsteps30, array7[0], array8[0], array4);
            updateDE30(n2, array5, array6, array4, inverse32, array9);
            updateFG30(n2, array7, array8, array4);
        }
        final int n3 = array7[n2 - 1] >> 31;
        cnegate30(n2, n3, array7);
        cnormalize30(n2, n3, array5, array9);
        decode30(n, array5, array3);
        return equalTo(n2, array7, 1) & equalTo(n2, array8, 0);
    }
    
    public static boolean modOddInverseVar(final int[] array, final int[] array2, final int[] array3) {
        final int length = array.length;
        final int n = (length << 5) - Integers.numberOfLeadingZeros(array[length - 1]);
        final int n2 = (n + 29) / 30;
        final int n3 = n - Nat.getBitLength(length, array2);
        final int[] array4 = new int[4];
        final int[] array5 = new int[n2];
        final int[] array6 = new int[n2];
        final int[] array7 = new int[n2];
        final int[] array8 = new int[n2];
        final int[] array9 = new int[n2];
        array6[0] = 1;
        encode30(n, array2, array8);
        encode30(n, array, array9);
        System.arraycopy(array9, 0, array7, 0, n2);
        int divsteps30Var = -n3;
        final int n4 = n2;
        int trimFG30 = n2;
        final int inverse32 = inverse32(array9[0]);
        final int maximumDivsteps = getMaximumDivsteps(n);
        int n5 = n3;
        while (!equalToVar(trimFG30, array8, 0)) {
            if (n5 >= maximumDivsteps) {
                return false;
            }
            n5 += 30;
            divsteps30Var = divsteps30Var(divsteps30Var, array7[0], array8[0], array4);
            updateDE30(n4, array5, array6, array4, inverse32, array9);
            updateFG30(trimFG30, array7, array8, array4);
            trimFG30 = trimFG30(trimFG30, array7, array8);
        }
        final int n6 = array7[trimFG30 - 1] >> 31;
        int n7 = array5[n4 - 1] >> 31;
        if (n7 < 0) {
            n7 = add30(n4, array5, array9);
        }
        if (n6 < 0) {
            n7 = negate30(n4, array5);
            negate30(trimFG30, array7);
        }
        if (!equalToVar(trimFG30, array7, 1)) {
            return false;
        }
        if (n7 < 0) {
            add30(n4, array5, array9);
        }
        decode30(n, array5, array3);
        return true;
    }
    
    public static int modOddIsCoprime(final int[] array, final int[] array2) {
        final int length = array.length;
        final int n = (length << 5) - Integers.numberOfLeadingZeros(array[length - 1]);
        final int n2 = (n + 29) / 30;
        final int[] array3 = new int[4];
        final int[] array4 = new int[n2];
        final int[] array5 = new int[n2];
        final int[] array6 = new int[n2];
        encode30(n, array2, array5);
        encode30(n, array, array6);
        System.arraycopy(array6, 0, array4, 0, n2);
        int hddivsteps30 = 0;
        for (int maximumHDDivsteps = getMaximumHDDivsteps(n), i = 0; i < maximumHDDivsteps; i += 30) {
            hddivsteps30 = hddivsteps30(hddivsteps30, array4[0], array5[0], array3);
            updateFG30(n2, array4, array5, array3);
        }
        cnegate30(n2, array4[n2 - 1] >> 31, array4);
        return equalTo(n2, array4, 1) & equalTo(n2, array5, 0);
    }
    
    public static boolean modOddIsCoprimeVar(final int[] array, final int[] array2) {
        final int length = array.length;
        final int n = (length << 5) - Integers.numberOfLeadingZeros(array[length - 1]);
        final int n2 = (n + 29) / 30;
        final int n3 = n - Nat.getBitLength(length, array2);
        final int[] array3 = new int[4];
        final int[] array4 = new int[n2];
        final int[] array5 = new int[n2];
        final int[] array6 = new int[n2];
        encode30(n, array2, array5);
        encode30(n, array, array6);
        System.arraycopy(array6, 0, array4, 0, n2);
        int divsteps30Var = -n3;
        int trimFG30 = n2;
        final int maximumDivsteps = getMaximumDivsteps(n);
        int n4 = n3;
        while (!equalToVar(trimFG30, array5, 0)) {
            if (n4 >= maximumDivsteps) {
                return false;
            }
            n4 += 30;
            divsteps30Var = divsteps30Var(divsteps30Var, array4[0], array5[0], array3);
            updateFG30(trimFG30, array4, array5, array3);
            trimFG30 = trimFG30(trimFG30, array4, array5);
        }
        if (array4[trimFG30 - 1] >> 31 < 0) {
            negate30(trimFG30, array4);
        }
        return equalToVar(trimFG30, array4, 1);
    }
    
    public static int[] random(final int[] array) {
        final int length = array.length;
        final Random random = new Random();
        final int[] create = Nat.create(length);
        final int n = array[length - 1];
        final int n2 = n | n >>> 1;
        final int n3 = n2 | n2 >>> 2;
        final int n4 = n3 | n3 >>> 4;
        final int n5 = n4 | n4 >>> 8;
        final int n6 = n5 | n5 >>> 16;
        do {
            for (int i = 0; i != length; ++i) {
                create[i] = random.nextInt();
            }
            final int[] array2 = create;
            final int n7 = length - 1;
            array2[n7] &= n6;
        } while (Nat.gte(length, create, array));
        return create;
    }
    
    private static int add30(final int n, final int[] array, final int[] array2) {
        int n2 = 0;
        final int n3 = n - 1;
        for (int i = 0; i < n3; ++i) {
            final int n4 = n2 + (array[i] + array2[i]);
            array[i] = (n4 & 0x3FFFFFFF);
            n2 = n4 >> 30;
        }
        return (array[n3] = n2 + (array[n3] + array2[n3])) >> 30;
    }
    
    private static void cnegate30(final int n, final int n2, final int[] array) {
        int n3 = 0;
        final int n4 = n - 1;
        for (int i = 0; i < n4; ++i) {
            final int n5 = n3 + ((array[i] ^ n2) - n2);
            array[i] = (n5 & 0x3FFFFFFF);
            n3 = n5 >> 30;
        }
        array[n4] = n3 + ((array[n4] ^ n2) - n2);
    }
    
    private static void cnormalize30(final int n, final int n2, final int[] array, final int[] array2) {
        final int n3 = n - 1;
        int n4 = 0;
        final int n5 = array[n3] >> 31;
        for (int i = 0; i < n3; ++i) {
            final int n6 = n4 + ((array[i] + (array2[i] & n5) ^ n2) - n2);
            array[i] = (n6 & 0x3FFFFFFF);
            n4 = n6 >> 30;
        }
        array[n3] = n4 + ((array[n3] + (array2[n3] & n5) ^ n2) - n2);
        int n7 = 0;
        final int n8 = array[n3] >> 31;
        for (int j = 0; j < n3; ++j) {
            final int n9 = n7 + (array[j] + (array2[j] & n8));
            array[j] = (n9 & 0x3FFFFFFF);
            n7 = n9 >> 30;
        }
        array[n3] = n7 + (array[n3] + (array2[n3] & n8));
    }
    
    private static void decode30(int i, final int[] array, final int[] array2) {
        int j = 0;
        long n = 0L;
        int n2 = 0;
        int n3 = 0;
        while (i > 0) {
            while (j < Math.min(32, i)) {
                n |= (long)array[n2++] << j;
                j += 30;
            }
            array2[n3++] = (int)n;
            n >>>= 32;
            j -= 32;
            i -= 32;
        }
    }
    
    private static int divsteps30Var(int n, final int n2, final int n3, final int[] array) {
        int n4 = 1;
        int n5 = 0;
        int n6 = 0;
        int n7 = 1;
        int n8 = n2;
        int n9 = n3;
        int n10 = 30;
        while (true) {
            final int numberOfTrailingZeros = Integers.numberOfTrailingZeros(n9 | -1 << n10);
            int n11 = n9 >> numberOfTrailingZeros;
            n4 <<= numberOfTrailingZeros;
            n5 <<= numberOfTrailingZeros;
            n -= numberOfTrailingZeros;
            n10 -= numberOfTrailingZeros;
            if (n10 <= 0) {
                break;
            }
            int n15;
            if (n <= 0) {
                n = 2 - n;
                final int n12 = n8;
                n8 = n11;
                n11 = -n12;
                final int n13 = n4;
                n4 = n6;
                n6 = -n13;
                final int n14 = n5;
                n5 = n7;
                n7 = -n14;
                n15 = (n8 * n11 * (n8 * n8 - 2) & (-1 >>> 32 - ((n > n10) ? n10 : n) & 0x3F));
            }
            else {
                n15 = ((n8 + ((n8 + 1 & 0x4) << 1)) * -n11 & (-1 >>> 32 - ((n > n10) ? n10 : n) & 0xF));
            }
            n9 = n11 + n8 * n15;
            n6 += n4 * n15;
            n7 += n5 * n15;
        }
        array[0] = n4;
        array[1] = n5;
        array[2] = n6;
        array[3] = n7;
        return n;
    }
    
    private static void encode30(int i, final int[] array, final int[] array2) {
        int n = 0;
        long n2 = 0L;
        int n3 = 0;
        int n4 = 0;
        while (i > 0) {
            if (n < Math.min(30, i)) {
                n2 |= ((long)array[n3++] & 0xFFFFFFFFL) << n;
                n += 32;
            }
            array2[n4++] = ((int)n2 & 0x3FFFFFFF);
            n2 >>>= 30;
            n -= 30;
            i -= 30;
        }
    }
    
    private static int equalTo(final int n, final int[] array, final int n2) {
        int n3 = array[0] ^ n2;
        for (int i = 1; i < n; ++i) {
            n3 |= array[i];
        }
        return (n3 >>> 1 | (n3 & 0x1)) - 1 >> 31;
    }
    
    private static boolean equalToVar(final int n, final int[] array, final int n2) {
        int n3 = array[0] ^ n2;
        if (n3 != 0) {
            return false;
        }
        for (int i = 1; i < n; ++i) {
            n3 |= array[i];
        }
        return n3 == 0;
    }
    
    private static int getMaximumDivsteps(final int n) {
        return (int)(188898L * n + ((n < 46) ? 308405 : 181188) >>> 16);
    }
    
    private static int getMaximumHDDivsteps(final int n) {
        return (int)(150964L * n + 99243L >>> 16);
    }
    
    private static int hddivsteps30(int n, final int n2, final int n3, final int[] array) {
        int n4 = 1073741824;
        int n5 = 0;
        int n6 = 0;
        int n7 = 1073741824;
        int n8 = n2;
        int n9 = n3;
        for (int i = 0; i < 30; ++i) {
            final int n10 = n >> 31;
            final int n11 = -(n9 & 0x1);
            final int n12 = n8 ^ n10;
            final int n13 = n4 ^ n10;
            final int n14 = n5 ^ n10;
            final int n15 = n9 - (n12 & n11);
            final int n16 = n6 - (n13 & n11);
            final int n17 = n7 - (n14 & n11);
            final int n18 = n11 & ~n10;
            n = (n ^ n18) + 1;
            n8 += (n15 & n18);
            n4 += (n16 & n18);
            n5 += (n17 & n18);
            n9 = n15 >> 1;
            n6 = n16 >> 1;
            n7 = n17 >> 1;
        }
        array[0] = n4;
        array[1] = n5;
        array[2] = n6;
        array[3] = n7;
        return n;
    }
    
    private static int negate30(final int n, final int[] array) {
        int n2 = 0;
        final int n3 = n - 1;
        for (int i = 0; i < n3; ++i) {
            final int n4 = n2 - array[i];
            array[i] = (n4 & 0x3FFFFFFF);
            n2 = n4 >> 30;
        }
        return (array[n3] = n2 - array[n3]) >> 30;
    }
    
    private static int trimFG30(int n, final int[] array, final int[] array2) {
        final int n2 = array[n - 1];
        final int n3 = array2[n - 1];
        if ((n - 2 >> 31 | (n2 ^ n2 >> 31) | (n3 ^ n3 >> 31)) == 0x0) {
            final int n4 = n - 2;
            array[n4] |= n2 << 30;
            final int n5 = n - 2;
            array2[n5] |= n3 << 30;
            --n;
        }
        return n;
    }
    
    private static void updateDE30(final int n, final int[] array, final int[] array2, final int[] array3, final int n2, final int[] array4) {
        final int n3 = array3[0];
        final int n4 = array3[1];
        final int n5 = array3[2];
        final int n6 = array3[3];
        final int n7 = array[n - 1] >> 31;
        final int n8 = array2[n - 1] >> 31;
        final int n9 = (n3 & n7) + (n4 & n8);
        final int n10 = (n5 & n7) + (n6 & n8);
        final int n11 = array4[0];
        final int n12 = array[0];
        final int n13 = array2[0];
        final long n14 = n3 * (long)n12 + n4 * (long)n13;
        final long n15 = n5 * (long)n12 + n6 * (long)n13;
        final int n16 = n9 - (n2 * (int)n14 + n9 & 0x3FFFFFFF);
        final int n17 = n10 - (n2 * (int)n15 + n10 & 0x3FFFFFFF);
        final long n18 = n14 + n11 * (long)n16;
        final long n19 = n15 + n11 * (long)n17;
        long n20 = n18 >> 30;
        long n21 = n19 >> 30;
        for (int i = 1; i < n; ++i) {
            final int n22 = array4[i];
            final int n23 = array[i];
            final int n24 = array2[i];
            final long n25 = n20 + (n3 * (long)n23 + n4 * (long)n24 + n22 * (long)n16);
            final long n26 = n21 + (n5 * (long)n23 + n6 * (long)n24 + n22 * (long)n17);
            array[i - 1] = ((int)n25 & 0x3FFFFFFF);
            n20 = n25 >> 30;
            array2[i - 1] = ((int)n26 & 0x3FFFFFFF);
            n21 = n26 >> 30;
        }
        array[n - 1] = (int)n20;
        array2[n - 1] = (int)n21;
    }
    
    private static void updateFG30(final int n, final int[] array, final int[] array2, final int[] array3) {
        final int n2 = array3[0];
        final int n3 = array3[1];
        final int n4 = array3[2];
        final int n5 = array3[3];
        final int n6 = array[0];
        final int n7 = array2[0];
        final long n8 = n2 * (long)n6 + n3 * (long)n7;
        final long n9 = n4 * (long)n6 + n5 * (long)n7;
        long n10 = n8 >> 30;
        long n11 = n9 >> 30;
        for (int i = 1; i < n; ++i) {
            final int n12 = array[i];
            final int n13 = array2[i];
            final long n14 = n10 + (n2 * (long)n12 + n3 * (long)n13);
            final long n15 = n11 + (n4 * (long)n12 + n5 * (long)n13);
            array[i - 1] = ((int)n14 & 0x3FFFFFFF);
            n10 = n14 >> 30;
            array2[i - 1] = ((int)n15 & 0x3FFFFFFF);
            n11 = n15 >> 30;
        }
        array[n - 1] = (int)n10;
        array2[n - 1] = (int)n11;
    }
}
