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

package org.bouncycastle.math.ec;

import java.util.Collections;
import java.util.HashSet;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.math.Primes;
import java.util.Set;
import org.bouncycastle.math.raw.Nat;
import java.util.Random;
import org.bouncycastle.math.field.FiniteFields;
import org.bouncycastle.util.Properties;
import org.bouncycastle.util.Integers;
import org.bouncycastle.util.BigIntegers;
import java.util.Hashtable;
import org.bouncycastle.math.ec.endo.GLVEndomorphism;
import java.security.SecureRandom;
import org.bouncycastle.math.ec.endo.ECEndomorphism;
import java.math.BigInteger;
import org.bouncycastle.math.field.FiniteField;

public abstract class ECCurve
{
    public static final int COORD_AFFINE = 0;
    public static final int COORD_HOMOGENEOUS = 1;
    public static final int COORD_JACOBIAN = 2;
    public static final int COORD_JACOBIAN_CHUDNOVSKY = 3;
    public static final int COORD_JACOBIAN_MODIFIED = 4;
    public static final int COORD_LAMBDA_AFFINE = 5;
    public static final int COORD_LAMBDA_PROJECTIVE = 6;
    public static final int COORD_SKEWED = 7;
    protected FiniteField field;
    protected ECFieldElement a;
    protected ECFieldElement b;
    protected BigInteger order;
    protected BigInteger cofactor;
    protected int coord;
    protected ECEndomorphism endomorphism;
    protected ECMultiplier multiplier;
    
    public static int[] getAllCoordinateSystems() {
        return new int[] { 0, 1, 2, 3, 4, 5, 6, 7 };
    }
    
    protected ECCurve(final FiniteField field) {
        this.coord = 0;
        this.endomorphism = null;
        this.multiplier = null;
        this.field = field;
    }
    
    public abstract int getFieldSize();
    
    public abstract ECFieldElement fromBigInteger(final BigInteger p0);
    
    public abstract boolean isValidFieldElement(final BigInteger p0);
    
    public abstract ECFieldElement randomFieldElement(final SecureRandom p0);
    
    public abstract ECFieldElement randomFieldElementMult(final SecureRandom p0);
    
    public synchronized Config configure() {
        return new Config(this.coord, this.endomorphism, this.multiplier);
    }
    
    public int getFieldElementEncodingLength() {
        return (this.getFieldSize() + 7) / 8;
    }
    
    public int getAffinePointEncodingLength(final boolean b) {
        final int fieldElementEncodingLength = this.getFieldElementEncodingLength();
        return b ? (1 + fieldElementEncodingLength) : (1 + fieldElementEncodingLength * 2);
    }
    
    public ECPoint validatePoint(final BigInteger bigInteger, final BigInteger bigInteger2) {
        final ECPoint point = this.createPoint(bigInteger, bigInteger2);
        if (!point.isValid()) {
            throw new IllegalArgumentException("Invalid point coordinates");
        }
        return point;
    }
    
    public ECPoint createPoint(final BigInteger bigInteger, final BigInteger bigInteger2) {
        return this.createRawPoint(this.fromBigInteger(bigInteger), this.fromBigInteger(bigInteger2));
    }
    
    protected abstract ECCurve cloneCurve();
    
    protected abstract ECPoint createRawPoint(final ECFieldElement p0, final ECFieldElement p1);
    
    protected abstract ECPoint createRawPoint(final ECFieldElement p0, final ECFieldElement p1, final ECFieldElement[] p2);
    
    protected ECMultiplier createDefaultMultiplier() {
        if (this.endomorphism instanceof GLVEndomorphism) {
            return new GLVMultiplier(this, (GLVEndomorphism)this.endomorphism);
        }
        return new WNafL2RMultiplier();
    }
    
    public boolean supportsCoordinateSystem(final int n) {
        return n == 0;
    }
    
    public PreCompInfo getPreCompInfo(final ECPoint ecPoint, final String key) {
        this.checkPoint(ecPoint);
        final Hashtable preCompTable;
        synchronized (ecPoint) {
            preCompTable = ecPoint.preCompTable;
        }
        if (null == preCompTable) {
            return null;
        }
        synchronized (preCompTable) {
            return (PreCompInfo)preCompTable.get(key);
        }
    }
    
