All Downloads are FREE. Search and download functionalities are using the official Maven repository.

lowentry.ue4.classes.internal.rsa.RsaBigInteger Maven / Gradle / Ivy

There is a newer version: 9.9.9.DELETED
Show newest version
package lowentry.ue4.classes.internal.rsa;


import java.security.SecureRandom;
import java.util.Arrays;


public class RsaBigInteger
{
	private static final SecureRandom		SECURE_RANDOM					= new SecureRandom();
	
	
	public final int						signum;
	public final int[]						mag;
	
	
	private int								bitLength						= 0;
	private int								lowestSetBit					= 0;
	private int								firstNonzeroIntNum				= 0;
	
	
	private final static long				LONG_MASK						= 0xffffffffL;
	private static final int				MAX_MAG_LENGTH					= (0x7fffffff / 32) + 1;
	private static final int				PRIME_SEARCH_BIT_LENGTH_LIMIT	= 500000000;
	private static final int				KARATSUBA_THRESHOLD				= 80;
	private static final int				TOOM_COOK_THRESHOLD				= 240;
	private static final int				KARATSUBA_SQUARE_THRESHOLD		= 128;
	private static final int				TOOM_COOK_SQUARE_THRESHOLD		= 216;
	public static final int					BURNIKEL_ZIEGLER_THRESHOLD		= 80;
	public static final int					BURNIKEL_ZIEGLER_OFFSET			= 40;
	private static final int				MULTIPLY_SQUARE_THRESHOLD		= 20;
	
	
	private static final int				DEFAULT_PRIME_CERTAINTY			= 100;
	
	
	private static final RsaBigInteger[]	cache_positive					= new RsaBigInteger[256];
	private static final RsaBigInteger[]	cache_negative					= new RsaBigInteger[32];
	
	public static final RsaBigInteger		ZERO							= new RsaBigInteger(new int[0], 0);
	public static final RsaBigInteger		NEGATIVE_ONE					= valueOf(-1);
	public static final RsaBigInteger		ONE								= valueOf(1);
	public static final RsaBigInteger		TWO								= valueOf(2);
	public static final RsaBigInteger		TEN								= valueOf(10);
	
	
	private static int[]					bnExpModThreshTable				= {7, 25, 81, 241, 673, 1793, 0x7fffffff};
	
	
	private RsaBigInteger(int[] val)
	{
		if((val == null) || (val.length == 0))
		{
			//throw new NumberFormatException("Zero length BigInteger");
			mag = new int[0];
			signum = 0;
			return;
		}
		
		if(val[0] < 0)
		{
			int[] newmag = makePositive(val);
			if(!checkRange(newmag))
			{
				signum = 0;
				mag = new int[0];
				return;
			}
			mag = newmag;
			signum = -1;
		}
		else
		{
			int[] newmag = trustedStripLeadingZeroInts(val);
			if(!checkRange(newmag))
			{
				signum = 0;
				mag = new int[0];
				return;
			}
			mag = newmag;
			signum = (mag.length == 0 ? 0 : 1);
		}
	}
	
	public RsaBigInteger(int signum, byte[] magnitude)
	{
		if((signum < -1) || (signum > 1))
		{
			//throw(new NumberFormatException("Invalid signum value"));
			this.mag = new int[0];
			this.signum = 0;
			return;
		}
		
		int[] mag = stripLeadingZeroBytes(magnitude);
		if(!checkRange(mag))
		{
			this.mag = new int[0];
			this.signum = 0;
			return;
		}
		
		if(mag.length == 0)
		{
			this.mag = mag;
			this.signum = 0;
		}
		else
		{
			if(signum == 0)
			{
				//throw(new NumberFormatException("signum-magnitude mismatch"));
				this.mag = new int[0];
				this.signum = 0;
				return;
			}
			this.mag = mag;
			this.signum = signum;
		}
	}
	
	private RsaBigInteger(int signum, int[] magnitude)
	{
		if((signum < -1) || (signum > 1))
		{
			//throw(new NumberFormatException("Invalid signum value"));
			this.mag = new int[0];
			this.signum = 0;
			return;
		}
		
		int[] mag = stripLeadingZeroInts(magnitude);
		if(!checkRange(mag))
		{
			this.mag = new int[0];
			this.signum = 0;
			return;
		}
		
		if(mag.length == 0)
		{
			this.mag = mag;
			this.signum = 0;
		}
		else
		{
			if(signum == 0)
			{
				//throw(new NumberFormatException("signum-magnitude mismatch"));
				this.mag = new int[0];
				this.signum = 0;
				return;
			}
			this.mag = mag;
			this.signum = signum;
		}
	}
	
	public RsaBigInteger(int[] magnitude, int signum)
	{
		if(!checkRange(magnitude))
		{
			this.signum = 0;
			this.mag = new int[0];
			return;
		}
		this.signum = (magnitude.length == 0 ? 0 : signum);
		this.mag = magnitude;
	}
	
	private RsaBigInteger(int numBits)
	{
		this(1, randomBits(numBits));
	}
	
	private RsaBigInteger(long val)
	{
		if(val < 0)
		{
			val = -val;
			signum = -1;
		}
		else
		{
			signum = 1;
		}
		
		int highWord = (int) sl(val, 32);
		if(highWord == 0)
		{
			mag = new int[1];
			mag[0] = (int) val;
		}
		else
		{
			mag = new int[2];
			mag[0] = highWord;
			mag[1] = (int) val;
		}
	}
	
	
	private static int si(int a, int b)
	{
		return (a >>> b);
	}
	private static long sl(long a, int b)
	{
		return (a >>> b);
	}
	
	
	private static void randomBytes(byte[] bytes)
	{
		SECURE_RANDOM.nextBytes(bytes);
	}
	
	private static byte[] randomBits(int numBits)
	{
		if(numBits < 0)
		{
			//throw new IllegalArgumentException("numBits must be non-negative");
			numBits = 0;
		}
		int numBytes = (int) (((long) numBits + 7) / 8);
		byte[] randomBits = new byte[numBytes];
		
		if(numBytes > 0)
		{
			randomBytes(randomBits);
			int excessBits = (8 * numBytes) - numBits;
			randomBits[0] &= (1 << (8 - excessBits)) - 1;
		}
		return randomBits;
	}
	
	public static RsaBigInteger probablePrime(int bitLength)
	{
		if(bitLength < 2)
		{
			//throw new ArithmeticException("bitLength < 2");
			return null;
		}
		return largePrime(bitLength, DEFAULT_PRIME_CERTAINTY);
	}
	
	
	private static RsaBigInteger largePrime(int bitLength, int certainty)
	{
		RsaBigInteger p;
		p = new RsaBigInteger(bitLength).setBit(bitLength - 1);
		if((p == null) || (p.mag.length <= 0))
		{
			return null;
		}
		p.mag[p.mag.length - 1] &= 0xfffffffe;
		
		if(bitLength > (PRIME_SEARCH_BIT_LENGTH_LIMIT + 1))
		{
			//throw new ArithmeticException("Prime search implementation restriction on bitLength");
			return null;
		}
		int searchLen = (bitLength / 20) * 64;
		
		RsaBitSieve searchSieve = new RsaBitSieve(p, searchLen);
		RsaBigInteger candidate = searchSieve.retrieve(p, certainty);
		
		while((candidate == null) || (candidate.bitLength() != bitLength))
		{
			p = p.add(RsaBigInteger.valueOf(2 * searchLen));
			if(p == null)
			{
				return null;
			}
			if(p.bitLength() != bitLength)
			{
				p = new RsaBigInteger(bitLength).setBit(bitLength - 1);
			}
			if((p == null) || (p.mag.length <= 0))
			{
				return null;
			}
			p.mag[p.mag.length - 1] &= 0xfffffffe;
			searchSieve = new RsaBitSieve(p, searchLen);
			candidate = searchSieve.retrieve(p, certainty);
		}
		return candidate;
	}
	
	
	public boolean primeToCertainty(int certainty)
	{
		int rounds = 0;
		int n = (Math.min(certainty, 0x7fffffff - 1) + 1) / 2;
		
		int sizeInBits = this.bitLength();
		if(sizeInBits < 100)
		{
			rounds = 50;
			rounds = n < rounds ? n : rounds;
			return passesMillerRabin(rounds);
		}
		
		if(sizeInBits < 256)
		{
			rounds = 27;
		}
		else if(sizeInBits < 512)
		{
			rounds = 15;
		}
		else if(sizeInBits < 768)
		{
			rounds = 8;
		}
		else if(sizeInBits < 1024)
		{
			rounds = 4;
		}
		else
		{
			rounds = 2;
		}
		rounds = n < rounds ? n : rounds;
		
		return passesMillerRabin(rounds) && passesLucasLehmer();
	}
	
