
// ContinuedFracs.java
// Andrew Davison, ad@coe.psu.ac.th, April 2023

/* Examples of Continued Fractions using Nut methods and 
   Apache Commons Number's classes.

   https://en.wikipedia.org/wiki/Continued_fraction

   Topics covered:
    * Fraction --> Continued fraction
    * Decimal --> Continued fraction
    * Continued fraction --> (Big) Fraction
    * Convergents
    * Generalized CFs using coefs


   ContinuedFraction API:
   https://commons.apache.org/proper/commons-numbers/commons-numbers-docs/apidocs/org/apache/commons/numbers/fraction/ContinuedFraction.html

   GeneralizedContinuedFraction API:
   https://commons.apache.org/proper/commons-numbers/commons-numbers-docs/apidocs/org/apache/commons/numbers/fraction/GeneralizedContinuedFraction.html

   Also see the relevant section in Numbers' User Guide:
   https://commons.apache.org/proper/commons-numbers/userguide/index.html#Continued_Fractions

   Usage:
     > compile ContinuedFracs.java
     > run ContinuedFracs <numerator> <denominator>
   e.g.
     > run ContinuedFracs 33 14
*/


import java.math.*;
import java.util.*;

import org.apache.commons.numbers.fraction.*;
import org.apache.commons.numbers.fraction.GeneralizedContinuedFraction.*;


public class ContinuedFracs
{

  public static void main(String[] args)
  {
    if (args.length != 2) {
      System.out.println("Usage: run ContinuedFracs <numerator> <denominator>");
      return;
    }
    int numer = Math.abs(Nut.parseInt(args[0]));
    int denom = Math.abs(Nut.parseInt(args[1]));

    // ---------------------
    System.out.println("--- Fraction --> Continued fraction ---");

    Fraction f = Fraction.of(numer, denom);
    List<Integer> cfList = Nut.cf(f);
    System.out.println(numer + "/" + denom + " = " + cfList);  
               // e.g. 33/14 --> [2,2,1,4]

    f = Fraction.of(7, 239);
    cfList = Nut.cf(f);
    System.out.println("7/239 = " + cfList);  

    // ---------------------
    System.out.println("\n--- Decimal --> Continued fraction ---");
    // generate a few 'famous' continued fractions...

    cfList = Nut.cf(Math.PI, 1);
    System.out.println("Math.PI = " + cfList); 
             // only a double; 3.141592653589793

    List<Integer> piList = Nut.cf(Nut.PI, BigDecimal.ONE, 30);
    System.out.println("\nNut.PI = " + piList);   // a BigDecimal

    // Set precision to 100
    MathContext mc = new MathContext(100);
    BigDecimal root2 = BigDecimal.valueOf(2).sqrt(mc);
    cfList = Nut.cf(root2, BigDecimal.ONE, 30);
    System.out.println("\nBigDecimal sqrt(2) = " + cfList); 

    cfList = Nut.cf(Nut.SQRT2, BigDecimal.ONE, 30);
    System.out.println("\nNut.SQRT2 = " + cfList);  // a BigDecimal

    cfList = Nut.cf(Nut.PHI, BigDecimal.ONE, 30);
    System.out.println("\nNut.PHI = " + cfList);  // a BigDecimal

    // ---------------------
    System.out.println("\n--- Continued fraction --> (Big) Fraction ---");

    cfList = Arrays.asList(2, 1, 2, 1);
    f = Nut.cf2Frac(cfList);
    System.out.println("[2, 1, 2, 1] = " + f); // 11/4

    double v = GeneralizedContinuedFraction.value( Nut.simpleCF(2, 1, 2, 1));
    System.out.println("\nGCF double for [2, 1, 2, 1] = " + v + 
                                          " = " + Fraction.from(v));

    BigFraction bf = Nut.cf2BigFrac(piList);    
                      // cf2Frac() has integer overflow
    System.out.println("\n" + piList + " = " + bf);
    System.out.println("        = " + bf.doubleValue());
    System.out.println("Math.PI = " + Math.PI);


    // ---------------------
    System.out.println("\n--- Convergents ---");

    List<Fraction> fracs = Nut.convergents(612,233, 10);
    System.out.println("Convergents of 612/233: " + fracs);

    fracs = Nut.convergents(Math.PI, 6);
    System.out.println("\nConvergents of Math.PI: " + fracs);

    // ---------------------
    System.out.println("\n--- Generalized CFs using coefs ---");
    // see the user guide at:  https://commons.apache.org/proper/commons-numbers/userguide/index.html#Fraction

    double phiGCF = GeneralizedContinuedFraction.value(
         () -> Coefficient.of(1, 1), 1e-20);  
          // a double is accurate to approx 16 dp
    System.out.println("Coef(1,1) to generate phi: " + phiGCF);

    ContinuedFraction tangentCF = new ContinuedFraction() {
      public double getA(int n, double x) 
      {  return (n < 2) ? x : (-x * x); }

      public double getB(int n, double x) 
      {  return (n == 0) ? 0 : (2*n-1); }
    };

    double val = tangentCF.evaluate(Math.PI/4, 1e-20);
    System.out.println("\nCoef(A,B) methods to generate tan(Math.PI/4) = " + val);

  } /* main */


}  // end of ContinuedFracs class