    public PreCompInfo precompute(final ECPoint ecPoint, final String s, final PreCompCallback preCompCallback) {
        this.checkPoint(ecPoint);
        Hashtable preCompTable;
        synchronized (ecPoint) {
            preCompTable = ecPoint.preCompTable;
            if (null == preCompTable) {
                preCompTable = (ecPoint.preCompTable = new Hashtable(4));
            }
        }
        synchronized (preCompTable) {
            final PreCompInfo preCompInfo = preCompTable.get(s);
            final PreCompInfo precompute = preCompCallback.precompute(preCompInfo);
            if (precompute != preCompInfo) {
                preCompTable.put(s, precompute);
            }
            return precompute;
        }
    }
    
    public ECPoint importPoint(ECPoint normalize) {
        if (this == normalize.getCurve()) {
            return normalize;
        }
        if (normalize.isInfinity()) {
            return this.getInfinity();
        }
        normalize = normalize.normalize();
        return this.createPoint(normalize.getXCoord().toBigInteger(), normalize.getYCoord().toBigInteger());
    }
    
    public void normalizeAll(final ECPoint[] array) {
        this.normalizeAll(array, 0, array.length, null);
    }
    
    public void normalizeAll(final ECPoint[] array, final int n, final int n2, final ECFieldElement ecFieldElement) {
        this.checkPoints(array, n, n2);
        switch (this.getCoordinateSystem()) {
            case 0:
            case 5: {
                if (ecFieldElement != null) {
                    throw new IllegalArgumentException("'iso' not valid for affine coordinates");
                }
                return;
            }
            default: {
                final ECFieldElement[] array2 = new ECFieldElement[n2];
                final int[] array3 = new int[n2];
                int n3 = 0;
                for (int i = 0; i < n2; ++i) {
                    final ECPoint ecPoint = array[n + i];
                    if (null != ecPoint && (ecFieldElement != null || !ecPoint.isNormalized())) {
                        array2[n3] = ecPoint.getZCoord(0);
                        array3[n3++] = n + i;
                    }
                }
                if (n3 == 0) {
                    return;
                }
                ECAlgorithms.montgomeryTrick(array2, 0, n3, ecFieldElement);
                for (int j = 0; j < n3; ++j) {
                    final int n4 = array3[j];
                    array[n4] = array[n4].normalize(array2[j]);
                }
            }
        }
    }
    
    public abstract ECPoint getInfinity();
    
    public FiniteField getField() {
        return this.field;
    }
    
    public ECFieldElement getA() {
        return this.a;
    }
    
    public ECFieldElement getB() {
        return this.b;
    }
    
    public BigInteger getOrder() {
        return this.order;
    }
    
    public BigInteger getCofactor() {
        return this.cofactor;
    }
    
    public int getCoordinateSystem() {
        return this.coord;
    }
    
    protected abstract ECPoint decompressPoint(final int p0, final BigInteger p1);
    
    public ECEndomorphism getEndomorphism() {
        return this.endomorphism;
    }
    
    public ECMultiplier getMultiplier() {
        if (this.multiplier == null) {
            this.multiplier = this.createDefaultMultiplier();
        }
        return this.multiplier;
    }
    
    public ECPoint decodePoint(final byte[] array) {
        final int fieldElementEncodingLength = this.getFieldElementEncodingLength();
        final byte i = array[0];
        ECPoint ecPoint = null;
        switch (i) {
            case 0: {
                if (array.length != 1) {
                    throw new IllegalArgumentException("Incorrect length for infinity encoding");
                }
                ecPoint = this.getInfinity();
                break;
            }
            case 2:
            case 3: {
                if (array.length != fieldElementEncodingLength + 1) {
                    throw new IllegalArgumentException("Incorrect length for compressed encoding");
                }
                ecPoint = this.decompressPoint(i & 0x1, BigIntegers.fromUnsignedByteArray(array, 1, fieldElementEncodingLength));
                if (!ecPoint.implIsValid(true, true)) {
                    throw new IllegalArgumentException("Invalid point");
                }
                break;
            }
            case 4: {
                if (array.length != 2 * fieldElementEncodingLength + 1) {
                    throw new IllegalArgumentException("Incorrect length for uncompressed encoding");
                }
                ecPoint = this.validatePoint(BigIntegers.fromUnsignedByteArray(array, 1, fieldElementEncodingLength), BigIntegers.fromUnsignedByteArray(array, 1 + fieldElementEncodingLength, fieldElementEncodingLength));
                break;
            }
            case 6:
            case 7: {
                if (array.length != 2 * fieldElementEncodingLength + 1) {
                    throw new IllegalArgumentException("Incorrect length for hybrid encoding");
                }
                final BigInteger fromUnsignedByteArray = BigIntegers.fromUnsignedByteArray(array, 1, fieldElementEncodingLength);
                final BigInteger fromUnsignedByteArray2 = BigIntegers.fromUnsignedByteArray(array, 1 + fieldElementEncodingLength, fieldElementEncodingLength);
                if (fromUnsignedByteArray2.testBit(0) != (i == 7)) {
                    throw new IllegalArgumentException("Inconsistent Y coordinate in hybrid encoding");
                }
                ecPoint = this.validatePoint(fromUnsignedByteArray, fromUnsignedByteArray2);
                break;
            }
            default: {
                throw new IllegalArgumentException("Invalid point encoding 0x" + Integer.toString(i, 16));
            }
        }
        if (i != 0 && ecPoint.isInfinity()) {
            throw new IllegalArgumentException("Invalid infinity encoding");
        }
        return ecPoint;
    }
    
