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

package org.bouncycastle.util;

import java.util.WeakHashMap;
import java.util.Map;
import java.io.IOException;
import java.io.OutputStream;
import org.bouncycastle.math.raw.Mod;
import org.bouncycastle.math.raw.Nat;
import java.security.SecureRandom;
import java.math.BigInteger;

public final class BigIntegers
{
    public static final BigInteger ZERO;
    public static final BigInteger ONE;
    public static final BigInteger TWO;
    private static final BigInteger THREE;
    private static final int MAX_ITERATIONS = 1000;
    private static final BigInteger SMALL_PRIMES_PRODUCT;
    private static final int MAX_SMALL;
    
    public static byte[] asUnsignedByteArray(final BigInteger bigInteger) {
        final byte[] byteArray = bigInteger.toByteArray();
        if (byteArray[0] == 0 && byteArray.length != 1) {
            final byte[] array = new byte[byteArray.length - 1];
            System.arraycopy(byteArray, 1, array, 0, array.length);
            return array;
        }
        return byteArray;
    }
    
    public static byte[] asUnsignedByteArray(final int n, final BigInteger bigInteger) {
        final byte[] byteArray = bigInteger.toByteArray();
        if (byteArray.length == n) {
            return byteArray;
        }
        final int n2 = (byteArray[0] == 0 && byteArray.length != 1) ? 1 : 0;
        final int n3 = byteArray.length - n2;
        if (n3 > n) {
            throw new IllegalArgumentException("standard length exceeded for value");
        }
        final byte[] array = new byte[n];
        System.arraycopy(byteArray, n2, array, array.length - n3, n3);
        return array;
    }
    
    public static void asUnsignedByteArray(final BigInteger bigInteger, final byte[] array, final int n, final int n2) {
        final byte[] byteArray = bigInteger.toByteArray();
        if (byteArray.length == n2) {
            System.arraycopy(byteArray, 0, array, n, n2);
            return;
        }
        final int n3 = (byteArray[0] == 0 && byteArray.length != 1) ? 1 : 0;
        final int n4 = byteArray.length - n3;
        if (n4 > n2) {
            throw new IllegalArgumentException("standard length exceeded for value");
        }
        final int n5 = n2 - n4;
        Arrays.fill(array, n, n + n5, (byte)0);
        System.arraycopy(byteArray, n3, array, n + n5, n4);
    }
    
    public static BigInteger createRandomInRange(final BigInteger val, final BigInteger bigInteger, final SecureRandom secureRandom) {
        final int compareTo = val.compareTo(bigInteger);
        if (compareTo >= 0) {
            if (compareTo > 0) {
                throw new IllegalArgumentException("'min' may not be greater than 'max'");
            }
            return val;
        }
        else {
            if (val.bitLength() > bigInteger.bitLength() / 2) {
                return createRandomInRange(BigIntegers.ZERO, bigInteger.subtract(val), secureRandom).add(val);
            }
            for (int i = 0; i < 1000; ++i) {
                final BigInteger randomBigInteger = createRandomBigInteger(bigInteger.bitLength(), secureRandom);
                if (randomBigInteger.compareTo(val) >= 0 && randomBigInteger.compareTo(bigInteger) <= 0) {
                    return randomBigInteger;
                }
            }
            return createRandomBigInteger(bigInteger.subtract(val).bitLength() - 1, secureRandom).add(val);
        }
    }
    
    public static BigInteger fromUnsignedByteArray(final byte[] magnitude) {
        return new BigInteger(1, magnitude);
    }
    
    public static BigInteger fromUnsignedByteArray(final byte[] array, final int n, final int n2) {
        byte[] magnitude = array;
        if (n != 0 || n2 != array.length) {
            magnitude = new byte[n2];
            System.arraycopy(array, n, magnitude, 0, n2);
        }
        return new BigInteger(1, magnitude);
    }
    
    public static byte byteValueExact(final BigInteger bigInteger) {
        if (bigInteger.bitLength() > 7) {
            throw new ArithmeticException("BigInteger out of int range");
        }
        return bigInteger.byteValue();
    }
    