	private boolean passesLucasLehmer()
	{
		RsaBigInteger thisPlusOne = this.add(ONE);
		if(thisPlusOne == null)
		{
			return true;
		}
		
		int d = 5;
		while(jacobiSymbol(d, this) != -1)
		{
			d = (d < 0) ? Math.abs(d) + 2 : -(d + 2);
		}
		
		RsaBigInteger u = lucasLehmerSequence(d, thisPlusOne, this);
		if(u == null)
		{
			return true;
		}
		
		RsaBigInteger umod = u.mod(this);
		if(umod == null)
		{
			return true;
		}
		
		return umod.equals(ZERO);
	}
	
	private static int jacobiSymbol(int p, RsaBigInteger n)
	{
		if((n == null) || (n.mag.length <= 0))
		{
			return -1;
		}
		
		if(p == 0)
		{
			return 0;
		}
		
		int j = 1;
		int u = n.mag[n.mag.length - 1];
		
		if(p < 0)
		{
			p = -p;
			int n8 = u & 7;
			if((n8 == 3) || (n8 == 7))
			{
				j = -j;
			}
		}
		
		while((p & 3) == 0)
		{
			p >>= 2;
		}
		if((p & 1) == 0)
		{
			p >>= 1;
			if(((u ^ (u >> 1)) & 2) != 0)
			{
				j = -j;
			}
		}
		if(p == 1)
		{
			return j;
		}
		
		if((p & u & 2) != 0)
		{
			j = -j;
		}
		
		RsaBigInteger uobj = n.mod(RsaBigInteger.valueOf(p));
		if(uobj == null)
		{
			return -1;
		}
		u = uobj.intValue();
		
		while(u != 0)
		{
			while((u & 3) == 0)
			{
				u >>= 2;
			}
			if((u & 1) == 0)
			{
				u >>= 1;
				if(((p ^ (p >> 1)) & 2) != 0)
				{
					j = -j;
				}
			}
			if(u == 1)
			{
				return j;
			}
			
			if(u < p)
			{
				return -1;
			}
			
			int t = u;
			u = p;
			p = t;
			if((u & p & 2) != 0)
			{
				j = -j;
			}
			
			u %= p;
		}
		return 0;
	}
	
	private static RsaBigInteger lucasLehmerSequence(int z, RsaBigInteger k, RsaBigInteger n)
	{
		if((k == null) || (n == null))
		{
			return null;
		}
		
		RsaBigInteger d = RsaBigInteger.valueOf(z);
		RsaBigInteger u = ONE;
		RsaBigInteger u2;
		RsaBigInteger v = ONE;
		RsaBigInteger v2;
		
		for(int i = k.bitLength() - 2; i >= 0; i--)
		{
			u2 = u.multiply(v);
			if(u2 == null)
			{
				return null;
			}
			
			u2 = u2.mod(n);
			if(u2 == null)
			{
				return null;
			}
			
			v2 = v.square();
			if(v2 == null)
			{
				return null;
			}
			
			RsaBigInteger usqr = u.square();
			if(usqr == null)
			{
				return null;
			}
			
			RsaBigInteger dmultUsql = d.multiply(usqr);
			if(dmultUsql == null)
			{
				return null;
			}
			
			v2 = v2.add(dmultUsql);
			if(v2 == null)
			{
				return null;
			}
			
			v2 = v2.mod(n);
			if(v2 == null)
			{
				return null;
			}
			
			if(v2.testBit(0))
			{
				v2 = v2.subtract(n);
				if(v2 == null)
				{
					return null;
				}
			}
			
			v2 = v2.shiftRight(1);
			if(v2 == null)
			{
				return null;
			}
			
			u = u2;
			v = v2;
			if(k.testBit(i))
			{
				u2 = u.add(v);
				if(u2 == null)
				{
					return null;
				}
				
				u2 = u2.mod(n);
				if(u2 == null)
				{
					return null;
				}
				
				if(u2.testBit(0))
				{
					u2 = u2.subtract(n);
					if(u2 == null)
					{
						return null;
					}
				}
				
				u2 = u2.shiftRight(1);
				if(u2 == null)
				{
					return null;
				}
				
				RsaBigInteger dmultU = d.multiply(u);
				if(dmultU == null)
				{
					return null;
				}
				
				v2 = v.add(dmultU);
				if(v2 == null)
				{
					return null;
				}
				
				v2 = v2.mod(n);
				if(v2 == null)
				{
					return null;
				}
				
				if(v2.testBit(0))
				{
					v2 = v2.subtract(n);
					if(v2 == null)
					{
						return null;
					}
				}
				
				v2 = v2.shiftRight(1);
				if(v2 == null)
				{
					return null;
				}
				
				u = u2;
				v = v2;
			}
		}
		return u;
	}
	
	private boolean passesMillerRabin(int iterations)
	{
		RsaBigInteger thisMinusOne = this.subtract(ONE);
		if(thisMinusOne == null)
		{
			return true;
		}
		RsaBigInteger m = thisMinusOne;
		int a = m.getLowestSetBit();
		m = m.shiftRight(a);
		if(m == null)
		{
			return true;
		}
		
		for(int i = 0; i < iterations; i++)
		{
			RsaBigInteger b = new RsaBigInteger(this.bitLength());
			while((b.compareTo(ONE) <= 0) || (b.compareTo(this) >= 0))
			{
				b = new RsaBigInteger(this.bitLength());
			}
			
			int j = 0;
			RsaBigInteger z = b.modPow(m, this);
			if(z == null)
			{
				return true;
			}
			
			while(!(((j == 0) && z.equals(ONE)) || z.equals(thisMinusOne)))
			{
				if(((j > 0) && z.equals(ONE)) || (++j == a))
				{
					return false;
				}
				z = z.modPow(TWO, this);
				if(z == null)
				{
					return true;
				}
			}
		}
		return true;
	}
	
	private static boolean checkRange(int[] mag)
	{
		if((mag == null) || (mag.length > MAX_MAG_LENGTH) || ((mag.length == MAX_MAG_LENGTH) && (mag[0] < 0)))
		{
			//throw new ArithmeticException("BigInteger would overflow supported range");
			return false;
		}
		return true;
	}
	
	
	public static RsaBigInteger valueOf(long val)
	{
		if(val == 0)
		{
			return ZERO;
		}
		if((val > 0) && (val <= cache_positive.length))
		{
			synchronized(cache_positive)
			{
				RsaBigInteger bigint = cache_positive[((int) val) - 1];
				if(bigint == null)
				{
					bigint = new RsaBigInteger(val);
					cache_positive[((int) val) - 1] = bigint;
				}
				return bigint;
			}
		}
		if((val < 0) && (-val <= cache_negative.length))
		{
			synchronized(cache_negative)
			{
				RsaBigInteger bigint = cache_negative[((int) -val) - 1];
				if(bigint == null)
				{
					bigint = new RsaBigInteger(val);
					cache_negative[((int) -val) - 1] = bigint;
				}
				return bigint;
			}
		}
		return new RsaBigInteger(val);
	}
	
	private static RsaBigInteger valueOf(int val[])
	{
		if((val == null) || (val.length <= 0))
		{
			return ZERO;
		}
		return (val[0] > 0 ? new RsaBigInteger(val, 1) : new RsaBigInteger(val));
	}
	
	
	public RsaBigInteger add(RsaBigInteger val)
	{
		if(val == null)
		{
			return null;
		}
		if(val.signum == 0)
		{
			return this;
		}
		if(signum == 0)
		{
			return val;
		}
		if(val.signum == signum)
		{
			return new RsaBigInteger(add(mag, val.mag), signum);
		}
		
		int cmp = compareMagnitude(val);
		if(cmp == 0)
		{
			return ZERO;
		}
		int[] resultMag = (cmp > 0 ? subtract(mag, val.mag) : subtract(val.mag, mag));
		resultMag = trustedStripLeadingZeroInts(resultMag);
		
		return new RsaBigInteger(resultMag, cmp == signum ? 1 : -1);
	}
	
	private static int[] add(int[] x, int[] y)
	{
		if((x == null) || (y == null))
		{
			return new int[0];
		}
		
		if(x.length < y.length)
		{
			int[] tmp = x;
			x = y;
			y = tmp;
		}
		
		int xIndex = x.length;
		int yIndex = y.length;
		int result[] = new int[xIndex];
		long sum = 0;
		if(yIndex == 1)
		{
			xIndex--;
			sum = (x[xIndex] & LONG_MASK) + (y[0] & LONG_MASK);
			result[xIndex] = (int) sum;
		}
		else
		{
			while(yIndex > 0)
			{
				xIndex--;
				yIndex--;
				sum = (x[xIndex] & LONG_MASK) + (y[yIndex] & LONG_MASK) + sl(sum, 32);
				result[xIndex] = (int) sum;
			}
		}
		
		boolean carry = (sl(sum, 32) != 0);
		while((xIndex > 0) && carry)
		{
			xIndex--;
			carry = ((result[xIndex] = x[xIndex] + 1) == 0);
		}
		
		while(xIndex > 0)
		{
			xIndex--;
			result[xIndex] = x[xIndex];
		}
		
		if(carry)
		{
			int[] bigger = new int[result.length + 1];
			System.arraycopy(result, 0, bigger, 1, result.length);
			bigger[0] = 0x01;
			return bigger;
		}
		return result;
	}
	