    public ECLookupTable createCacheSafeLookupTable(final ECPoint[] array, final int n, final int n2) {
        final int fieldElementEncodingLength = this.getFieldElementEncodingLength();
        final byte[] array2 = new byte[n2 * fieldElementEncodingLength * 2];
        int n3 = 0;
        for (int i = 0; i < n2; ++i) {
            final ECPoint ecPoint = array[n + i];
            ecPoint.getRawXCoord().encodeTo(array2, n3);
            final int n4 = n3 + fieldElementEncodingLength;
            ecPoint.getRawYCoord().encodeTo(array2, n4);
            n3 = n4 + fieldElementEncodingLength;
        }
        return new AbstractECLookupTable() {
            @Override
            public int getSize() {
                return n2;
            }
            
            @Override
            public ECPoint lookup(final int n) {
                final byte[] array = new byte[fieldElementEncodingLength];
                final byte[] array2 = new byte[fieldElementEncodingLength];
                int n2 = 0;
                for (int i = 0; i < n2; ++i) {
                    final int n3 = (i ^ n) - 1 >> 31;
                    for (int j = 0; j < fieldElementEncodingLength; ++j) {
                        final byte[] array3 = array;
                        final int n4 = j;
                        array3[n4] ^= (byte)(array2[n2 + j] & n3);
                        final byte[] array4 = array2;
                        final int n5 = j;
                        array4[n5] ^= (byte)(array2[n2 + fieldElementEncodingLength + j] & n3);
                    }
                    n2 += fieldElementEncodingLength * 2;
                }
                return this.createPoint(array, array2);
            }
            
            @Override
            public ECPoint lookupVar(final int n) {
                final byte[] array = new byte[fieldElementEncodingLength];
                final byte[] array2 = new byte[fieldElementEncodingLength];
                final int n2 = n * fieldElementEncodingLength * 2;
                for (int i = 0; i < fieldElementEncodingLength; ++i) {
                    array[i] = array2[n2 + i];
                    array2[i] = array2[n2 + fieldElementEncodingLength + i];
                }
                return this.createPoint(array, array2);
            }
            
            private ECPoint createPoint(final byte[] magnitude, final byte[] magnitude2) {
                return ECCurve.this.createRawPoint(ECCurve.this.fromBigInteger(new BigInteger(1, magnitude)), ECCurve.this.fromBigInteger(new BigInteger(1, magnitude2)));
            }
        };
    }
    
    protected void checkPoint(final ECPoint ecPoint) {
        if (null == ecPoint || this != ecPoint.getCurve()) {
            throw new IllegalArgumentException("'point' must be non-null and on this curve");
        }
    }
    
    protected void checkPoints(final ECPoint[] array) {
        this.checkPoints(array, 0, array.length);
    }
    
    protected void checkPoints(final ECPoint[] array, final int n, final int n2) {
        if (array == null) {
            throw new IllegalArgumentException("'points' cannot be null");
        }
        if (n < 0 || n2 < 0 || n > array.length - n2) {
            throw new IllegalArgumentException("invalid range specified for 'points'");
        }
        for (int i = 0; i < n2; ++i) {
            final ECPoint ecPoint = array[n + i];
            if (null != ecPoint && this != ecPoint.getCurve()) {
                throw new IllegalArgumentException("'points' entries must be null or on this curve");
            }
        }
    }
    
    public boolean equals(final ECCurve ecCurve) {
        return this == ecCurve || (null != ecCurve && this.getField().equals(ecCurve.getField()) && this.getA().toBigInteger().equals(ecCurve.getA().toBigInteger()) && this.getB().toBigInteger().equals(ecCurve.getB().toBigInteger()));
    }
    
    @Override
    public boolean equals(final Object o) {
        return this == o || (o instanceof ECCurve && this.equals((ECCurve)o));
    }
    
