package meerkat.crypto.secretsharing.shamir;

import meerkat.crypto.utils.Arithmetic;

import java.math.BigInteger;

/**
 * Created by Tzlil on 1/28/2016.
 *
 * container of lagrange polynomial
 *
 * Constructor is private (use {@link #lagrangePolynomials(Polynomial.Point[], Arithmetic)} to construct)
 *
 * l = (evaluate/divisor)* polynomial
 *
 * Note : image and divisor stored separately for avoiding lose of information by division
 */
class LagrangePolynomial{
    public final Polynomial polynomial;
    public final BigInteger image;
    public final BigInteger divisor;

    /**
     * inner constructor, stores all given parameters
     * @param polynomial
     * @param image
     * @param divisor
     */
    private LagrangePolynomial(Polynomial polynomial, BigInteger image, BigInteger divisor) {
        this.polynomial = polynomial;
        this.image = image;
        this.divisor = divisor;
    }

    /**
     * static method
     * @param points array points s.t there are no couple of points that shares the same x value
     *
     * @return the lagrange polynomials that mach to given points.
     *         in case there exists i != j s.t points[i].x == points[j].x returns null.
     */
    public static LagrangePolynomial[] lagrangePolynomials(Polynomial.Point[] points,Arithmetic<BigInteger> arithmetic) {
        Polynomial one = new Polynomial(new BigInteger[]{BigInteger.ONE},arithmetic);
        LagrangePolynomial[] lagrangePolynomials = new LagrangePolynomial[points.length];
        Polynomial[] factors = new Polynomial[points.length];
        for (int i = 0 ; i < factors.length ; i++){
            factors[i] = new Polynomial(new BigInteger[]{points[i].x.negate(),BigInteger.ONE},arithmetic); // X - Xi
        }
        Polynomial product;
        BigInteger divisor;
        for(int i = 0; i < points.length; i ++) {
            product = one;
            divisor = BigInteger.ONE;
            for (int j = 0; j < points.length; j++) {
                if (i != j) {
                    divisor = arithmetic.mul(divisor,arithmetic.sub(points[i].x,points[j].x));
                    product = product.mul(factors[j]);
                }
            }
            if(divisor.equals(BigInteger.ZERO))
                return null;
            lagrangePolynomials[i] = new LagrangePolynomial(product,points[i].y,divisor);
        }
        return lagrangePolynomials;
    }
}