	public RsaBigInteger subtract(RsaBigInteger val)
	{
		if(val == null)
		{
			return null;
		}
		if(val.signum == 0)
		{
			return this;
		}
		if(signum == 0)
		{
			return val.negate();
		}
		if(val.signum != signum)
		{
			return new RsaBigInteger(add(mag, val.mag), signum);
		}
		
		int cmp = compareMagnitude(val);
		if(cmp == 0)
		{
			return ZERO;
		}
		int[] resultMag = (cmp > 0 ? subtract(mag, val.mag) : subtract(val.mag, mag));
		resultMag = trustedStripLeadingZeroInts(resultMag);
		return new RsaBigInteger(resultMag, cmp == signum ? 1 : -1);
	}
	
	private static int[] subtract(int[] big, int[] little)
	{
		if((big == null) || (little == null))
		{
			return new int[0];
		}
		
		int bigIndex = big.length;
		int result[] = new int[bigIndex];
		int littleIndex = little.length;
		long difference = 0;
		
		while(littleIndex > 0)
		{
			bigIndex--;
			littleIndex--;
			difference = ((big[bigIndex] & LONG_MASK) - (little[littleIndex] & LONG_MASK)) + (difference >> 32);
			result[bigIndex] = (int) difference;
		}
		
		boolean borrow = ((difference >> 32) != 0);
		while((bigIndex > 0) && borrow)
		{
			bigIndex--;
			borrow = ((result[bigIndex] = big[bigIndex] - 1) == -1);
		}
		
		while(bigIndex > 0)
		{
			bigIndex--;
			result[bigIndex] = big[bigIndex];
		}
		return result;
	}
	
	public RsaBigInteger multiply(RsaBigInteger val)
	{
		if(val == null)
		{
			return null;
		}
		
		if((val.signum == 0) || (signum == 0))
		{
			return ZERO;
		}
		
		int xlen = mag.length;
		
		if((val == this) && (xlen > MULTIPLY_SQUARE_THRESHOLD))
		{
			return square();
		}
		
		int ylen = val.mag.length;
		
		if((xlen < KARATSUBA_THRESHOLD) || (ylen < KARATSUBA_THRESHOLD))
		{
			int resultSign = signum == val.signum ? 1 : -1;
			if(val.mag.length == 1)
			{
				return multiplyByInt(mag, val.mag[0], resultSign);
			}
			if(mag.length == 1)
			{
				return multiplyByInt(val.mag, mag[0], resultSign);
			}
			int[] result = multiplyToLen(mag, xlen, val.mag, ylen, null);
			result = trustedStripLeadingZeroInts(result);
			return new RsaBigInteger(result, resultSign);
		}
		else
		{
			if((xlen < TOOM_COOK_THRESHOLD) && (ylen < TOOM_COOK_THRESHOLD))
			{
				return multiplyKaratsuba(this, val);
			}
			else
			{
				return multiplyToomCook3(this, val);
			}
		}
	}
	
	private static int integerBitCount(int i)
	{
		i = i - (si(i, 1) & 0x55555555);
		i = (i & 0x33333333) + (si(i, 2) & 0x33333333);
		i = (i + si(i, 4)) & 0x0f0f0f0f;
		i = i + si(i, 8);
		i = i + si(i, 16);
		return (i & 0x3f);
	}
	
	private static int integerNumberOfTrailingZeros(int i)
	{
		int y;
		if(i == 0)
		{
			return 32;
		}
		int n = 31;
		y = i << 16;
		if(y != 0)
		{
			n = n - 16;
			i = y;
		}
		y = i << 8;
		if(y != 0)
		{
			n = n - 8;
			i = y;
		}
		y = i << 4;
		if(y != 0)
		{
			n = n - 4;
			i = y;
		}
		y = i << 2;
		if(y != 0)
		{
			n = n - 2;
			i = y;
		}
		return (n - si((i << 1), 31));
	}
	
	private static int integerNumberOfLeadingZeros(int i)
	{
		if(i == 0)
		{
			return 32;
		}
		int n = 1;
		if(si(i, 16) == 0)
		{
			n += 16;
			i <<= 16;
		}
		if(si(i, 24) == 0)
		{
			n += 8;
			i <<= 8;
		}
		if(si(i, 28) == 0)
		{
			n += 4;
			i <<= 4;
		}
		if(si(i, 30) == 0)
		{
			n += 2;
			i <<= 2;
		}
		n -= si(i, 31);
		return n;
	}
	
	private static RsaBigInteger multiplyByInt(int[] x, int y, int sign)
	{
		if(x == null)
		{
			return null;
		}
		if(integerBitCount(y) == 1)
		{
			return new RsaBigInteger(shiftLeft(x, integerNumberOfTrailingZeros(y)), sign);
		}
		int xlen = x.length;
		int[] rmag = new int[xlen + 1];
		long carry = 0;
		long yl = y & LONG_MASK;
		int rstart = rmag.length - 1;
		for(int i = xlen - 1; i >= 0; i--)
		{
			long product = ((x[i] & LONG_MASK) * yl) + carry;
			rmag[rstart--] = (int) product;
			carry = sl(product, 32);
		}
		if(carry == 0L)
		{
			rmag = Arrays.copyOfRange(rmag, 1, rmag.length);
		}
		else
		{
			rmag[rstart] = (int) carry;
		}
		return new RsaBigInteger(rmag, sign);
	}
	
	private int[] multiplyToLen(int[] x, int xlen, int[] y, int ylen, int[] z)
	{
		if((x == null) || (y == null))
		{
			return new int[0];
		}
		
		int xstart = xlen - 1;
		int ystart = ylen - 1;
		
		if((z == null) || (z.length < (xlen + ylen)))
		{
			z = new int[xlen + ylen];
		}
		
		long carry = 0;
		for(int j = ystart, k = ystart + 1 + xstart; j >= 0; j--, k--)
		{
			long product = ((y[j] & LONG_MASK) * (x[xstart] & LONG_MASK)) + carry;
			z[k] = (int) product;
			carry = sl(product, 32);
		}
		z[xstart] = (int) carry;
		
		for(int i = xstart - 1; i >= 0; i--)
		{
			carry = 0;
			for(int j = ystart, k = ystart + 1 + i; j >= 0; j--, k--)
			{
				long product = ((y[j] & LONG_MASK) * (x[i] & LONG_MASK)) + (z[k] & LONG_MASK) + carry;
				z[k] = (int) product;
				carry = sl(product, 32);
			}
			z[i] = (int) carry;
		}
		return z;
	}
	
	private static RsaBigInteger multiplyKaratsuba(RsaBigInteger x, RsaBigInteger y)
	{
		if((x == null) || (y == null))
		{
			return null;
		}
		
		int xlen = x.mag.length;
		int ylen = y.mag.length;
		
		int half = (Math.max(xlen, ylen) + 1) / 2;
		
		RsaBigInteger xl = x.getLower(half);
		if(xl == null)
		{
			return null;
		}
		
		RsaBigInteger xh = x.getUpper(half);
		if(xh == null)
		{
			return null;
		}
		
		RsaBigInteger yl = y.getLower(half);
		if(yl == null)
		{
			return null;
		}
		
		RsaBigInteger yh = y.getUpper(half);
		if(yh == null)
		{
			return null;
		}
		
		
		RsaBigInteger p1 = xh.multiply(yh);
		if(p1 == null)
		{
			return null;
		}
		
		RsaBigInteger p2 = xl.multiply(yl);
		if(p2 == null)
		{
			return null;
		}
		
		
		RsaBigInteger p3 = xh.add(xl);
		if(p3 == null)
		{
			return null;
		}
		
		RsaBigInteger yhAddY1 = yh.add(yl);
		if(yhAddY1 == null)
		{
			return null;
		}
		
		p3 = p3.multiply(yhAddY1);
		if(p3 == null)
		{
			return null;
		}
		
		RsaBigInteger p3sub = p3.subtract(p1);
		if(p3sub == null)
		{
			return null;
		}
		p3sub = p3sub.subtract(p2);
		if(p3sub == null)
		{
			return null;
		}
		
		RsaBigInteger result = p1.shiftLeft(32 * half);
		if(result == null)
		{
			return null;
		}
		result = result.add(p3sub);
		if(result == null)
		{
			return null;
		}
		result = result.shiftLeft(32 * half);
		if(result == null)
		{
			return null;
		}
		result = result.add(p2);
		if(result == null)
		{
			return null;
		}
		
		if(x.signum != y.signum)
		{
			return result.negate();
		}
		else
		{
			return result;
		}
	}
	