    @Override
    public int hashCode() {
        return this.getField().hashCode() ^ Integers.rotateLeft(this.getA().toBigInteger().hashCode(), 8) ^ Integers.rotateLeft(this.getB().toBigInteger().hashCode(), 16);
    }
    
    private static int getNumberOfIterations(final int n, final int n2) {
        if (n >= 1536) {
            return (n2 <= 100) ? 3 : ((n2 <= 128) ? 4 : (4 + (n2 - 128 + 1) / 2));
        }
        if (n >= 1024) {
            return (n2 <= 100) ? 4 : ((n2 <= 112) ? 5 : (5 + (n2 - 112 + 1) / 2));
        }
        if (n >= 512) {
            return (n2 <= 80) ? 5 : ((n2 <= 100) ? 7 : (7 + (n2 - 100 + 1) / 2));
        }
        return (n2 <= 80) ? 40 : (40 + (n2 - 80 + 1) / 2);
    }
    
    public abstract static class AbstractF2m extends ECCurve
    {
        private BigInteger[] si;
        
        public static BigInteger inverse(final int n, final int[] array, final BigInteger bigInteger) {
            return new LongArray(bigInteger).modInverse(n, array).toBigInteger();
        }
        
        private static FiniteField buildField(final int i, final int n, final int n2, final int n3) {
            if (i > Properties.asInteger("org.bouncycastle.ec.max_f2m_field_size", 1142)) {
                throw new IllegalArgumentException("field size out of range: " + i);
            }
            return FiniteFields.getBinaryExtensionField(((n2 | n3) == 0x0) ? new int[] { 0, n, i } : new int[] { 0, n, n2, n3, i });
        }
        
        protected AbstractF2m(final int n, final int n2, final int n3, final int n4) {
            super(buildField(n, n2, n3, n4));
            this.si = null;
            if (Properties.isOverrideSet("org.bouncycastle.ec.disable")) {
                throw new UnsupportedOperationException("F2M disabled by \"org.bouncycastle.ec.disable\"");
            }
            if (Properties.isOverrideSet("org.bouncycastle.ec.disable_f2m")) {
                throw new UnsupportedOperationException("F2M disabled by \"org.bouncycastle.ec.disable_f2m\"");
            }
        }
        
        @Override
        public ECPoint createPoint(final BigInteger bigInteger, final BigInteger bigInteger2) {
            final ECFieldElement fromBigInteger = this.fromBigInteger(bigInteger);
            ECFieldElement ecFieldElement = this.fromBigInteger(bigInteger2);
            switch (this.getCoordinateSystem()) {
                case 5:
                case 6: {
                    if (!fromBigInteger.isZero()) {
                        ecFieldElement = ecFieldElement.divide(fromBigInteger).add(fromBigInteger);
                        break;
                    }
                    if (!ecFieldElement.square().equals(this.getB())) {
                        throw new IllegalArgumentException();
                    }
                    break;
                }
            }
            return this.createRawPoint(fromBigInteger, ecFieldElement);
        }
        
        @Override
        public boolean isValidFieldElement(final BigInteger bigInteger) {
            return bigInteger != null && bigInteger.signum() >= 0 && bigInteger.bitLength() <= this.getFieldSize();
        }
        
        @Override
        public ECFieldElement randomFieldElement(final SecureRandom secureRandom) {
            return this.fromBigInteger(BigIntegers.createRandomBigInteger(this.getFieldSize(), secureRandom));
        }
        
        @Override
        public ECFieldElement randomFieldElementMult(final SecureRandom secureRandom) {
            final int fieldSize = this.getFieldSize();
            return this.fromBigInteger(implRandomFieldElementMult(secureRandom, fieldSize)).multiply(this.fromBigInteger(implRandomFieldElementMult(secureRandom, fieldSize)));
        }
        
        @Override
        protected ECPoint decompressPoint(final int n, final BigInteger bigInteger) {
            final ECFieldElement fromBigInteger = this.fromBigInteger(bigInteger);
            ECFieldElement ecFieldElement = null;
            if (fromBigInteger.isZero()) {
                ecFieldElement = this.getB().sqrt();
            }
            else {
                ECFieldElement ecFieldElement2 = this.solveQuadraticEquation(fromBigInteger.square().invert().multiply(this.getB()).add(this.getA()).add(fromBigInteger));
                if (ecFieldElement2 != null) {
                    if (ecFieldElement2.testBitZero() != (n == 1)) {
                        ecFieldElement2 = ecFieldElement2.addOne();
                    }
                    switch (this.getCoordinateSystem()) {
                        case 5:
                        case 6: {
                            ecFieldElement = ecFieldElement2.add(fromBigInteger);
                            break;
                        }
                        default: {
                            ecFieldElement = ecFieldElement2.multiply(fromBigInteger);
                            break;
                        }
                    }
                }
            }
            if (ecFieldElement == null) {
                throw new IllegalArgumentException("Invalid point compression");
            }
            return this.createRawPoint(fromBigInteger, ecFieldElement);
        }
        
