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

package org.bouncycastle.math.ec;

import org.bouncycastle.math.raw.Nat;
import org.bouncycastle.math.ec.endo.EndoUtil;
import org.bouncycastle.math.ec.endo.ECEndomorphism;
import org.bouncycastle.math.ec.endo.GLVEndomorphism;
import java.math.BigInteger;
import org.bouncycastle.math.field.PolynomialExtensionField;
import org.bouncycastle.math.field.FiniteField;

public class ECAlgorithms
{
    public static boolean isF2mCurve(final ECCurve ecCurve) {
        return isF2mField(ecCurve.getField());
    }
    
    public static boolean isF2mField(final FiniteField finiteField) {
        return finiteField.getDimension() > 1 && finiteField.getCharacteristic().equals(ECConstants.TWO) && finiteField instanceof PolynomialExtensionField;
    }
    
    public static boolean isFpCurve(final ECCurve ecCurve) {
        return isFpField(ecCurve.getField());
    }
    
    public static boolean isFpField(final FiniteField finiteField) {
        return finiteField.getDimension() == 1;
    }
    
    public static ECPoint sumOfMultiplies(final ECPoint[] array, final BigInteger[] array2) {
        if (array == null || array2 == null || array.length != array2.length || array.length < 1) {
            throw new IllegalArgumentException("point and scalar arrays should be non-null, and of equal, non-zero, length");
        }
        final int length = array.length;
        switch (length) {
            case 1: {
                return array[0].multiply(array2[0]);
            }
            case 2: {
                return sumOfTwoMultiplies(array[0], array2[0], array[1], array2[1]);
            }
            default: {
                final ECPoint ecPoint = array[0];
                final ECCurve curve = ecPoint.getCurve();
                final ECPoint[] array3 = new ECPoint[length];
                array3[0] = ecPoint;
                for (int i = 1; i < length; ++i) {
                    array3[i] = importPoint(curve, array[i]);
                }
                final ECEndomorphism endomorphism = curve.getEndomorphism();
                if (endomorphism instanceof GLVEndomorphism) {
                    return implCheckResult(implSumOfMultipliesGLV(array3, array2, (GLVEndomorphism)endomorphism));
                }
                return implCheckResult(implSumOfMultiplies(array3, array2));
            }
        }
    }
    
    public static ECPoint sumOfTwoMultiplies(final ECPoint ecPoint, final BigInteger bigInteger, ECPoint importPoint, final BigInteger bigInteger2) {
        final ECCurve curve = ecPoint.getCurve();
        importPoint = importPoint(curve, importPoint);
        if (curve instanceof ECCurve.AbstractF2m && ((ECCurve.AbstractF2m)curve).isKoblitz()) {
            return implCheckResult(ecPoint.multiply(bigInteger).add(importPoint.multiply(bigInteger2)));
        }
        final ECEndomorphism endomorphism = curve.getEndomorphism();
        if (endomorphism instanceof GLVEndomorphism) {
            return implCheckResult(implSumOfMultipliesGLV(new ECPoint[] { ecPoint, importPoint }, new BigInteger[] { bigInteger, bigInteger2 }, (GLVEndomorphism)endomorphism));
        }
        return implCheckResult(implShamirsTrickWNaf(ecPoint, bigInteger, importPoint, bigInteger2));
    }
    
    public static ECPoint shamirsTrick(final ECPoint ecPoint, final BigInteger bigInteger, ECPoint importPoint, final BigInteger bigInteger2) {
        importPoint = importPoint(ecPoint.getCurve(), importPoint);
        return implCheckResult(implShamirsTrickJsf(ecPoint, bigInteger, importPoint, bigInteger2));
    }
    
    public static ECPoint importPoint(final ECCurve ecCurve, final ECPoint ecPoint) {
        if (!ecCurve.equals(ecPoint.getCurve())) {
            throw new IllegalArgumentException("Point must be on the same curve");
        }
        return ecCurve.importPoint(ecPoint);
    }
    