	private static RsaBigInteger multiplyToomCook3(RsaBigInteger a, RsaBigInteger b)
	{
		if((a == null) || (b == null))
		{
			return null;
		}
		
		int alen = a.mag.length;
		int blen = b.mag.length;
		
		int largest = Math.max(alen, blen);
		
		int k = (largest + 2) / 3;
		
		int r = largest - (2 * k);
		
		
		RsaBigInteger a0, a1, a2, b0, b1, b2;
		
		a2 = a.getToomSlice(k, r, 0, largest);
		if(a2 == null)
		{
			return null;
		}
		
		a1 = a.getToomSlice(k, r, 1, largest);
		if(a1 == null)
		{
			return null;
		}
		
		a0 = a.getToomSlice(k, r, 2, largest);
		if(a0 == null)
		{
			return null;
		}
		
		b2 = b.getToomSlice(k, r, 0, largest);
		if(b2 == null)
		{
			return null;
		}
		
		b1 = b.getToomSlice(k, r, 1, largest);
		if(b1 == null)
		{
			return null;
		}
		
		b0 = b.getToomSlice(k, r, 2, largest);
		if(b0 == null)
		{
			return null;
		}
		
		
		RsaBigInteger v0, v1, v2, vm1, vinf, t1, t2, tm1, da1, db1;
		
		v0 = a0.multiply(b0);
		if(v0 == null)
		{
			return null;
		}
		
		da1 = a2.add(a0);
		if(da1 == null)
		{
			return null;
		}
		
		db1 = b2.add(b0);
		if(db1 == null)
		{
			return null;
		}
		
		RsaBigInteger db1SubB1 = db1.subtract(b1);
		if(db1SubB1 == null)
		{
			return null;
		}
		
		vm1 = da1.subtract(a1);
		if(vm1 == null)
		{
			return null;
		}
		vm1 = vm1.multiply(db1SubB1);
		if(vm1 == null)
		{
			return null;
		}
		
		da1 = da1.add(a1);
		if(da1 == null)
		{
			return null;
		}
		
		db1 = db1.add(b1);
		if(db1 == null)
		{
			return null;
		}
		
		v1 = da1.multiply(db1);
		if(v1 == null)
		{
			return null;
		}
		
		RsaBigInteger v2inner = db1.add(b2);
		if(v2inner == null)
		{
			return null;
		}
		v2inner = v2inner.shiftLeft(1);
		if(v2inner == null)
		{
			return null;
		}
		v2inner = v2inner.subtract(b0);
		if(v2inner == null)
		{
			return null;
		}
		
		v2 = da1.add(a2);
		if(v2 == null)
		{
			return null;
		}
		v2 = v2.shiftLeft(1);
		if(v2 == null)
		{
			return null;
		}
		v2 = v2.subtract(a0);
		if(v2 == null)
		{
			return null;
		}
		v2 = v2.multiply(v2inner);
		if(v2 == null)
		{
			return null;
		}
		
		vinf = a2.multiply(b2);
		if(vinf == null)
		{
			return null;
		}
		
		
		t2 = v2.subtract(vm1);
		if(t2 == null)
		{
			return null;
		}
		t2 = t2.exactDivideBy3();
		if(t2 == null)
		{
			return null;
		}
		
		tm1 = v1.subtract(vm1);
		if(tm1 == null)
		{
			return null;
		}
		tm1 = v1.shiftRight(1);
		if(tm1 == null)
		{
			return null;
		}
		
		t1 = v1.subtract(v0);
		if(t1 == null)
		{
			return null;
		}
		
		t2 = t2.subtract(t1);
		if(t2 == null)
		{
			return null;
		}
		t2 = t2.shiftRight(1);
		if(t2 == null)
		{
			return null;
		}
		
		t1 = t1.subtract(tm1);
		if(t1 == null)
		{
			return null;
		}
		t1 = t1.subtract(vinf);
		if(t1 == null)
		{
			return null;
		}
		
		RsaBigInteger vinfShiftleft = vinf.shiftLeft(1);
		if(vinfShiftleft == null)
		{
			return null;
		}
		
		t2 = t2.subtract(vinfShiftleft);
		if(t2 == null)
		{
			return null;
		}
		
		tm1 = tm1.subtract(t2);
		if(tm1 == null)
		{
			return null;
		}
		
		int ss = k * 32;
		
		RsaBigInteger result = vinf.shiftLeft(ss);
		if(result == null)
		{
			return null;
		}
		result = result.add(t2);
		if(result == null)
		{
			return null;
		}
		result = result.shiftLeft(ss);
		if(result == null)
		{
			return null;
		}
		result = result.add(t1);
		if(result == null)
		{
			return null;
		}
		result = result.shiftLeft(ss);
		if(result == null)
		{
			return null;
		}
		result = result.add(tm1);
		if(result == null)
		{
			return null;
		}
		result = result.shiftLeft(ss);
		if(result == null)
		{
			return null;
		}
		result = result.add(v0);
		if(result == null)
		{
			return null;
		}
		
		if(a.signum != b.signum)
		{
			return result.negate();
		}
		else
		{
			return result;
		}
	}
	
	private RsaBigInteger getToomSlice(int lowerSize, int upperSize, int slice, int fullsize)
	{
		int start, end, sliceSize, len, offset;
		
		len = mag.length;
		offset = fullsize - len;
		
		if(slice == 0)
		{
			start = 0 - offset;
			end = upperSize - 1 - offset;
		}
		else
		{
			start = (upperSize + ((slice - 1) * lowerSize)) - offset;
			end = (start + lowerSize) - 1;
		}
		
		if(start < 0)
		{
			start = 0;
		}
		if(end < 0)
		{
			return ZERO;
		}
		
		sliceSize = (end - start) + 1;
		
		if(sliceSize <= 0)
		{
			return ZERO;
		}
		
		if((start == 0) && (sliceSize >= len))
		{
			return this.abs();
		}
		
		int intSlice[] = new int[sliceSize];
		System.arraycopy(mag, start, intSlice, 0, sliceSize);
		
		return new RsaBigInteger(trustedStripLeadingZeroInts(intSlice), 1);
	}
	
	private RsaBigInteger exactDivideBy3()
	{
		int len = mag.length;
		int[] result = new int[len];
		long x, w, q, borrow;
		borrow = 0L;
		for(int i = len - 1; i >= 0; i--)
		{
			x = (mag[i] & LONG_MASK);
			w = x - borrow;
			if(borrow > x)
			{
				borrow = 1L;
			}
			else
			{
				borrow = 0L;
			}
			
			q = (w * 0xAAAAAAABL) & LONG_MASK;
			result[i] = (int) q;
			
			if(q >= 0x55555556L)
			{
				borrow++;
				if(q >= 0xAAAAAAABL)
				{
					borrow++;
				}
			}
		}
		result = trustedStripLeadingZeroInts(result);
		return new RsaBigInteger(result, signum);
	}
	
	private RsaBigInteger getLower(int n)
	{
		int len = mag.length;
		
		if(len <= n)
		{
			return abs();
		}
		
		int lowerInts[] = new int[n];
		System.arraycopy(mag, len - n, lowerInts, 0, n);
		
		return new RsaBigInteger(trustedStripLeadingZeroInts(lowerInts), 1);
	}
	
	private RsaBigInteger getUpper(int n)
	{
		int len = mag.length;
		
		if(len <= n)
		{
			return ZERO;
		}
		
		int upperLen = len - n;
		int upperInts[] = new int[upperLen];
		System.arraycopy(mag, 0, upperInts, 0, upperLen);
		
		return new RsaBigInteger(trustedStripLeadingZeroInts(upperInts), 1);
	}
	
	
	private RsaBigInteger square()
	{
		if(signum == 0)
		{
			return ZERO;
		}
		int len = mag.length;
		
		if(len < KARATSUBA_SQUARE_THRESHOLD)
		{
			int[] z = squareToLen(mag, len, null);
			return new RsaBigInteger(trustedStripLeadingZeroInts(z), 1);
		}
		else
		{
			if(len < TOOM_COOK_SQUARE_THRESHOLD)
			{
				return squareKaratsuba();
			}
			else
			{
				return squareToomCook3();
			}
		}
	}
	
	private static final int[] squareToLen(int[] x, int len, int[] z)
	{
		if(x == null)
		{
			return new int[0];
		}
		
		int zlen = len << 1;
		if((z == null) || (z.length < zlen))
		{
			z = new int[zlen];
		}
		
		int lastProductLowWord = 0;
		for(int j = 0, i = 0; j < len; j++)
		{
			long piece = (x[j] & LONG_MASK);
			long product = piece * piece;
			z[i++] = (lastProductLowWord << 31) | (int) sl(product, 33);
			z[i++] = (int) sl(product, 1);
			lastProductLowWord = (int) product;
		}
		
		for(int i = len, offset = 1; i > 0; i--, offset += 2)
		{
			int t = x[i - 1];
			t = mulAdd(z, x, offset, i - 1, t);
			addOne(z, offset - 1, i, t);
		}
		
		primitiveLeftShift(z, zlen, 1);
		z[zlen - 1] |= x[len - 1] & 1;
		
		return z;
	}
	