        protected ECFieldElement solveQuadraticEquation(final ECFieldElement ecFieldElement) {
            final ECFieldElement.AbstractF2m abstractF2m = (ECFieldElement.AbstractF2m)ecFieldElement;
            final boolean hasFastTrace = abstractF2m.hasFastTrace();
            if (hasFastTrace && 0 != abstractF2m.trace()) {
                return null;
            }
            final int fieldSize = this.getFieldSize();
            if (0x0 != (fieldSize & 0x1)) {
                final ECFieldElement halfTrace = abstractF2m.halfTrace();
                if (hasFastTrace || halfTrace.square().add(halfTrace).add(ecFieldElement).isZero()) {
                    return halfTrace;
                }
                return null;
            }
            else {
                if (ecFieldElement.isZero()) {
                    return ecFieldElement;
                }
                final ECFieldElement fromBigInteger = this.fromBigInteger(ECConstants.ZERO);
                final Random rnd = new Random();
                ECFieldElement add;
                do {
                    final ECFieldElement fromBigInteger2 = this.fromBigInteger(new BigInteger(fieldSize, rnd));
                    add = fromBigInteger;
                    ECFieldElement add2 = ecFieldElement;
                    for (int i = 1; i < fieldSize; ++i) {
                        final ECFieldElement square = add2.square();
                        add = add.square().add(square.multiply(fromBigInteger2));
                        add2 = square.add(ecFieldElement);
                    }
                    if (!add2.isZero()) {
                        return null;
                    }
                } while (add.square().add(add).isZero());
                return add;
            }
        }
        
        synchronized BigInteger[] getSi() {
            if (this.si == null) {
                this.si = Tnaf.getSi(this);
            }
            return this.si;
        }
        
        public boolean isKoblitz() {
            return this.order != null && this.cofactor != null && this.b.isOne() && (this.a.isZero() || this.a.isOne());
        }
        
        private static BigInteger implRandomFieldElementMult(final SecureRandom secureRandom, final int n) {
            BigInteger randomBigInteger;
            do {
                randomBigInteger = BigIntegers.createRandomBigInteger(n, secureRandom);
            } while (randomBigInteger.signum() <= 0);
            return randomBigInteger;
        }
    }
    
    public abstract static class AbstractFp extends ECCurve
    {
        protected AbstractFp(final BigInteger bigInteger) {
            super(FiniteFields.getPrimeField(bigInteger));
        }
        
        public BigInteger getQ() {
            return this.getField().getCharacteristic();
        }
        
        @Override
        public boolean isValidFieldElement(final BigInteger bigInteger) {
            return bigInteger != null && bigInteger.signum() >= 0 && bigInteger.compareTo(this.getQ()) < 0;
        }
        
        @Override
        public ECFieldElement randomFieldElement(final SecureRandom secureRandom) {
            final BigInteger q = this.getQ();
            return this.fromBigInteger(implRandomFieldElement(secureRandom, q)).multiply(this.fromBigInteger(implRandomFieldElement(secureRandom, q)));
        }
        
        @Override
        public ECFieldElement randomFieldElementMult(final SecureRandom secureRandom) {
            final BigInteger q = this.getQ();
            return this.fromBigInteger(implRandomFieldElementMult(secureRandom, q)).multiply(this.fromBigInteger(implRandomFieldElementMult(secureRandom, q)));
        }
        
        @Override
        protected ECPoint decompressPoint(final int n, final BigInteger bigInteger) {
            final ECFieldElement fromBigInteger = this.fromBigInteger(bigInteger);
            ECFieldElement ecFieldElement = fromBigInteger.square().add(this.a).multiply(fromBigInteger).add(this.b).sqrt();
            if (ecFieldElement == null) {
                throw new IllegalArgumentException("Invalid point compression");
            }
            if (ecFieldElement.testBitZero() != (n == 1)) {
                ecFieldElement = ecFieldElement.negate();
            }
            return this.createRawPoint(fromBigInteger, ecFieldElement);
        }
        