    public static short shortValueExact(final BigInteger bigInteger) {
        if (bigInteger.bitLength() > 15) {
            throw new ArithmeticException("BigInteger out of int range");
        }
        return bigInteger.shortValue();
    }
    
    public static int intValueExact(final BigInteger bigInteger) {
        if (bigInteger.bitLength() > 31) {
            throw new ArithmeticException("BigInteger out of int range");
        }
        return bigInteger.intValue();
    }
    
    public static long longValueExact(final BigInteger bigInteger) {
        if (bigInteger.bitLength() > 63) {
            throw new ArithmeticException("BigInteger out of long range");
        }
        return bigInteger.longValue();
    }
    
    public static BigInteger modOddInverse(final BigInteger m, BigInteger mod) {
        if (!m.testBit(0)) {
            throw new IllegalArgumentException("'M' must be odd");
        }
        if (m.signum() != 1) {
            throw new ArithmeticException("BigInteger: modulus not positive");
        }
        if (mod.signum() < 0 || mod.bitLength() > m.bitLength()) {
            mod = mod.mod(m);
        }
        final int bitLength = m.bitLength();
        final int[] fromBigInteger = Nat.fromBigInteger(bitLength, m);
        final int[] fromBigInteger2 = Nat.fromBigInteger(bitLength, mod);
        final int length = fromBigInteger.length;
        final int[] create = Nat.create(length);
        if (0 == Mod.modOddInverse(fromBigInteger, fromBigInteger2, create)) {
            throw new ArithmeticException("BigInteger not invertible.");
        }
        return Nat.toBigInteger(length, create);
    }
    
    public static BigInteger modOddInverseVar(final BigInteger m, BigInteger mod) {
        if (!m.testBit(0)) {
            throw new IllegalArgumentException("'M' must be odd");
        }
        if (m.signum() != 1) {
            throw new ArithmeticException("BigInteger: modulus not positive");
        }
        if (m.equals(BigIntegers.ONE)) {
            return BigIntegers.ZERO;
        }
        if (mod.signum() < 0 || mod.bitLength() > m.bitLength()) {
            mod = mod.mod(m);
        }
        if (mod.equals(BigIntegers.ONE)) {
            return BigIntegers.ONE;
        }
        final int bitLength = m.bitLength();
        final int[] fromBigInteger = Nat.fromBigInteger(bitLength, m);
        final int[] fromBigInteger2 = Nat.fromBigInteger(bitLength, mod);
        final int length = fromBigInteger.length;
        final int[] create = Nat.create(length);
        if (!Mod.modOddInverseVar(fromBigInteger, fromBigInteger2, create)) {
            throw new ArithmeticException("BigInteger not invertible.");
        }
        return Nat.toBigInteger(length, create);
    }
    
    public static boolean modOddIsCoprime(final BigInteger m, BigInteger mod) {
        if (!m.testBit(0)) {
            throw new IllegalArgumentException("'M' must be odd");
        }
        if (m.signum() != 1) {
            throw new ArithmeticException("BigInteger: modulus not positive");
        }
        if (mod.signum() < 0 || mod.bitLength() > m.bitLength()) {
            mod = mod.mod(m);
        }
        final int bitLength = m.bitLength();
        return 0 != Mod.modOddIsCoprime(Nat.fromBigInteger(bitLength, m), Nat.fromBigInteger(bitLength, mod));
    }
    
    public static boolean modOddIsCoprimeVar(final BigInteger m, BigInteger mod) {
        if (!m.testBit(0)) {
            throw new IllegalArgumentException("'M' must be odd");
        }
        if (m.signum() != 1) {
            throw new ArithmeticException("BigInteger: modulus not positive");
        }
        if (mod.signum() < 0 || mod.bitLength() > m.bitLength()) {
            mod = mod.mod(m);
        }
        if (mod.equals(BigIntegers.ONE)) {
            return true;
        }
        final int bitLength = m.bitLength();
        return Mod.modOddIsCoprimeVar(Nat.fromBigInteger(bitLength, m), Nat.fromBigInteger(bitLength, mod));
    }
    