	private RsaBigInteger squareKaratsuba()
	{
		int half = (mag.length + 1) / 2;
		
		RsaBigInteger xl = getLower(half);
		if(xl == null)
		{
			return null;
		}
		
		RsaBigInteger xh = getUpper(half);
		if(xh == null)
		{
			return null;
		}
		
		RsaBigInteger xhs = xh.square();
		if(xhs == null)
		{
			return null;
		}
		
		RsaBigInteger xls = xl.square();
		if(xls == null)
		{
			return null;
		}
		
		RsaBigInteger resultA = xhs.add(xls);
		if(resultA == null)
		{
			return null;
		}
		
		RsaBigInteger resultB = xl.add(xh);
		if(resultB == null)
		{
			return null;
		}
		resultB = resultB.square();
		if(resultB == null)
		{
			return null;
		}
		resultB = resultB.subtract(resultA);
		if(resultB == null)
		{
			return null;
		}
		
		RsaBigInteger result = xhs.shiftLeft(half * 32);
		if(result == null)
		{
			return null;
		}
		result = result.add(resultB);
		if(result == null)
		{
			return null;
		}
		result = result.shiftLeft(half * 32);
		if(result == null)
		{
			return null;
		}
		result = result.add(xls);
		return result;
	}
	
	private RsaBigInteger squareToomCook3()
	{
		int len = mag.length;
		int k = (len + 2) / 3;
		int r = len - (2 * k);
		
		
		RsaBigInteger a0, a1, a2;
		
		a2 = getToomSlice(k, r, 0, len);
		if(a2 == null)
		{
			return null;
		}
		
		a1 = getToomSlice(k, r, 1, len);
		if(a1 == null)
		{
			return null;
		}
		
		a0 = getToomSlice(k, r, 2, len);
		if(a0 == null)
		{
			return null;
		}
		
		
		RsaBigInteger v0, v1, v2, vm1, vinf, t1, t2, tm1, da1;
		
		v0 = a0.square();
		if(v0 == null)
		{
			return null;
		}
		
		da1 = a2.add(a0);
		if(da1 == null)
		{
			return null;
		}
		
		vm1 = da1.subtract(a1);
		if(vm1 == null)
		{
			return null;
		}
		vm1 = vm1.square();
		if(vm1 == null)
		{
			return null;
		}
		
		da1 = da1.add(a1);
		if(da1 == null)
		{
			return null;
		}
		
		v1 = da1.square();
		if(v1 == null)
		{
			return null;
		}
		
		vinf = a2.square();
		if(vinf == null)
		{
			return null;
		}
		
		v2 = da1.add(a2);
		if(v2 == null)
		{
			return null;
		}
		v2 = v2.shiftLeft(1);
		if(v2 == null)
		{
			return null;
		}
		v2 = v2.subtract(a0);
		if(v2 == null)
		{
			return null;
		}
		v2 = v2.square();
		if(v2 == null)
		{
			return null;
		}
		
		
		t2 = v2.subtract(vm1);
		if(t2 == null)
		{
			return null;
		}
		t2 = t2.exactDivideBy3();
		if(t2 == null)
		{
			return null;
		}
		
		tm1 = v1.subtract(vm1);
		if(tm1 == null)
		{
			return null;
		}
		tm1 = tm1.shiftRight(1);
		if(tm1 == null)
		{
			return null;
		}
		
		t1 = v1.subtract(v0);
		if(t1 == null)
		{
			return null;
		}
		
		t2 = t2.subtract(t1);
		if(t2 == null)
		{
			return null;
		}
		t2 = t2.shiftRight(1);
		if(t2 == null)
		{
			return null;
		}
		
		t1 = t1.subtract(tm1);
		if(t1 == null)
		{
			return null;
		}
		t1 = t1.subtract(vinf);
		if(t1 == null)
		{
			return null;
		}
		
		RsaBigInteger vinfShiftLeft = vinf.shiftLeft(1);
		if(vinfShiftLeft == null)
		{
			return null;
		}
		
		t2 = t2.subtract(vinfShiftLeft);
		if(t2 == null)
		{
			return null;
		}
		
		tm1 = tm1.subtract(t2);
		if(tm1 == null)
		{
			return null;
		}
		
		int ss = k * 32;
		
		RsaBigInteger result = vinf.shiftLeft(ss);
		if(result == null)
		{
			return null;
		}
		result = result.add(t2);
		if(result == null)
		{
			return null;
		}
		result = result.shiftLeft(ss);
		if(result == null)
		{
			return null;
		}
		result = result.add(t1);
		if(result == null)
		{
			return null;
		}
		result = result.shiftLeft(ss);
		if(result == null)
		{
			return null;
		}
		result = result.add(tm1);
		if(result == null)
		{
			return null;
		}
		result = result.shiftLeft(ss);
		if(result == null)
		{
			return null;
		}
		result = result.add(v0);
		return result;
	}
	
	
	public RsaBigInteger remainder(RsaBigInteger val)
	{
		if(val == null)
		{
			return null;
		}
		if((val.mag.length < BURNIKEL_ZIEGLER_THRESHOLD) || ((mag.length - val.mag.length) < BURNIKEL_ZIEGLER_OFFSET))
		{
			return remainderKnuth(val);
		}
		else
		{
			return remainderBurnikelZiegler(val);
		}
	}
	
	private RsaBigInteger remainderKnuth(RsaBigInteger val)
	{
		if(val == null)
		{
			return null;
		}
		RsaMutableBigInteger q = new RsaMutableBigInteger();
		RsaMutableBigInteger a = new RsaMutableBigInteger(this.mag);
		RsaMutableBigInteger b = new RsaMutableBigInteger(val.mag);
		RsaMutableBigInteger result = a.divideKnuth(b, q);
		if(result == null)
		{
			return null;
		}
		return result.toBigInteger(this.signum);
	}
	
	private RsaBigInteger remainderBurnikelZiegler(RsaBigInteger val)
	{
		if(val == null)
		{
			return null;
		}
		RsaBigInteger[] result = divideAndRemainderBurnikelZiegler(val);
		if((result == null) || (result.length <= 1))
		{
			return null;
		}
		return result[1];
	}
	
	private RsaBigInteger[] divideAndRemainderBurnikelZiegler(RsaBigInteger val)
	{
		if(val == null)
		{
			return new RsaBigInteger[0];
		}
		RsaMutableBigInteger q = new RsaMutableBigInteger();
		RsaMutableBigInteger r = new RsaMutableBigInteger(this).divideAndRemainderBurnikelZiegler(new RsaMutableBigInteger(val), q);
		if(r == null)
		{
			return new RsaBigInteger[0];
		}
		RsaBigInteger qBigInt = q.isZero() ? ZERO : q.toBigInteger(signum * val.signum);
		RsaBigInteger rBigInt = r.isZero() ? ZERO : r.toBigInteger(signum);
		return new RsaBigInteger[]{qBigInt, rBigInt};
	}
	
	
	public RsaBigInteger gcd(RsaBigInteger val)
	{
		if(val == null)
		{
			return null;
		}
		if(val.signum == 0)
		{
			return this.abs();
		}
		else if(this.signum == 0)
		{
			return val.abs();
		}
		
		RsaMutableBigInteger a = new RsaMutableBigInteger(this);
		RsaMutableBigInteger b = new RsaMutableBigInteger(val);
		
		RsaMutableBigInteger result = a.hybridGCD(b);
		if(result == null)
		{
			return null;
		}
		
		return result.toBigInteger(1);
	}
	
	public static int bitLengthForInt(int n)
	{
		return 32 - integerNumberOfLeadingZeros(n);
	}
	
	private static int[] leftShift(int[] a, int len, int n)
	{
		if((a == null) || (a.length <= 1))
		{
			return new int[0];
		}
		
		if(len > a.length)
		{
			len = a.length;
		}
		
		int nInts = si(n, 5);
		int nBits = n & 0x1F;
		int bitsInHighWord = bitLengthForInt(a[0]);
		
		if(n <= (32 - bitsInHighWord))
		{
			primitiveLeftShift(a, len, nBits);
			return a;
		}
		else
		{
			if(nBits <= (32 - bitsInHighWord))
			{
				int result[] = new int[nInts + len];
				System.arraycopy(a, 0, result, 0, len);
				primitiveLeftShift(result, result.length, nBits);
				return result;
			}
			else
			{
				int result[] = new int[nInts + len + 1];
				System.arraycopy(a, 0, result, 0, len);
				primitiveRightShift(result, result.length, 32 - nBits);
				return result;
			}
		}
	}
	
	private static void primitiveRightShift(int[] a, int len, int n)
	{
		if((a == null) || (a.length <= 1))
		{
			return;
		}
		
		if(len > a.length)
		{
			len = a.length;
		}
		
		int n2 = 32 - n;
		for(int i = len - 1, c = a[i]; i > 0; i--)
		{
			int b = c;
			c = a[i - 1];
			a[i] = (c << n2) | si(b, n);
		}
		a[0] = si(a[0], n);
	}
	