        private static BigInteger implRandomFieldElement(final SecureRandom secureRandom, final BigInteger val) {
            BigInteger randomBigInteger;
            do {
                randomBigInteger = BigIntegers.createRandomBigInteger(val.bitLength(), secureRandom);
            } while (randomBigInteger.compareTo(val) >= 0);
            return randomBigInteger;
        }
        
        private static BigInteger implRandomFieldElementMult(final SecureRandom secureRandom, final BigInteger val) {
            BigInteger randomBigInteger;
            do {
                randomBigInteger = BigIntegers.createRandomBigInteger(val.bitLength(), secureRandom);
            } while (randomBigInteger.signum() <= 0 || randomBigInteger.compareTo(val) >= 0);
            return randomBigInteger;
        }
    }
    
    public class Config
    {
        protected int coord;
        protected ECEndomorphism endomorphism;
        protected ECMultiplier multiplier;
        
        Config(final int coord, final ECEndomorphism endomorphism, final ECMultiplier multiplier) {
            this.coord = coord;
            this.endomorphism = endomorphism;
            this.multiplier = multiplier;
        }
        
        public Config setCoordinateSystem(final int coord) {
            this.coord = coord;
            return this;
        }
        
        public Config setEndomorphism(final ECEndomorphism endomorphism) {
            this.endomorphism = endomorphism;
            return this;
        }
        
        public Config setMultiplier(final ECMultiplier multiplier) {
            this.multiplier = multiplier;
            return this;
        }
        
        public ECCurve create() {
            if (!ECCurve.this.supportsCoordinateSystem(this.coord)) {
                throw new IllegalStateException("unsupported coordinate system");
            }
            final ECCurve cloneCurve = ECCurve.this.cloneCurve();
            if (cloneCurve == ECCurve.this) {
                throw new IllegalStateException("implementation returned current curve");
            }
            synchronized (cloneCurve) {
                cloneCurve.coord = this.coord;
                cloneCurve.endomorphism = this.endomorphism;
                cloneCurve.multiplier = this.multiplier;
            }
            return cloneCurve;
        }
    }
    
    public static class F2m extends AbstractF2m
    {
        private static final int F2M_DEFAULT_COORDS = 6;
        private int m;
        private int k1;
        private int k2;
        private int k3;
        private ECPoint.F2m infinity;
        
        @Deprecated
        public F2m(final int n, final int n2, final BigInteger bigInteger, final BigInteger bigInteger2) {
            this(n, n2, 0, 0, bigInteger, bigInteger2, null, null);
        }
        
        public F2m(final int n, final int n2, final BigInteger bigInteger, final BigInteger bigInteger2, final BigInteger bigInteger3, final BigInteger bigInteger4) {
            this(n, n2, 0, 0, bigInteger, bigInteger2, bigInteger3, bigInteger4);
        }
        
        @Deprecated
        public F2m(final int n, final int n2, final int n3, final int n4, final BigInteger bigInteger, final BigInteger bigInteger2) {
            this(n, n2, n3, n4, bigInteger, bigInteger2, null, null);
        }
        
        public F2m(final int m, final int k1, final int k2, final int k3, final BigInteger bigInteger, final BigInteger bigInteger2, final BigInteger order, final BigInteger cofactor) {
            super(m, k1, k2, k3);
            this.m = m;
            this.k1 = k1;
            this.k2 = k2;
            this.k3 = k3;
            this.order = order;
            this.cofactor = cofactor;
            this.infinity = new ECPoint.F2m(this, null, null);
            this.a = this.fromBigInteger(bigInteger);
            this.b = this.fromBigInteger(bigInteger2);
            this.coord = 6;
        }
        
        protected F2m(final int m, final int k1, final int k2, final int k3, final ECFieldElement a, final ECFieldElement b, final BigInteger order, final BigInteger cofactor) {
            super(m, k1, k2, k3);
            this.m = m;
            this.k1 = k1;
            this.k2 = k2;
            this.k3 = k3;
            this.order = order;
            this.cofactor = cofactor;
            this.infinity = new ECPoint.F2m(this, null, null);
            this.a = a;
            this.b = b;
            this.coord = 6;
        }
        
        @Override
        protected ECCurve cloneCurve() {
            return new F2m(this.m, this.k1, this.k2, this.k3, this.a, this.b, this.order, this.cofactor);
        }
        
        @Override
        public boolean supportsCoordinateSystem(final int n) {
            switch (n) {
                case 0:
                case 1:
                case 6: {
                    return true;
                }
                default: {
                    return false;
                }
            }
        }
        