    public static int getUnsignedByteLength(final BigInteger bigInteger) {
        if (bigInteger.equals(BigIntegers.ZERO)) {
            return 1;
        }
        return (bigInteger.bitLength() + 7) / 8;
    }
    
    public static BigInteger createRandomBigInteger(final int n, final SecureRandom secureRandom) {
        return new BigInteger(1, createRandom(n, secureRandom));
    }
    
    public static BigInteger createRandomPrime(final int n, final int certainty, final SecureRandom secureRandom) {
        if (n < 2) {
            throw new IllegalArgumentException("bitLength < 2");
        }
        if (n == 2) {
            return (secureRandom.nextInt() < 0) ? BigIntegers.TWO : BigIntegers.THREE;
        }
        BigInteger add;
        do {
            final byte[] random = createRandom(n, secureRandom);
            final byte b = (byte)(1 << 7 - (8 * random.length - n));
            final byte[] array = random;
            final int n2 = 0;
            array[n2] |= b;
            final byte[] array2 = random;
            final int n3 = random.length - 1;
            array2[n3] |= 0x1;
            add = new BigInteger(1, random);
            if (n > BigIntegers.MAX_SMALL) {
                while (!add.gcd(BigIntegers.SMALL_PRIMES_PRODUCT).equals(BigIntegers.ONE)) {
                    add = add.add(BigIntegers.TWO);
                }
            }
        } while (!add.isProbablePrime(certainty));
        return add;
    }
    
    public static void writeUnsignedByteArray(final OutputStream outputStream, final BigInteger bigInteger) throws IOException {
        final byte[] byteArray = bigInteger.toByteArray();
        final int off = (byteArray[0] == 0) ? 1 : 0;
        outputStream.write(byteArray, off, byteArray.length - off);
    }
    
    private static byte[] createRandom(final int n, final SecureRandom secureRandom) throws IllegalArgumentException {
        if (n < 1) {
            throw new IllegalArgumentException("bitLength must be at least 1");
        }
        final int n2 = (n + 7) / 8;
        final byte[] bytes = new byte[n2];
        secureRandom.nextBytes(bytes);
        final int n3 = 8 * n2 - n;
        final byte[] array = bytes;
        final int n4 = 0;
        array[n4] &= (byte)(255 >>> n3);
        return bytes;
    }
    
    static {
        ZERO = BigInteger.valueOf(0L);
        ONE = BigInteger.valueOf(1L);
        TWO = BigInteger.valueOf(2L);
        THREE = BigInteger.valueOf(3L);
        SMALL_PRIMES_PRODUCT = new BigInteger("8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f73d893fa424cd2edc8636a6c3285e022b0e3866a565ae8108eed8591cd4fe8d2ce86165a978d719ebf647f362d33fca29cd179fb42401cbaf3df0c614056f9c8f3cfd51e474afb6bc6974f78db8aba8e9e517fded658591ab7502bd41849462f", 16);
        MAX_SMALL = BigInteger.valueOf(743L).bitLength();
    }
    
    public static class Cache
    {
        private final Map<BigInteger, Boolean> values;
        private final BigInteger[] preserve;
        private int preserveCounter;
        
        public Cache() {
            this.values = new WeakHashMap<BigInteger, Boolean>();
            this.preserve = new BigInteger[8];
            this.preserveCounter = 0;
        }
        
        public synchronized void add(final BigInteger bigInteger) {
            this.values.put(bigInteger, Boolean.TRUE);
            this.preserve[this.preserveCounter] = bigInteger;
            this.preserveCounter = (this.preserveCounter + 1) % this.preserve.length;
        }
        
        public synchronized boolean contains(final BigInteger bigInteger) {
            return this.values.containsKey(bigInteger);
        }
        
        public synchronized int size() {
            return this.values.size();
        }
        
        public synchronized void clear() {
            this.values.clear();
            for (int i = 0; i != this.preserve.length; ++i) {
                this.preserve[i] = null;
            }
        }
    }
}