	private static void primitiveLeftShift(int[] a, int len, int n)
	{
		if((a == null) || (a.length <= 1))
		{
			return;
		}
		
		if(len > a.length)
		{
			len = a.length;
		}
		
		if((len == 0) || (n == 0))
		{
			return;
		}
		
		int n2 = 32 - n;
		for(int i = 0, c = a[i], m = (i + len) - 1; i < m; i++)
		{
			int b = c;
			c = a[i + 1];
			a[i] = (b << n) | si(c, n2);
		}
		a[len - 1] <<= n;
	}
	
	private static int bitLength(int[] val, int len)
	{
		if((val == null) || (val.length <= 1))
		{
			return 0;
		}
		if(len == 0)
		{
			return 0;
		}
		return ((len - 1) << 5) + bitLengthForInt(val[0]);
	}
	
	private RsaBigInteger abs()
	{
		return (signum >= 0 ? this : this.negate());
	}
	
	private RsaBigInteger negate()
	{
		return new RsaBigInteger(this.mag, -this.signum);
	}
	
	
	private RsaBigInteger mod(RsaBigInteger m)
	{
		if(m == null)
		{
			return null;
		}
		if(m.signum <= 0)
		{
			//throw new ArithmeticException("BigInteger: modulus not positive");
			return null;
		}
		
		RsaBigInteger result = this.remainder(m);
		if(result == null)
		{
			return null;
		}
		return (result.signum >= 0 ? result : result.add(m));
	}
	
	public RsaBigInteger modPow(RsaBigInteger exponent, RsaBigInteger m)
	{
		if((exponent == null) || (m == null))
		{
			return null;
		}
		if(m.signum <= 0)
		{
			//throw new ArithmeticException("BigInteger: modulus not positive");
			return null;
		}
		
		if(exponent.signum == 0)
		{
			return (m.equals(ONE) ? ZERO : ONE);
		}
		
		if(this.equals(ONE))
		{
			return (m.equals(ONE) ? ZERO : ONE);
		}
		
		if(this.equals(ZERO) && (exponent.signum >= 0))
		{
			return ZERO;
		}
		
		if(this.equals(NEGATIVE_ONE) && (!exponent.testBit(0)))
		{
			return (m.equals(ONE) ? ZERO : ONE);
		}
		
		boolean invertResult = (exponent.signum < 0);
		if(invertResult)
		{
			exponent = exponent.negate();
			if(exponent == null)
			{
				return null;
			}
		}
		
		RsaBigInteger base = ((this.signum < 0) || (this.compareTo(m) >= 0) ? this.mod(m) : this);
		if(base == null)
		{
			return null;
		}
		RsaBigInteger result;
		if(m.testBit(0))
		{
			result = base.oddModPow(exponent, m);
			if(result == null)
			{
				return null;
			}
		}
		else
		{
			int p = m.getLowestSetBit();
			
			RsaBigInteger m1 = m.shiftRight(p);
			if(m1 == null)
			{
				return null;
			}
			
			RsaBigInteger m2 = ONE.shiftLeft(p);
			if(m2 == null)
			{
				return null;
			}
			
			RsaBigInteger base2 = ((this.signum < 0) || (this.compareTo(m1) >= 0) ? this.mod(m1) : this);
			if(base2 == null)
			{
				return null;
			}
			
			RsaBigInteger a1 = (m1.equals(ONE) ? ZERO : base2.oddModPow(exponent, m1));
			if(a1 == null)
			{
				return null;
			}
			
			RsaBigInteger a2 = base.modPow2(exponent, p);
			if(a2 == null)
			{
				return null;
			}
			
			RsaBigInteger y1 = m2.modInverse(m1);
			if(y1 == null)
			{
				return null;
			}
			
			RsaBigInteger y2 = m1.modInverse(m2);
			if(y2 == null)
			{
				return null;
			}
			
			if(m.mag.length < (MAX_MAG_LENGTH / 2))
			{
				RsaBigInteger resultInner = a2.multiply(m1);
				if(resultInner == null)
				{
					return null;
				}
				resultInner = resultInner.multiply(y2);
				if(resultInner == null)
				{
					return null;
				}
				
				result = a1.multiply(m2);
				if(result == null)
				{
					return null;
				}
				result = result.multiply(y1);
				if(result == null)
				{
					return null;
				}
				result = result.add(resultInner);
				if(result == null)
				{
					return null;
				}
				result = result.mod(m);
			}
			else
			{
				RsaMutableBigInteger t1 = new RsaMutableBigInteger();
				new RsaMutableBigInteger(a1.multiply(m2)).multiply(new RsaMutableBigInteger(y1), t1);
				RsaMutableBigInteger t2 = new RsaMutableBigInteger();
				new RsaMutableBigInteger(a2.multiply(m1)).multiply(new RsaMutableBigInteger(y2), t2);
				t1.add(t2);
				RsaMutableBigInteger q = new RsaMutableBigInteger();
				RsaMutableBigInteger r = t1.divide(new RsaMutableBigInteger(m), q);
				if(r == null)
				{
					return null;
				}
				result = r.toBigInteger();
				if(result == null)
				{
					return null;
				}
			}
		}
		return (invertResult ? result.modInverse(m) : result);
	}
	
	private RsaBigInteger oddModPow(RsaBigInteger y, RsaBigInteger z)
	{
		if((y == null) || (z == null) || (y.mag.length <= 0) || (z.mag.length <= 0))
		{
			return null;
		}
		
		if(y.equals(ONE))
		{
			return this;
		}
		
		if(signum == 0)
		{
			return ZERO;
		}
		
		int[] base = mag.clone();
		int[] exp = y.mag;
		int[] mod = z.mag;
		int modLen = mod.length;
		
		int wbits = 0;
		int ebits = bitLength(exp, exp.length);
		
		if(ebits <= 0)
		{
			return null;
		}
		
		if((ebits != 17) || (exp[0] != 65537))
		{
			while(ebits > bnExpModThreshTable[wbits])
			{
				wbits++;
			}
		}
		
		int tblmask = 1 << wbits;
		
		int[][] table = new int[tblmask][];
		for(int i = 0; i < tblmask; i++)
		{
			table[i] = new int[modLen];
		}
		
		int inv = -RsaMutableBigInteger.inverseMod32(mod[modLen - 1]);
		int[] a = leftShift(base, base.length, modLen << 5);
		
		RsaMutableBigInteger q = new RsaMutableBigInteger(), a2 = new RsaMutableBigInteger(a), b2 = new RsaMutableBigInteger(mod);
		RsaMutableBigInteger r = a2.divide(b2, q);
		if(r == null)
		{
			return null;
		}
		table[0] = r.toIntArray();
		
		if(table[0].length < modLen)
		{
			int offset = modLen - table[0].length;
			int[] t2 = new int[modLen];
			for(int i = 0; i < table[0].length; i++)
			{
				t2[i + offset] = table[0][i];
			}
			table[0] = t2;
		}
		
		int[] b = squareToLen(table[0], modLen, null);
		b = montReduce(b, mod, modLen, inv);
		
		int[] t = Arrays.copyOf(b, modLen);
		
		for(int i = 1; i < tblmask; i++)
		{
			int[] prod = multiplyToLen(t, modLen, table[i - 1], modLen, null);
			table[i] = montReduce(prod, mod, modLen, inv);
		}
		
		int bitpos = 1 << ((ebits - 1) & (32 - 1));
		
		int buf = 0;
		int elen = exp.length;
		int eIndex = 0;
		for(int i = 0; i <= wbits; i++)
		{
			buf = (buf << 1) | (((exp[eIndex] & bitpos) != 0) ? 1 : 0);
			bitpos = si(bitpos, 1);
			if(bitpos == 0)
			{
				eIndex++;
				bitpos = 1 << (32 - 1);
				elen--;
			}
		}
		
		int multpos = ebits;
		
		ebits--;
		boolean isone = true;
		
		multpos = ebits - wbits;
		if(buf == 0)
		{
			return null;
		}
		while((buf & 1) == 0)
		{
			buf = si(buf, 1);
			multpos++;
		}
		
		int[] mult = table[si(buf, 1)];
		
		buf = 0;
		if(multpos == ebits)
		{
			isone = false;
		}
		
		while(true)
		{
			ebits--;
			
			buf <<= 1;
			
			if(elen != 0)
			{
				buf |= ((exp[eIndex] & bitpos) != 0) ? 1 : 0;
				bitpos = si(bitpos, 1);
				if(bitpos == 0)
				{
					eIndex++;
					bitpos = 1 << (32 - 1);
					elen--;
				}
			}
			
			if((buf & tblmask) != 0)
			{
				multpos = ebits - wbits;
				if(buf == 0)
				{
					return null;
				}
				while((buf & 1) == 0)
				{
					buf = si(buf, 1);
					multpos++;
				}
				mult = table[si(buf, 1)];
				buf = 0;
			}
			
			if(ebits == multpos)
			{
				if(isone)
				{
					b = mult.clone();
					isone = false;
				}
				else
				{
					t = b;
					a = multiplyToLen(t, modLen, mult, modLen, a);
					a = montReduce(a, mod, modLen, inv);
					t = a;
					a = b;
					b = t;
				}
			}
			
			if(ebits <= 0)
			{
				break;
			}
			
			if(!isone)
			{
				t = b;
				a = squareToLen(t, modLen, a);
				a = montReduce(a, mod, modLen, inv);
				t = a;
				a = b;
				b = t;
			}
		}
		
		int[] t2 = new int[2 * modLen];
		System.arraycopy(b, 0, t2, modLen, modLen);
		
		b = montReduce(t2, mod, modLen, inv);
		
		t2 = Arrays.copyOf(b, modLen);
		
		return new RsaBigInteger(1, t2);
	}
	
