
// FunBigNumbers.java
// Andrew Davison, ad@coe.psu.ac.th, Nov. 2022

/* This code uses Java's BigDecimal and BigInteger to try 
   to mimic FunNumbers.java, but the lack of math functions 
   makes it a lot more work. Argueably, the only advantage of 
   Java classes is the presence of "probable" prime functions 
   in BigInteger. 

   Usage:
     > javac FunBigNumbers.java
     > java FunBigNumbers
*/

import java.util.*;
import java.math.*;
import java.text.*;


public class FunBigNumbers
{
  public static void main(String[] args)
  {
    // =======================================
    // BigDecimal using 50 digit precision

    // BigDecimal f13 = new BigDecimal(1).divide(new BigDecimal(13));
       // causes a Non-terminating decimal expansion exception

    MathContext mc50 = new MathContext(50, RoundingMode.HALF_EVEN);
         // MathContext.DECIMAL128 only support 34 digits

    BigDecimal f13 = new BigDecimal(1).divide(new BigDecimal(13), mc50);
    System.out.printf("Periodic 1/13 = " + f13.toPlainString() + "\n");
    System.out.println("Periodic 1/13 = " + f13 + "\n");

    // sqrt 163
    BigDecimal root163 = new BigDecimal(163).sqrt(mc50);
    System.out.println("sqrt of 163 = " + root163 + "\n");

    BigDecimal pi = calculatePi(1000000L, mc50); 
    System.out.println("1M iterative Pi = " + pi);
    System.out.println("Math.PI = " + Math.PI + "\n");

    // Euler-Mascheroni constant; not coded

    BigDecimal phi = new BigDecimal(5).sqrt(mc50).add(BigDecimal.ONE).
                                    divide(new BigDecimal(2), mc50);
    System.out.println("Phi = " + phi + "\n");   // (sqrt(5)+1)/2

    // low-precision versions of pi and e:
    BigDecimal BD_PI = new BigDecimal(Math.PI);
    BigDecimal BD_E = new BigDecimal(Math.E);

    // None of these coded:
    // e^(pi*sqrt(163))
    // e^(pi)-pi = 20
    // sin(11)
    // 510*log10(7)

    // The Simpsons, "Treehouse of Horror VI". 
    // 1782^12 + 1841^12 = 1922^12
    BigDecimal p12 = new BigDecimal(12);
    BigDecimal f12 = new BigDecimal(1).divide(p12, mc50);

    BigDecimal v1 = new BigDecimal(1782).pow(12, mc50);
    BigDecimal v2 = new BigDecimal(1841).pow(12, mc50);
    BigDecimal v3 = new BigDecimal(1922).pow(12, mc50);
    BigDecimal res = v1.add(v2).subtract(v3);   // no pow() for reals
    System.out.println("1782^12 + 1841^12 - 1922^12 = " + res + "\n");

    // The Simpsons, "The Wizard of Evergreen Terrace"
    // 3987^12 + 4365^12 = 4472^12
    v1 = new BigDecimal(3987).pow(12, mc50);
    v2 = new BigDecimal(4365).pow(12, mc50);
    v3 = new BigDecimal(4472).pow(12, mc50);
    res = v1.add(v2).subtract(v3);   // no pow() for reals
    System.out.println("3987^12 + 4365^12 - 4472^12 = " +  res + "\n");

    // The first 144 decimal digits of pi add up to 666 - not coded
    
    // ======================================================
    // BigInteger

    System.out.println("Factorial 100 = " + 
                              factorial(100) + "\n");

    // Not coded:
    // getPrimes(), primeFactors()
    // but would be similar to the Apint versions

    System.out.println("Is 7789 prime? " + 
              new BigInteger("7789").isProbablePrime(100) + "\n");

    System.out.println("Is 170141183460469231731687303715884105727 prime? " + 
              new BigInteger("170141183460469231731687303715884105727").isProbablePrime(100) + "\n"); // 12th Mersenne prime, 2^127-1

    // https://en.wikipedia.org/wiki/List_of_Mersenne_primes_and_perfect_numbers
  }  // end of main



  private static BigDecimal calculatePi(long n, MathContext mc) 
  /* Leibniz formula:
       1 -  1/3  + 1/5 - 1/7 + 1/9 - ... = π/4

    This converges very slowly, but is easy to code up.
  */
  {
    BigDecimal two = new BigDecimal(2);
    BigDecimal minus1 = new BigDecimal(-1);

    BigDecimal pi = BigDecimal.ZERO;   // initial value
	for (long i = 1; i < n; i++) {
      BigDecimal denom = new BigDecimal(i).multiply(two).
                                    subtract(BigDecimal.ONE);  // (2*i - 1)
      BigDecimal numer = (i%2 == 0) ? minus1 : BigDecimal.ONE;    // -1^(i+1)
      pi = pi.add(numer.divide(denom, mc)); 
	} 

	return pi.multiply(new BigDecimal(4));
  } // end of calculatePi()


  private static BigInteger factorial(int n) 
  {
    BigInteger fact = BigInteger.ONE;
    for (int i = 2; i <= n; i++)
      fact = fact.multiply(BigInteger.valueOf(i));
    return fact;
  }


}  // end of FunBigNumbers class