    public static void montgomeryTrick(final ECFieldElement[] array, final int n, final int n2) {
        montgomeryTrick(array, n, n2, null);
    }
    
    public static void montgomeryTrick(final ECFieldElement[] array, final int n, final int n2, final ECFieldElement ecFieldElement) {
        final ECFieldElement[] array2 = new ECFieldElement[n2];
        array2[0] = array[n];
        int i = 0;
        while (++i < n2) {
            array2[i] = array2[i - 1].multiply(array[n + i]);
        }
        --i;
        if (ecFieldElement != null) {
            array2[i] = array2[i].multiply(ecFieldElement);
        }
        ECFieldElement ecFieldElement2;
        int n3;
        ECFieldElement ecFieldElement3;
        for (ecFieldElement2 = array2[i].invert(); i > 0; n3 = n + i--, ecFieldElement3 = array[n3], array[n3] = array2[i].multiply(ecFieldElement2), ecFieldElement2 = ecFieldElement2.multiply(ecFieldElement3)) {}
        array[n] = ecFieldElement2;
    }
    
    public static ECPoint referenceMultiply(ECPoint twice, final BigInteger bigInteger) {
        final BigInteger abs = bigInteger.abs();
        ECPoint ecPoint = twice.getCurve().getInfinity();
        final int bitLength = abs.bitLength();
        if (bitLength > 0) {
            if (abs.testBit(0)) {
                ecPoint = twice;
            }
            for (int i = 1; i < bitLength; ++i) {
                twice = twice.twice();
                if (abs.testBit(i)) {
                    ecPoint = ecPoint.add(twice);
                }
            }
        }
        return (bigInteger.signum() < 0) ? ecPoint.negate() : ecPoint;
    }
    
    public static ECPoint validatePoint(final ECPoint ecPoint) {
        if (!ecPoint.isValid()) {
            throw new IllegalStateException("Invalid point");
        }
        return ecPoint;
    }
    
    public static ECPoint cleanPoint(final ECCurve ecCurve, final ECPoint ecPoint) {
        if (!ecCurve.equals(ecPoint.getCurve())) {
            throw new IllegalArgumentException("Point must be on the same curve");
        }
        return ecCurve.decodePoint(ecPoint.getEncoded(false));
    }
    
    static ECPoint implCheckResult(final ECPoint ecPoint) {
        if (!ecPoint.isValidPartial()) {
            throw new IllegalStateException("Invalid result");
        }
        return ecPoint;
    }
    
    static ECPoint implShamirsTrickJsf(final ECPoint ecPoint, final BigInteger bigInteger, final ECPoint ecPoint2, final BigInteger bigInteger2) {
        final ECCurve curve = ecPoint.getCurve();
        final ECPoint infinity = curve.getInfinity();
        final ECPoint[] array = { ecPoint2, ecPoint.subtract(ecPoint2), ecPoint, ecPoint.add(ecPoint2) };
        curve.normalizeAll(array);
        final ECPoint[] array2 = { array[3].negate(), array[2].negate(), array[1].negate(), array[0].negate(), infinity, array[0], array[1], array[2], array[3] };
        final byte[] generateJSF = WNafUtil.generateJSF(bigInteger, bigInteger2);
        ECPoint twicePlus = infinity;
        int length = generateJSF.length;
        while (--length >= 0) {
            final byte b = generateJSF[length];
            twicePlus = twicePlus.twicePlus(array2[4 + (b << 24 >> 28) * 3 + (b << 28 >> 28)]);
        }
        return twicePlus;
    }
    