	private static int[] montReduce(int[] n, int[] mod, int mlen, int inv)
	{
		if((n == null) || (mod == null))
		{
			return new int[0];
		}
		
		int c = 0;
		int len = mlen;
		int offset = 0;
		
		do
		{
			int nEnd = n[n.length - 1 - offset];
			int carry = mulAdd(n, mod, offset, mlen, inv * nEnd);
			c += addOne(n, offset, mlen, carry);
			offset++;
		}
		while(--len > 0);
		
		while(c > 0)
		{
			int add = subN(n, mod, mlen);
			if(add == 0)
			{
				return new int[0];
			}
			c += add;
		}
		
		while(intArrayCmpToLen(n, mod, mlen) >= 0)
		{
			subN(n, mod, mlen);
		}
		
		return n;
	}
	
	private static int intArrayCmpToLen(int[] arg1, int[] arg2, int len)
	{
		if((arg1 == null) || (arg2 == null) || (len > arg1.length) || (len > arg2.length))
		{
			return 0;
		}
		
		for(int i = 0; i < len; i++)
		{
			long b1 = arg1[i] & LONG_MASK;
			long b2 = arg2[i] & LONG_MASK;
			if(b1 < b2)
			{
				return -1;
			}
			if(b1 > b2)
			{
				return 1;
			}
		}
		return 0;
	}
	
	private static int subN(int[] a, int[] b, int len)
	{
		if((a == null) || (b == null) || (len > a.length) || (len > b.length))
		{
			return 0;
		}
		
		long sum = 0;
		while(--len >= 0)
		{
			sum = ((a[len] & LONG_MASK) - (b[len] & LONG_MASK)) + (sum >> 32);
			a[len] = (int) sum;
		}
		return (int) (sum >> 32);
	}
	
	private static int mulAdd(int[] out, int[] in, int offset, int len, int k)
	{
		if((out == null) || (in == null) || (len > out.length) || (len > in.length))
		{
			return 0;
		}
		
		long kLong = k & LONG_MASK;
		long carry = 0;
		
		offset = out.length - offset - 1;
		for(int j = len - 1; j >= 0; j--)
		{
			long product = ((in[j] & LONG_MASK) * kLong) + (out[offset] & LONG_MASK) + carry;
			out[offset--] = (int) product;
			carry = sl(product, 32);
		}
		return (int) carry;
	}
	
	private static int addOne(int[] a, int offset, int mlen, int carry)
	{
		if((a == null) || ((a.length - 1 - mlen - offset) >= a.length) || ((a.length - 1 - mlen - offset) < 0))
		{
			return 0;
		}
		
		offset = a.length - 1 - mlen - offset;
		long t = (a[offset] & LONG_MASK) + (carry & LONG_MASK);
		
		a[offset] = (int) t;
		if(sl(t, 32) == 0)
		{
			return 0;
		}
		while(--mlen >= 0)
		{
			if(--offset < 0)
			{
				return 1;
			}
			else
			{
				a[offset]++;
				if(a[offset] != 0)
				{
					return 0;
				}
			}
		}
		return 1;
	}
	
	private RsaBigInteger modPow2(RsaBigInteger exponent, int p)
	{
		if(exponent == null)
		{
			return null;
		}
		
		RsaBigInteger result = ONE;
		RsaBigInteger baseToPow2 = this.mod2(p);
		if(baseToPow2 == null)
		{
			return null;
		}
		
		int expOffset = 0;
		int limit = exponent.bitLength();
		
		if(this.testBit(0))
		{
			limit = (p - 1) < limit ? (p - 1) : limit;
		}
		
		while(expOffset < limit)
		{
			if(exponent.testBit(expOffset))
			{
				result = result.multiply(baseToPow2);
				if(result == null)
				{
					return null;
				}
				result = result.mod2(p);
				if(result == null)
				{
					return null;
				}
			}
			expOffset++;
			if(expOffset < limit)
			{
				baseToPow2 = baseToPow2.square();
				if(baseToPow2 == null)
				{
					return null;
				}
				baseToPow2 = baseToPow2.mod2(p);
				if(baseToPow2 == null)
				{
					return null;
				}
			}
		}
		
		return result;
	}
	
	private RsaBigInteger mod2(int p)
	{
		if(bitLength() <= p)
		{
			return this;
		}
		
		int numInts = si((p + 31), 5);
		if(numInts > this.mag.length)
		{
			return null;
		}
		
		int[] mag = new int[numInts];
		System.arraycopy(this.mag, (this.mag.length - numInts), mag, 0, numInts);
		
		int excessBits = (numInts << 5) - p;
		mag[0] &= (1L << (32 - excessBits)) - 1;
		
		return (mag[0] == 0 ? new RsaBigInteger(1, mag) : new RsaBigInteger(mag, 1));
	}
	
	public RsaBigInteger modInverse(RsaBigInteger m)
	{
		if(m == null)
		{
			return null;
		}
		if(m.signum != 1)
		{
			//throw new ArithmeticException("BigInteger: modulus not positive");
			return null;
		}
		if(m.equals(ONE))
		{
			return ZERO;
		}
		
		RsaBigInteger modVal = this;
		if((signum < 0) || (this.compareMagnitude(m) >= 0))
		{
			modVal = this.mod(m);
			if(modVal == null)
			{
				return null;
			}
		}
		
		if(modVal.equals(ONE))
		{
			return ONE;
		}
		
		RsaMutableBigInteger a = new RsaMutableBigInteger(modVal);
		RsaMutableBigInteger b = new RsaMutableBigInteger(m);
		
		RsaMutableBigInteger result = a.mutableModInverse(b);
		if(result == null)
		{
			return null;
		}
		return result.toBigInteger(1);
	}
	
	private RsaBigInteger shiftLeft(int n)
	{
		if(signum == 0)
		{
			return ZERO;
		}
		if(n > 0)
		{
			return new RsaBigInteger(shiftLeft(mag, n), signum);
		}
		else if(n == 0)
		{
			return this;
		}
		else
		{
			return shiftRightImpl(-n);
		}
	}
	
	private static int[] shiftLeft(int[] mag, int n)
	{
		if(mag == null)
		{
			return new int[0];
		}
		
		int nInts = si(n, 5);
		int nBits = n & 0x1f;
		int magLen = mag.length;
		int newMag[] = null;
		
		if(nBits == 0)
		{
			newMag = new int[magLen + nInts];
			System.arraycopy(mag, 0, newMag, 0, magLen);
		}
		else
		{
			if(mag.length <= 0)
			{
				return new int[0];
			}
			
			int i = 0;
			int nBits2 = 32 - nBits;
			int highBits = si(mag[0], nBits2);
			if(highBits != 0)
			{
				newMag = new int[magLen + nInts + 1];
				newMag[i++] = highBits;
			}
			else
			{
				newMag = new int[magLen + nInts];
			}
			int j = 0;
			while(j < (magLen - 1))
			{
				newMag[i] = (mag[j] << nBits) | si(mag[j + 1], nBits2);
				i++;
				j++;
			}
			newMag[i] = mag[j] << nBits;
		}
		return newMag;
	}
	
	private RsaBigInteger shiftRight(int n)
	{
		if(signum == 0)
		{
			return ZERO;
		}
		if(n > 0)
		{
			return shiftRightImpl(n);
		}
		else if(n == 0)
		{
			return this;
		}
		else
		{
			return new RsaBigInteger(shiftLeft(mag, -n), signum);
		}
	}
	
	private RsaBigInteger shiftRightImpl(int n)
	{
		int nInts = si(n, 5);
		int nBits = n & 0x1f;
		int magLen = mag.length;
		int newMag[] = null;
		
		if(nInts >= magLen)
		{
			return (signum >= 0 ? ZERO : NEGATIVE_ONE);
		}
		
		if(nBits == 0)
		{
			int newMagLen = magLen - nInts;
			newMag = Arrays.copyOf(mag, newMagLen);
		}
		else
		{
			int i = 0;
			int highBits = si(mag[0], nBits);
			if(highBits != 0)
			{
				newMag = new int[magLen - nInts];
				newMag[i++] = highBits;
			}
			else
			{
				newMag = new int[magLen - nInts - 1];
			}
			
			int nBits2 = 32 - nBits;
			int j = 0;
			while(j < (magLen - nInts - 1))
			{
				newMag[i] = (mag[j] << nBits2) | si(mag[j + 1], nBits);
				i++;
				j++;
			}
		}
		
		if(signum < 0)
		{
			boolean onesLost = false;
			for(int i = magLen - 1, j = magLen - nInts; (i >= j) && !onesLost; i--)
			{
				onesLost = (mag[i] != 0);
			}
			if(!onesLost && (nBits != 0))
			{
				onesLost = ((mag[magLen - nInts - 1] << (32 - nBits)) != 0);
			}
			
			if(onesLost)
			{
				newMag = javaIncrement(newMag);
			}
		}
		
		return new RsaBigInteger(newMag, signum);
	}
	
