/* 
 * This code is part of Project Waterloo from King's College London
 * 
 *
 * Copyright King's College London  2011-
 * 
 * @author Malcolm Lidierth, King's College London  [Contact]
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 *
 */
package kcl.waterloo.math;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;

/**
 *
 * @author Malcolm Lidierth, King's College London  [Contact]
 */
public class Precise {

    private Precise() {
    }

    /**
     * Returns the sum for a double[]. The sum is accumulated as a
     * {@code BigDecimal}
     *
     * @param in
     * @return a BigDecimal
     */
    public static BigDecimal sum(final double[] in) {
        BigDecimal sum = BigDecimal.ZERO;
        for (int k = 0; k < in.length; k++) {
            sum = sum.add(new BigDecimal(in[k]));
        }
        return sum;
    }

    /**
     * Returns the cumulative sum for a double[] as a {@code BigDecimal}
     *
     * @param in
     * @return BigDecimal
     */
    public static BigDecimal[] cumsum(final double[] in) {
        BigDecimal[] sum = new BigDecimal[in.length];
        sum[0] = new BigDecimal(in[0]);
        for (int k = 1; k < in.length; k++) {
            sum[k] = sum[k - 1].add(new BigDecimal(in[k]));
        }
        return sum;
    }

    /**
     * Returns the cumulative sum for a double[] as a {@code BigDecimal}
     *
     * @param in
     * @return a BigDecimal
     */
    public static BigDecimal[] cumprod(final double[] in) {
        BigDecimal[] sum = new BigDecimal[in.length];
        sum[0] = new BigDecimal(in[0]);
        for (int k = 1; k < in.length; k++) {
            sum[k] = sum[k - 1].multiply(new BigDecimal(in[k]));
        }
        return sum;
    }

    /**
     * Returns the sum of squares for a double[] accumulated as a
     * {@code BigDecimal}
     *
     * @param in BigDecimal
     * @return `
     */
    public static BigDecimal sumsq(final double[] in) {
        BigDecimal sumsq = BigDecimal.ZERO;
        BigDecimal temp;
        for (int k = 0; k < in.length; k++) {
            temp = new BigDecimal(in[k]);
            temp = temp.multiply(temp);
            sumsq = sumsq.add(temp);
        }
        return sumsq;
    }

    public static BigDecimal dot(final double[] in1, final double[] in2) {
        BigDecimal dot = BigDecimal.ZERO;
        for (int k = 0; k < in1.length; k++) {
            dot = dot.add(new BigDecimal(in1[k]).multiply(new BigDecimal(in2[k])));
        }
        return dot;
    }

    /**
     * Returns the sumsq for a double[]. The mean is calculated as a
     * {@code BigDecimal} but returned as double
     *
     * @param in
     * @return a BigDecimal
     */
    public static BigDecimal mean(final double[] in) {
        return sum(in).divide(new BigDecimal(in.length));
    }
    /**
     * Factorials and "n choose k"
     *
     */
    final static LinkedHashMap factorialTable = fT();

    private static LinkedHashMap fT() {
        LinkedHashMap t = new LinkedHashMap< BigDecimal, BigDecimal>();
        t.put(BigDecimal.ZERO, BigDecimal.ONE);
        t.put(BigDecimal.ONE, BigDecimal.ONE);
        return t;
    }

    /**
     * Resets the internal hashmaps - thus freeing memory
     */
    public static void reset() {
        factorialTable.clear();
    }

    public static LinkedHashMap getFactorialTable() {
        return (LinkedHashMap) factorialTable.clone();
    }

//    /**
//     * Returns the factorial as a BigDecimal.
//     * For {@code{n less than 0} returns null.
//     * For {@code n=0,1} returns 1.
//     * Otherwise returns n! Once
//     * calculated, values of n and n! are stored in a HashMap. Subsequent calls
//     * with input n will return the stored value of n!.
//     *
//     * @param n
//     * @return n!
//     */
//    public static BigDecimal factorial(int n) {
//        return factorial(new BigDecimal(new Integer(n).toString()));
//    }
    /**
     * Returns the factorial as a BigDecimal. For {@code n less than 0} returns
     * null. For {@code n=0,1} returns 1. Otherwise returns n!. Once calculated,
     * values of n and n! are stored in a HashMap. Subsequent calls with input n
     * will return the stored value of n!.
     *
     * @param n - type long
     * @return n! - type BigDecimal
     */
    public static BigDecimal factorial(long n) {
        return factorial(new BigDecimal(n));
    }