    static ECPoint implShamirsTrickWNaf(final ECPoint ecPoint, final BigInteger bigInteger, final ECPoint ecPoint2, final BigInteger bigInteger2) {
        final boolean b = bigInteger.signum() < 0;
        final boolean b2 = bigInteger2.signum() < 0;
        final BigInteger abs = bigInteger.abs();
        final BigInteger abs2 = bigInteger2.abs();
        final int windowSize = WNafUtil.getWindowSize(abs.bitLength(), 8);
        final int windowSize2 = WNafUtil.getWindowSize(abs2.bitLength(), 8);
        final WNafPreCompInfo precompute = WNafUtil.precompute(ecPoint, windowSize, true);
        final WNafPreCompInfo precompute2 = WNafUtil.precompute(ecPoint2, windowSize2, true);
        final int combSize = FixedPointUtil.getCombSize(ecPoint.getCurve());
        if (!b && !b2 && bigInteger.bitLength() <= combSize && bigInteger2.bitLength() <= combSize && precompute.isPromoted() && precompute2.isPromoted()) {
            return implShamirsTrickFixedPoint(ecPoint, bigInteger, ecPoint2, bigInteger2);
        }
        return implShamirsTrickWNaf(b ? precompute.getPreCompNeg() : precompute.getPreComp(), b ? precompute.getPreComp() : precompute.getPreCompNeg(), WNafUtil.generateWindowNaf(Math.min(8, precompute.getWidth()), abs), b2 ? precompute2.getPreCompNeg() : precompute2.getPreComp(), b2 ? precompute2.getPreComp() : precompute2.getPreCompNeg(), WNafUtil.generateWindowNaf(Math.min(8, precompute2.getWidth()), abs2));
    }
    
    static ECPoint implShamirsTrickWNaf(final ECEndomorphism ecEndomorphism, final ECPoint ecPoint, BigInteger abs, BigInteger abs2) {
        final boolean b = abs.signum() < 0;
        final boolean b2 = abs2.signum() < 0;
        abs = abs.abs();
        abs2 = abs2.abs();
        final WNafPreCompInfo precompute = WNafUtil.precompute(ecPoint, WNafUtil.getWindowSize(Math.max(abs.bitLength(), abs2.bitLength()), 8), true);
        final WNafPreCompInfo precomputeWithPointMap = WNafUtil.precomputeWithPointMap(EndoUtil.mapPoint(ecEndomorphism, ecPoint), ecEndomorphism.getPointMap(), precompute, true);
        return implShamirsTrickWNaf(b ? precompute.getPreCompNeg() : precompute.getPreComp(), b ? precompute.getPreComp() : precompute.getPreCompNeg(), WNafUtil.generateWindowNaf(Math.min(8, precompute.getWidth()), abs), b2 ? precomputeWithPointMap.getPreCompNeg() : precomputeWithPointMap.getPreComp(), b2 ? precomputeWithPointMap.getPreComp() : precomputeWithPointMap.getPreCompNeg(), WNafUtil.generateWindowNaf(Math.min(8, precomputeWithPointMap.getWidth()), abs2));
    }
    
    private static ECPoint implShamirsTrickWNaf(final ECPoint[] array, final ECPoint[] array2, final byte[] array3, final ECPoint[] array4, final ECPoint[] array5, final byte[] array6) {
        final int max = Math.max(array3.length, array6.length);
        ECPoint ecPoint2;
        final ECPoint ecPoint = ecPoint2 = array[0].getCurve().getInfinity();
        int n = 0;
        for (int i = max - 1; i >= 0; --i) {
            final byte a = (byte)((i < array3.length) ? array3[i] : 0);
            final byte a2 = (byte)((i < array6.length) ? array6[i] : 0);
            if ((a | a2) == 0x0) {
                ++n;
            }
            else {
                ECPoint ecPoint3 = ecPoint;
                if (a != 0) {
                    ecPoint3 = ecPoint3.add(((a < 0) ? array2 : array)[Math.abs(a) >>> 1]);
                }
                if (a2 != 0) {
                    ecPoint3 = ecPoint3.add(((a2 < 0) ? array5 : array4)[Math.abs(a2) >>> 1]);
                }
                if (n > 0) {
                    ecPoint2 = ecPoint2.timesPow2(n);
                    n = 0;
                }
                ecPoint2 = ecPoint2.twicePlus(ecPoint3);
            }
        }
        if (n > 0) {
            ecPoint2 = ecPoint2.timesPow2(n);
        }
        return ecPoint2;
    }
    