	private int[] javaIncrement(int[] val)
	{
		if(val == null)
		{
			return new int[0];
		}
		
		int lastSum = 0;
		for(int i = val.length - 1; (i >= 0) && (lastSum == 0); i--)
		{
			lastSum = (val[i] += 1);
		}
		if(lastSum == 0)
		{
			val = new int[val.length + 1];
			val[0] = 1;
		}
		return val;
	}
	
	
	private boolean testBit(int n)
	{
		if(n < 0)
		{
			//throw new ArithmeticException("Negative bit address");
			return false;
		}
		
		return (getInt(si(n, 5)) & (1 << (n & 31))) != 0;
	}
	
	private RsaBigInteger setBit(int n)
	{
		if(n < 0)
		{
			//throw new ArithmeticException("Negative bit address");
			return null;
		}
		
		int intNum = si(n, 5);
		int[] result = new int[Math.max(intLength(), intNum + 2)];
		
		for(int i = 0; i < result.length; i++)
		{
			result[result.length - i - 1] = getInt(i);
		}
		
		result[result.length - intNum - 1] |= (1 << (n & 31));
		
		return valueOf(result);
	}
	
	private int getLowestSetBit()
	{
		int lsb = lowestSetBit - 2;
		if(lsb == -2)
		{
			lsb = 0;
			if(signum == 0)
			{
				lsb -= 1;
			}
			else
			{
				int i, b;
				for(i = 0; (b = getInt(i)) == 0; i++)
				{
					;
				}
				lsb += (i << 5) + integerNumberOfTrailingZeros(b);
			}
			lowestSetBit = lsb + 2;
		}
		return lsb;
	}
	
	public int bitLength()
	{
		int n = bitLength - 1;
		if(n == -1)
		{
			int[] m = mag;
			int len = m.length;
			if(len == 0)
			{
				n = 0;
			}
			else
			{
				int magBitLength = ((len - 1) << 5) + bitLengthForInt(mag[0]);
				if(signum < 0)
				{
					boolean pow2 = (integerBitCount(mag[0]) == 1);
					for(int i = 1; (i < len) && pow2; i++)
					{
						pow2 = (mag[i] == 0);
					}
					
					n = (pow2 ? magBitLength - 1 : magBitLength);
				}
				else
				{
					n = magBitLength;
				}
			}
			bitLength = n + 1;
		}
		return n;
	}
	
	
	public int compareTo(RsaBigInteger val)
	{
		if(val == null)
		{
			return 0;
		}
		if(signum == val.signum)
		{
			switch(signum)
			{
				case 1:
					return compareMagnitude(val);
				case -1:
					return val.compareMagnitude(this);
				default:
					return 0;
			}
		}
		return signum > val.signum ? 1 : -1;
	}
	
	private final int compareMagnitude(RsaBigInteger val)
	{
		if(val == null)
		{
			return 0;
		}
		int[] m1 = mag;
		int len1 = m1.length;
		int[] m2 = val.mag;
		int len2 = m2.length;
		if(len1 < len2)
		{
			return -1;
		}
		if(len1 > len2)
		{
			return 1;
		}
		for(int i = 0; i < len1; i++)
		{
			int a = m1[i];
			int b = m2[i];
			if(a != b)
			{
				return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
			}
		}
		return 0;
	}
	
	public byte[] toByteArray()
	{
		int byteLen = (bitLength() / 8) + 1;
		byte[] byteArray = new byte[byteLen];
		
		for(int i = byteLen - 1, bytesCopied = 4, nextInt = 0, intIndex = 0; i >= 0; i--)
		{
			if(bytesCopied == 4)
			{
				nextInt = getInt(intIndex++);
				bytesCopied = 1;
			}
			else
			{
				nextInt = si(nextInt, 8);
				bytesCopied++;
			}
			byteArray[i] = (byte) nextInt;
		}
		
		if(byteArray[0] == 0)
		{
			byte[] tmp = new byte[byteArray.length - 1];
			System.arraycopy(byteArray, 1, tmp, 0, tmp.length);
			byteArray = tmp;
		}
		return byteArray;
	}
	
	public int intValue()
	{
		int result = 0;
		result = getInt(0);
		return result;
	}
	
	
	private static int[] stripLeadingZeroInts(int[] val)
	{
		if(val == null)
		{
			return new int[0];
		}
		
		int vlen = val.length;
		int keep;
		
		for(keep = 0; (keep < vlen) && (val[keep] == 0); keep++)
		{
			;
		}
		return Arrays.copyOfRange(val, keep, vlen);
	}
	
	private static int[] trustedStripLeadingZeroInts(int[] val)
	{
		if(val == null)
		{
			return new int[0];
		}
		
		int vlen = val.length;
		int keep;
		
		for(keep = 0; (keep < vlen) && (val[keep] == 0); keep++)
		{
			;
		}
		return keep == 0 ? val : Arrays.copyOfRange(val, keep, vlen);
	}
	
	private static int[] stripLeadingZeroBytes(byte[] a)
	{
		if(a == null)
		{
			return new int[0];
		}
		
		int byteLength = a.length;
		int keep;
		
		for(keep = 0; (keep < byteLength) && (a[keep] == 0); keep++)
		{
			;
		}
		
		int intLength = si(((byteLength - keep) + 3), 2);
		int[] result = new int[intLength];
		int b = byteLength - 1;
		for(int i = intLength - 1; i >= 0; i--)
		{
			result[i] = a[b--] & 0xff;
			int bytesRemaining = (b - keep) + 1;
			int bytesToTransfer = Math.min(3, bytesRemaining);
			for(int j = 8; j <= (bytesToTransfer << 3); j += 8)
			{
				result[i] |= ((a[b--] & 0xff) << j);
			}
		}
		return result;
	}
	
	private static int[] makePositive(int a[])
	{
		if(a == null)
		{
			return new int[0];
		}
		
		int keep, j;
		
		for(keep = 0; (keep < a.length) && (a[keep] == -1); keep++)
		{
			;
		}
		
		for(j = keep; (j < a.length) && (a[j] == 0); j++)
		{
			;
		}
		int extraInt = (j == a.length ? 1 : 0);
		int[] result = new int[(a.length - keep) + extraInt];
		
		for(int i = keep; i < a.length; i++)
		{
			result[(i - keep) + extraInt] = ~a[i];
		}
		
		for(int i = result.length - 1; ++result[i] == 0; i--)
		{
			;
		}
		
		return result;
	}
	
	
	private int intLength()
	{
		return si(bitLength(), 5) + 1;
	}
	
	private int signInt()
	{
		return signum < 0 ? -1 : 0;
	}
	
	private int getInt(int n)
	{
		if(n < 0)
		{
			return 0;
		}
		if(n >= mag.length)
		{
			return signInt();
		}
		
		int index = mag.length - n - 1;
		if((index < 0) || (index >= mag.length))
		{
			return 0;
		}
		
		int magInt = mag[index];
		
		return (signum >= 0 ? magInt : (n <= firstNonzeroIntNum() ? -magInt : ~magInt));
	}
	
	private int firstNonzeroIntNum()
	{
		int fn = firstNonzeroIntNum - 2;
		if(fn == -2)
		{
			fn = 0;
			
			int i;
			int mlen = mag.length;
			for(i = mlen - 1; (i >= 0) && (mag[i] == 0); i--)
			{
				;
			}
			fn = mlen - i - 1;
			firstNonzeroIntNum = fn + 2;
		}
		return fn;
	}
	
	
	@Override
	public boolean equals(Object x)
	{
		if(x == null)
		{
			return false;
		}
		
		if(x == this)
		{
			return true;
		}
		
		if(!(x instanceof RsaBigInteger))
		{
			return false;
		}
		
		RsaBigInteger xInt = (RsaBigInteger) x;
		if(xInt.signum != signum)
		{
			return false;
		}
		
		int[] m = mag;
		int len = m.length;
		int[] xm = xInt.mag;
		if(len != xm.length)
		{
			return false;
		}
		
		for(int i = 0; i < len; i++)
		{
			if(xm[i] != m[i])
			{
				return false;
			}
		}
		
		return true;
	}
	
	@Override
	public int hashCode()
	{
		int hashCode = 0;
		for(int i = 0; i < mag.length; i++)
		{
			hashCode = (int) ((31 * hashCode) + (mag[i] & LONG_MASK));
		}
		return hashCode * signum;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy