/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.commons.util;

import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;

public class MathUtils {

	/**
	 * Non-public constructor
	 */
	private MathUtils() {
    	
    }
    
	/**
     * Returns the square root of a given number if it has an exact square root,
     * or the square root rounded down to the whole number otherwise
     *
     * @param input
     *            the number
     * @return the floored root square.
     * @throws ArithmeticException
     *             if the number is negative
     */
    public static BigInteger isqrt(BigInteger input) {
        if (input.signum() == -1) {
            throw new ArithmeticException("Input number cannot be negative");
        }

        final BigInteger TWO = BigInteger.valueOf(2);
        int c;

        // Significantly speed-up algorithm by proper select of initial approximation
        // As square root has 2 times less digits as original value
        // we can start with 2^(length of N1 / 2)
        BigInteger n0 = TWO.pow(input.bitLength() / 2);

        // Value of approximate value on previous step
        BigInteger np = input;
        Set<BigInteger> cachedAttempts = new HashSet<>();

        do {
            // next approximation step: n0 = (n0 + in/n0) / 2
            n0 = n0.add(input.divide(n0)).divide(TWO);

            // compare current approximation with previous step
            c = np.compareTo(n0);

            // return if no new attempts
            if (cachedAttempts.contains(n0)) {
                return np;
            }

            cachedAttempts.add(n0);

            // save value as previous approximation
            np = n0;

            // finish when previous step is equal to current
        } while (c != 0);

        return n0;
    }
}