    static ECPoint implSumOfMultiplies(final ECPoint[] array, final BigInteger[] array2) {
        final int length = array.length;
        final boolean[] array3 = new boolean[length];
        final WNafPreCompInfo[] array4 = new WNafPreCompInfo[length];
        final byte[][] array5 = new byte[length][];
        for (int i = 0; i < length; ++i) {
            final BigInteger bigInteger = array2[i];
            array3[i] = (bigInteger.signum() < 0);
            final BigInteger abs = bigInteger.abs();
            final WNafPreCompInfo precompute = WNafUtil.precompute(array[i], WNafUtil.getWindowSize(abs.bitLength(), 8), true);
            final int min = Math.min(8, precompute.getWidth());
            array4[i] = precompute;
            array5[i] = WNafUtil.generateWindowNaf(min, abs);
        }
        return implSumOfMultiplies(array3, array4, array5);
    }
    
    static ECPoint implSumOfMultipliesGLV(final ECPoint[] array, final BigInteger[] array2, final GLVEndomorphism glvEndomorphism) {
        final BigInteger order = array[0].getCurve().getOrder();
        final int length = array.length;
        final BigInteger[] array3 = new BigInteger[length << 1];
        int i = 0;
        int n = 0;
        while (i < length) {
            final BigInteger[] decomposeScalar = glvEndomorphism.decomposeScalar(array2[i].mod(order));
            array3[n++] = decomposeScalar[0];
            array3[n++] = decomposeScalar[1];
            ++i;
        }
        if (glvEndomorphism.hasEfficientPointMap()) {
            return implSumOfMultiplies(glvEndomorphism, array, array3);
        }
        final ECPoint[] array4 = new ECPoint[length << 1];
        int j = 0;
        int n2 = 0;
        while (j < length) {
            final ECPoint ecPoint = array[j];
            final ECPoint mapPoint = EndoUtil.mapPoint(glvEndomorphism, ecPoint);
            array4[n2++] = ecPoint;
            array4[n2++] = mapPoint;
            ++j;
        }
        return implSumOfMultiplies(array4, array3);
    }
    
    static ECPoint implSumOfMultiplies(final ECEndomorphism ecEndomorphism, final ECPoint[] array, final BigInteger[] array2) {
        final int length = array.length;
        final int n = length << 1;
        final boolean[] array3 = new boolean[n];
        final WNafPreCompInfo[] array4 = new WNafPreCompInfo[n];
        final byte[][] array5 = new byte[n][];
        final ECPointMap pointMap = ecEndomorphism.getPointMap();
        for (int i = 0; i < length; ++i) {
            final int n2 = i << 1;
            final int n3 = n2 + 1;
            final BigInteger bigInteger = array2[n2];
            array3[n2] = (bigInteger.signum() < 0);
            final BigInteger abs = bigInteger.abs();
            final BigInteger bigInteger2 = array2[n3];
            array3[n3] = (bigInteger2.signum() < 0);
            final BigInteger abs2 = bigInteger2.abs();
            final int windowSize = WNafUtil.getWindowSize(Math.max(abs.bitLength(), abs2.bitLength()), 8);
            final ECPoint ecPoint = array[i];
            final WNafPreCompInfo precompute = WNafUtil.precompute(ecPoint, windowSize, true);
            final WNafPreCompInfo precomputeWithPointMap = WNafUtil.precomputeWithPointMap(EndoUtil.mapPoint(ecEndomorphism, ecPoint), pointMap, precompute, true);
            final int min = Math.min(8, precompute.getWidth());
            final int min2 = Math.min(8, precomputeWithPointMap.getWidth());
            array4[n2] = precompute;
            array4[n3] = precomputeWithPointMap;
            array5[n2] = WNafUtil.generateWindowNaf(min, abs);
            array5[n3] = WNafUtil.generateWindowNaf(min2, abs2);
        }
        return implSumOfMultiplies(array3, array4, array5);
    }
    
