// Arup Guha
// 10/13/2025
// Demonstration of Fast Mod Expo (is provided in BigInteger)

import java.util.*;
import java.math.*;

public class FastModExpo {

	public static void main(String[] args) {
	
		// Generate 3 random BigIntegers.
		Random r = new Random();
		BigInteger base = new BigInteger(1000, r);
		BigInteger exp = new BigInteger(1000, r);
		BigInteger mod = new BigInteger(1001, r);
		
		// Time and run my code and Javas.
		long s1 = System.currentTimeMillis();
		BigInteger mine = myModPow(base, exp, mod);
		long s2 = System.currentTimeMillis();
		BigInteger real = base.modPow(exp, mod);
		long s3 = System.currentTimeMillis();
		
		// I only print this if mine worked.
		if (mine.equals(real)) {
			System.out.println("Our test worked.");
			System.out.println("Mine took "+(s2-s1)+" ms.");
			System.out.println("Java took "+(s3-s2)+" ms.");
		}
	}
	
	public static BigInteger myModPow(BigInteger base, BigInteger exp, BigInteger n) {
	
		// I'll need this.
		BigInteger two = new BigInteger("2");
	
		// Base case.
		if (exp.equals(BigInteger.ZERO)) return BigInteger.ONE;
		
		BigInteger[] divans = exp.divideAndRemainder(two);
		
		// Even exponent.
		if (divans[1].equals(BigInteger.ZERO)) {
			BigInteger tmp = myModPow(base, divans[0], n);
			return (tmp.multiply(tmp)).mod(n);
		}
		
		// New exponent (e-1).
		BigInteger newe = exp.subtract(BigInteger.ONE);
		BigInteger tmp = myModPow(base, newe, n);
		
		// Return (b^(e-1) times b ) mod n
		return (tmp.multiply(base)).mod(n);
	}
}