All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nom.bdezonia.zorbage.algorithm.DivMod Maven / Gradle / Ivy

/*
 * Zorbage: an algebraic data hierarchy for use in numeric processing.
 *
 * Copyright (c) 2016-2021 Barry DeZonia All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list
 * of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright notice, this
 * list of conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution.
 * 
 * Neither the name of the  nor the names of its contributors may
 * be used to endorse or promote products derived from this software without specific
 * prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
package nom.bdezonia.zorbage.algorithm;

import nom.bdezonia.zorbage.function.Function2;
import nom.bdezonia.zorbage.algebra.Addition;
import nom.bdezonia.zorbage.algebra.Algebra;
import nom.bdezonia.zorbage.algebra.Bounded;
import nom.bdezonia.zorbage.algebra.ScaleByOneHalf;
import nom.bdezonia.zorbage.algebra.Ordered;
import nom.bdezonia.zorbage.algebra.Unity;

//Adapted from an algorithm as published by Stepanov and Rose 2015.

/**
 * 
 * @author Barry DeZonia
 *
 */
public class DivMod {

	// do not instantiate
	
	private DivMod() { }
	
	/**
	 * Calculate both div and mod for a pair of numbers.
	 * 
	 * @param alg
	 * @param a Numerator
	 * @param b Denominator
	 * @param d Div result
	 * @param m Mod result
	 */
	public static  & Ordered & Unity & Addition & ScaleByOneHalf & Bounded,U>
		void compute(T alg, U a, U b, U d, U m)
	{
		if (alg.isZero().call(b))
			throw new IllegalArgumentException("divide by zero");

		U zero = alg.construct();
		U one = alg.construct();
		U min = alg.construct();
		alg.unity().call(one);
		alg.minBound().call(min);
		
		if (alg.isLess().call(min, zero)) {
			// signed numbers

			// capture one problematic edge case for signed math: dividing -minint by 1 will
			//   force a sign change and an illegal negation. but we know the right result in
			//   all divide by 1 cases. so just assign it and return.
			if (alg.isEqual().call(one, b)) {
				alg.assign().call(a, d);
				alg.zero().call(m);
				return;
			}
			
			// work in negative numbers to accommodate -minint asymmetry
			
			boolean aPos;
			boolean bPos;
			U aNeg = alg.construct();
			U bNeg = alg.construct();
			if (alg.isGreater().call(a, zero)) {
				aPos = true;
				alg.negate().call(a, aNeg);
			}
			else {
				aPos = false;
				alg.assign().call(a, aNeg);
			}
			if (alg.isGreater().call(b, zero)) {
				bPos = true;
				alg.negate().call(b, bNeg);
			}
			else {
				bPos = false;
				alg.assign().call(b, bNeg);
			}
			if (alg.isGreater().call(aNeg, bNeg)) 
			{
				if (aPos)
					alg.negate().call(aNeg, aNeg);
				alg.assign().call(zero, d);
				alg.assign().call(aNeg, m);
				return;
			}
			U c = alg.construct();
			largestDoubling(alg, alg.isLessEqual(), aNeg, bNeg, c);
			U n = alg.construct(one);
			alg.subtract().call(aNeg, c, aNeg);
			while (!alg.isEqual().call(c, bNeg)) {
				alg.scaleByOneHalf().call(1, c, c);
				alg.add().call(n, n, n);
				if (alg.isGreaterEqual().call(c, aNeg)) {
					alg.subtract().call(aNeg, c, aNeg);
					alg.add().call(n, one, n);
				}
			}
			if (aPos != bPos)
				alg.negate().call(n, n);
			if (aPos)
				alg.negate().call(aNeg, aNeg);
			alg.assign().call(n, d);
			alg.assign().call(aNeg, m);
		}
		else {
			// unsigned numbers
			
			// work with positive number algorithm
			
			U aPos = alg.construct(a);
			U bPos = alg.construct(b);
			if (alg.isLess().call(aPos, bPos)) 
			{
				alg.assign().call(zero, d);
				alg.assign().call(aPos, m);
				return;
			}
			U c = alg.construct();
			largestDoubling(alg, alg.isGreaterEqual(), aPos, bPos, c);
			U n = alg.construct(one);
			alg.subtract().call(aPos, c, aPos);
			while (!alg.isEqual().call(c, bPos)) {
				alg.scaleByOneHalf().call(1, c, c);
				alg.add().call(n, n, n);
				if (alg.isLessEqual().call(c, aPos)) {
					alg.subtract().call(aPos, c, aPos);
					alg.add().call(n, one, n);
				}
			}
			alg.assign().call(n, d);
			alg.assign().call(aPos, m);
		}
	}

	private static  & Ordered & Addition,U>
		void largestDoubling(T alg, Function2 test, U a, U b, U c)
	{
		U tmpB = alg.construct(b);
		U diff = alg.construct();
		alg.subtract().call(a, tmpB, diff);
		while (test.call(diff, tmpB)) {
			alg.add().call(tmpB, tmpB, tmpB);
			alg.subtract().call(a, tmpB, diff);
		}
		alg.assign().call(tmpB, c);
	}
	
}