    /**
     * Returns the factorial as a BigDecimal. For {@code n less than 0} returns
     * null. For {@code n=0,1} returns 1. Otherwise returns n! Once calculated,
     * values of n and n! are stored in a HashMap. Subsequent calls with input n
     * will return the stored value of n!.
     *
     * @param n - type BigDecimal
     * @return n! - type BigDecimal
     */
    public static BigDecimal factorial(BigDecimal n) {
        if (n.signum() == -1) {
            return null;
        }
        if (factorialTable.containsKey(n)) {
            // Already in the table
            return factorialTable.get(n);
        } else {
            //Not in the table
            int count = 0;
            Collection c = factorialTable.keySet();
            BigDecimal[] seed = c.toArray(new BigDecimal[c.size()]);
            BigDecimal product;
            BigDecimal n2;
            for (Iterator it = c.iterator(); it.hasNext();) {
                BigDecimal entry = it.next();
                if (entry.compareTo(n) > 0) {
                    product = factorialTable.get(seed[count]);
                    n2 = seed[count];
                    while (!n2.equals(n)) {
                        product = product.divide(n2);
                        n2 = n2.subtract(BigDecimal.ONE);
                    }
                    factorialTable.put(n, product);
                    return product;
                }
                count++;
            }
            // Iterator did not find a bigger entry
            product = factorialTable.get(seed[seed.length - 1]);
            n2 = seed[seed.length - 1].add(BigDecimal.ONE);
            while (n2.compareTo(n) <= 0) {
                product = product.multiply(n2);
                n2 = n2.add(BigDecimal.ONE);
            }
            factorialTable.put(n, product);
            return product;
        }
    }

    /**
     * 

* Accurately returns the binomial (N choose K) as a BigDecimal

*

* For k< 0, return ZERO

For k==0 or * k * ==n return ONE

*

* For k==1 return n

*

* Otherwise returns *

n!/((n*k)! k!)

* * @param n - int * @param k - int * @return
n!/((n*k)! k!)
as a BigDecimal */ public static BigDecimal nCk(int n, int k) { return nCk(new BigDecimal(n), new BigDecimal(k)); } /** *

* Accurately returns the binomial (N choose K) as a BigDecimal

*

* For k< 0, return ZERO

For k==0 or * k * ==n return ONE

*

* For k==1 return n

*

* Otherwise returns *

n!/((n*k)! k!)

* * @param n - long * @param k - long * @return
n!/((n*k)! k!)
as a BigDecimal */ public static BigDecimal nCk(long n, long k) { return nCk(new BigDecimal(Long.toString(n)), new BigDecimal(Long.toString(k))); } /** *

* Accurately returns the binomial (N choose K) as a BigDecimal

*

* For k< 0, return ZERO

For k==0 or * k * ==n return ONE

*

* For k==1 return n

*

* Otherwise returns *

n!/((n*k)! k!)

* * @param n - BigDecimal * @param k - BigDecimal * @return
n!/((n*k)! k!)
as a BigDecimal */ public static BigDecimal nCk(BigDecimal n, BigDecimal k) { switch (k.signum()) { case -1: // k<0 return BigDecimal.ZERO; case 0: // k==0 return BigDecimal.ONE; case 1: // k==1 if (k.equals(BigDecimal.ONE)) { return n; } } switch (n.subtract(k).signum()) { case -1: // k>n return BigDecimal.ZERO; case 0: // k==n return BigDecimal.ONE; default: // Note, k=n-1 and k>n/2 are not treated specially as n! and/or // k! may be in the factorialTable or required later. return factorial(n).divide(factorial(n.subtract(k)).multiply(factorial(k))); } } }

Ads help maintain this website.