    private static ECPoint implSumOfMultiplies(final boolean[] array, final WNafPreCompInfo[] array2, final byte[][] array3) {
        int max = 0;
        final int length = array3.length;
        for (int i = 0; i < length; ++i) {
            max = Math.max(max, array3[i].length);
        }
        ECPoint ecPoint2;
        final ECPoint ecPoint = ecPoint2 = array2[0].getPreComp()[0].getCurve().getInfinity();
        int n = 0;
        for (int j = max - 1; j >= 0; --j) {
            ECPoint add = ecPoint;
            for (int k = 0; k < length; ++k) {
                final byte[] array4 = array3[k];
                final byte a = (byte)((j < array4.length) ? array4[j] : 0);
                if (a != 0) {
                    final int abs = Math.abs(a);
                    final WNafPreCompInfo wNafPreCompInfo = array2[k];
                    add = add.add(((a < 0 == array[k]) ? wNafPreCompInfo.getPreComp() : wNafPreCompInfo.getPreCompNeg())[abs >>> 1]);
                }
            }
            if (add == ecPoint) {
                ++n;
            }
            else {
                if (n > 0) {
                    ecPoint2 = ecPoint2.timesPow2(n);
                    n = 0;
                }
                ecPoint2 = ecPoint2.twicePlus(add);
            }
        }
        if (n > 0) {
            ecPoint2 = ecPoint2.timesPow2(n);
        }
        return ecPoint2;
    }
    
    private static ECPoint implShamirsTrickFixedPoint(final ECPoint ecPoint, final BigInteger bigInteger, final ECPoint ecPoint2, final BigInteger bigInteger2) {
        final ECCurve curve = ecPoint.getCurve();
        final int combSize = FixedPointUtil.getCombSize(curve);
        if (bigInteger.bitLength() > combSize || bigInteger2.bitLength() > combSize) {
            throw new IllegalStateException("fixed-point comb doesn't support scalars larger than the curve order");
        }
        final FixedPointPreCompInfo precompute = FixedPointUtil.precompute(ecPoint);
        final FixedPointPreCompInfo precompute2 = FixedPointUtil.precompute(ecPoint2);
        final ECLookupTable lookupTable = precompute.getLookupTable();
        final ECLookupTable lookupTable2 = precompute2.getLookupTable();
        final int width = precompute.getWidth();
        if (width != precompute2.getWidth()) {
            final FixedPointCombMultiplier fixedPointCombMultiplier = new FixedPointCombMultiplier();
            return fixedPointCombMultiplier.multiply(ecPoint, bigInteger).add(fixedPointCombMultiplier.multiply(ecPoint2, bigInteger2));
        }
        final int n = width;
        final int n2 = (combSize + n - 1) / n;
        ECPoint ecPoint3 = curve.getInfinity();
        final int n3 = n2 * n;
        final int[] fromBigInteger = Nat.fromBigInteger(n3, bigInteger);
        final int[] fromBigInteger2 = Nat.fromBigInteger(n3, bigInteger2);
        final int n4 = n3 - 1;
        for (int i = 0; i < n2; ++i) {
            int n5 = 0;
            int n6 = 0;
            for (int j = n4 - i; j >= 0; j -= n2) {
                final int n7 = fromBigInteger[j >>> 5] >>> (j & 0x1F);
                n5 = ((n5 ^ n7 >>> 1) << 1 ^ n7);
                final int n8 = fromBigInteger2[j >>> 5] >>> (j & 0x1F);
                n6 = ((n6 ^ n8 >>> 1) << 1 ^ n8);
            }
            ecPoint3 = ecPoint3.twicePlus(lookupTable.lookupVar(n5).add(lookupTable2.lookupVar(n6)));
        }
        return ecPoint3.add(precompute.getOffset()).add(precompute2.getOffset());
    }
}