        @Override
        protected ECMultiplier createDefaultMultiplier() {
            if (this.isKoblitz()) {
                return new WTauNafMultiplier();
            }
            return super.createDefaultMultiplier();
        }
        
        @Override
        public int getFieldSize() {
            return this.m;
        }
        
        @Override
        public ECFieldElement fromBigInteger(final BigInteger bigInteger) {
            if (bigInteger == null || bigInteger.signum() < 0 || bigInteger.bitLength() > this.m) {
                throw new IllegalArgumentException("x value invalid in F2m field element");
            }
            return new ECFieldElement.F2m(this.m, ((this.k2 | this.k3) == 0x0) ? new int[] { this.k1 } : new int[] { this.k1, this.k2, this.k3 }, new LongArray(bigInteger));
        }
        
        @Override
        protected ECPoint createRawPoint(final ECFieldElement ecFieldElement, final ECFieldElement ecFieldElement2) {
            return new ECPoint.F2m(this, ecFieldElement, ecFieldElement2);
        }
        
        @Override
        protected ECPoint createRawPoint(final ECFieldElement ecFieldElement, final ECFieldElement ecFieldElement2, final ECFieldElement[] array) {
            return new ECPoint.F2m(this, ecFieldElement, ecFieldElement2, array);
        }
        
        @Override
        public ECPoint getInfinity() {
            return this.infinity;
        }
        
        public int getM() {
            return this.m;
        }
        
        public boolean isTrinomial() {
            return this.k2 == 0 && this.k3 == 0;
        }
        
        public int getK1() {
            return this.k1;
        }
        
        public int getK2() {
            return this.k2;
        }
        
        public int getK3() {
            return this.k3;
        }
        
        @Override
        public ECLookupTable createCacheSafeLookupTable(final ECPoint[] array, final int n, final int n2) {
            final int n3 = this.m + 63 >>> 6;
            final int[] array2 = this.isTrinomial() ? new int[] { this.k1 } : new int[] { this.k1, this.k2, this.k3 };
            final long[] array3 = new long[n2 * n3 * 2];
            int n4 = 0;
            for (int i = 0; i < n2; ++i) {
                final ECPoint ecPoint = array[n + i];
                ((ECFieldElement.F2m)ecPoint.getRawXCoord()).x.copyTo(array3, n4);
                final int n5 = n4 + n3;
                ((ECFieldElement.F2m)ecPoint.getRawYCoord()).x.copyTo(array3, n5);
                n4 = n5 + n3;
            }
            return new AbstractECLookupTable() {
                @Override
                public int getSize() {
                    return n2;
                }
                
                @Override
                public ECPoint lookup(final int n) {
                    final long[] create64 = Nat.create64(n3);
                    final long[] create65 = Nat.create64(n3);
                    int n2 = 0;
                    for (int i = 0; i < n2; ++i) {
                        final long n3 = (i ^ n) - 1 >> 31;
                        for (int j = 0; j < n3; ++j) {
                            final long[] array = create64;
                            final int n4 = j;
                            array[n4] ^= (array3[n2 + j] & n3);
                            final long[] array2 = create65;
                            final int n5 = j;
                            array2[n5] ^= (array3[n2 + n3 + j] & n3);
                        }
                        n2 += n3 * 2;
                    }
                    return this.createPoint(create64, create65);
                }
                
                @Override
                public ECPoint lookupVar(final int n) {
                    final long[] create64 = Nat.create64(n3);
                    final long[] create65 = Nat.create64(n3);
                    final int n2 = n * n3 * 2;
                    for (int i = 0; i < n3; ++i) {
                        create64[i] = array3[n2 + i];
                        create65[i] = array3[n2 + n3 + i];
                    }
                    return this.createPoint(create64, create65);
                }
                
                private ECPoint createPoint(final long[] array, final long[] array2) {
                    return F2m.this.createRawPoint(new ECFieldElement.F2m(F2m.this.m, array2, new LongArray(array)), new ECFieldElement.F2m(F2m.this.m, array2, new LongArray(array2)));
                }
            };
        }
    }
    
    public static class Fp extends AbstractFp
    {
        private static final int FP_DEFAULT_COORDS = 4;
        private static final Set<BigInteger> knownQs;
        private static final BigIntegers.Cache validatedQs;
        BigInteger q;
        BigInteger r;
        ECPoint.Fp infinity;
        
        @Deprecated
        public Fp(final BigInteger bigInteger, final BigInteger bigInteger2, final BigInteger bigInteger3) {
            this(bigInteger, bigInteger2, bigInteger3, null, null);
        }
        
        public Fp(final BigInteger bigInteger, final BigInteger bigInteger2, final BigInteger bigInteger3, final BigInteger bigInteger4, final BigInteger bigInteger5) {
            this(bigInteger, bigInteger2, bigInteger3, bigInteger4, bigInteger5, false);
        }
        
        public Fp(final BigInteger q, final BigInteger bigInteger, final BigInteger bigInteger2, final BigInteger order, final BigInteger cofactor, final boolean b) {
            super(q);
            if (b) {
                Fp.knownQs.add(q);
            }
            else if (!Fp.knownQs.contains(q)) {
                if (!Fp.validatedQs.contains(q)) {
                    final int integer = Properties.asInteger("org.bouncycastle.ec.fp_max_size", 1042);
                    final int integer2 = Properties.asInteger("org.bouncycastle.ec.fp_certainty", 100);
                    final int bitLength = q.bitLength();
                    if (integer < bitLength) {
                        throw new IllegalArgumentException("Fp q value out of range");
                    }
                    if (Primes.hasAnySmallFactors(q) || !Primes.isMRProbablePrime(q, CryptoServicesRegistrar.getSecureRandom(), getNumberOfIterations(bitLength, integer2))) {
                        throw new IllegalArgumentException("Fp q value not prime");
                    }
                    Fp.validatedQs.add(q);
                }
            }
            this.q = q;
            this.r = ECFieldElement.Fp.calculateResidue(q);
            this.infinity = new ECPoint.Fp(this, null, null);
            this.a = this.fromBigInteger(bigInteger);
            this.b = this.fromBigInteger(bigInteger2);
            this.order = order;
            this.cofactor = cofactor;
            this.coord = 4;
        }
        
        protected Fp(final BigInteger q, final BigInteger r, final ECFieldElement a, final ECFieldElement b, final BigInteger order, final BigInteger cofactor) {
            super(q);
            this.q = q;
            this.r = r;
            this.infinity = new ECPoint.Fp(this, null, null);
            this.a = a;
            this.b = b;
            this.order = order;
            this.cofactor = cofactor;
            this.coord = 4;
        }
        
        @Override
        protected ECCurve cloneCurve() {
            return new Fp(this.q, this.r, this.a, this.b, this.order, this.cofactor);
        }
        
        @Override
        public boolean supportsCoordinateSystem(final int n) {
            switch (n) {
                case 0:
                case 1:
                case 2:
                case 4: {
                    return true;
                }
                default: {
                    return false;
                }
            }
        }
        
        @Override
        public BigInteger getQ() {
            return this.q;
        }
        
        @Override
        public int getFieldSize() {
            return this.q.bitLength();
        }
        
        @Override
        public ECFieldElement fromBigInteger(final BigInteger bigInteger) {
            if (bigInteger == null || bigInteger.signum() < 0 || bigInteger.compareTo(this.q) >= 0) {
                throw new IllegalArgumentException("x value invalid for Fp field element");
            }
            return new ECFieldElement.Fp(this.q, this.r, bigInteger);
        }
        
        @Override
        protected ECPoint createRawPoint(final ECFieldElement ecFieldElement, final ECFieldElement ecFieldElement2) {
            return new ECPoint.Fp(this, ecFieldElement, ecFieldElement2);
        }
        
        @Override
        protected ECPoint createRawPoint(final ECFieldElement ecFieldElement, final ECFieldElement ecFieldElement2, final ECFieldElement[] array) {
            return new ECPoint.Fp(this, ecFieldElement, ecFieldElement2, array);
        }
        
        @Override
        public ECPoint importPoint(final ECPoint ecPoint) {
            if (this != ecPoint.getCurve() && this.getCoordinateSystem() == 2 && !ecPoint.isInfinity()) {
                switch (ecPoint.getCurve().getCoordinateSystem()) {
                    case 2:
                    case 3:
                    case 4: {
                        return new ECPoint.Fp(this, this.fromBigInteger(ecPoint.x.toBigInteger()), this.fromBigInteger(ecPoint.y.toBigInteger()), new ECFieldElement[] { this.fromBigInteger(ecPoint.zs[0].toBigInteger()) });
                    }
                }
            }
            return super.importPoint(ecPoint);
        }
        
        @Override
        public ECPoint getInfinity() {
            return this.infinity;
        }
        
        static {
            knownQs = Collections.synchronizedSet(new HashSet<BigInteger>());
            validatedQs = new BigIntegers.Cache();
        }
    }
}
