/*
* Copyright (c) 2009-2015
* IT-Consulting Stephan Schloepke (http://www.schloepke.de/)
* klemm software consulting Mirko Klemm (http://www.klemm-scs.com/)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jbasics.math;
import org.jbasics.math.impl.ExponentialIrationalNumber;
import org.jbasics.testing.Java14LoggingTestCase;
import org.junit.Assert;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
public class BigRationalTest extends Java14LoggingTestCase {
public final static IrationalNumber<BigDecimal> E = ExponentialIrationalNumber.valueOf(BigDecimal.ONE);
@Test
public void testConstructor() {
BigInteger numerator = BigInteger.valueOf(17L);
BigInteger denominator = BigInteger.valueOf(23L);
BigRational test = new BigRational(numerator, denominator);
Assert.assertEquals(new BigRational(17, 23), test);
Assert.assertEquals(numerator, test.numerator());
Assert.assertEquals(denominator, test.denominator());
test = new BigRational(numerator, denominator.negate());
Assert.assertEquals(new BigRational(-17, 23), test);
Assert.assertEquals(numerator.negate(), test.numerator());
Assert.assertEquals(denominator, test.denominator());
try {
test = new BigRational(null, null);
Assert.fail("BigRational constructor accepts null values for numerator and/or denominator");
} catch (IllegalArgumentException e) {
// correct
}
try {
test = new BigRational(numerator, BigInteger.ZERO);
Assert.fail("BigRational accepts a zero denominator");
} catch (ArithmeticException e) {
// correct
}
test = new BigRational(numerator.longValue(), denominator.longValue());
Assert.assertEquals(numerator, test.numerator());
Assert.assertEquals(denominator, test.denominator());
}
@Test
public void testStaticValueOf() {
Assert.assertEquals(new BigRational(5, 1), BigRational.valueOf(5L));
Assert.assertEquals(new BigRational(-4, 1), BigRational.valueOf(-4L));
Assert.assertEquals(new BigRational(1, 2), BigRational.valueOf(0.5d));
Assert.assertEquals(new BigRational(-1, 8), BigRational.valueOf(new BigDecimal("-0.125")));
Assert.assertSame(BigRational.ZERO, BigRational.valueOf(BigDecimal.ZERO));
Assert.assertSame(BigRational.ONE, BigRational.valueOf(BigDecimal.ONE));
try {
BigRational.valueOf((BigDecimal) null);
Assert.fail("BigRational accepts a null value for valueOf(BigDecimal)");
} catch (IllegalArgumentException e) {
// this is ok since expected
}
Assert.assertEquals(new BigRational(3, 4), BigRational.valueOf("-3/-4"));
Assert.assertEquals(new BigRational(-7, 1), BigRational.valueOf("-7"));
try {
BigRational.valueOf((String) null);
Assert.fail("BigRational accepts a null value for valueOf(String)");
} catch (IllegalArgumentException e) {
// this is ok since expected
}
try {
BigRational.valueOf("1/2/3");
Assert.fail("BigRational accepts a multiple divide elements in value for valueOf(String)");
} catch (IllegalArgumentException e) {
// this is ok since expected
}
}
@Test
public void testNumberConvert() {
BigRational test = new BigRational(42, 12);
Assert.assertEquals(3.5d, test.doubleValue(), 0.0d);
Assert.assertEquals(3, test.intValue());
Assert.assertEquals(3L, test.longValue());
Assert.assertEquals(3.5d, test.floatValue(), 0.0d);
// Number interface extension to be used with BigClasses
Assert.assertEquals(new BigDecimal("3.5"), test.decimalValue());
Assert.assertEquals(BigInteger.valueOf(3L), test.toBigInteger());
Assert.assertEquals(BigInteger.valueOf(6L), test.remainder().numerator());
Assert.assertEquals(BigInteger.valueOf(6L), test.gcd());
Assert.assertEquals(BigInteger.valueOf(3L), BigRational.valueOf(3L).toBigIntegerExact());
try {
test.toBigIntegerExact();
Assert.fail("toBigIntegerExact does not throw an arithmetic exception if a remainder exists");
} catch (ArithmeticException e) {
// this is correct.
}
try {
test.decimalValue(null);
Assert.fail("BigRational accepts null value for decimalValue(MathContext)");
} catch (IllegalArgumentException e) {
// Expected
}
}
@Test
public void testHashCodeAndEquals() {
BigRational test = BigRational.valueOf("42/12");
BigRational testTwo = BigRational.valueOf("7/2");
BigRational testThree = BigRational.valueOf("42/12");
BigRational testFour = BigRational.valueOf("48/12");
Assert.assertEquals(test, test);
Assert.assertFalse(test.equals(null));
Assert.assertFalse(test.equals(test.decimalValue()));
Assert.assertNotSame(test, testThree);
Assert.assertEquals(test, testThree);
Assert.assertFalse(test.equals(testTwo));
Assert.assertFalse(test.hashCode() == testTwo.hashCode());
Assert.assertTrue(test.hashCode() == testThree.hashCode());
Assert.assertFalse(test.equals(testFour));
Assert.assertTrue(test.reduce().hashCode() == testTwo.hashCode());
Assert.assertTrue(testThree.reduce().equals(testTwo));
}
@Test
public void testComparable() {
BigRational test = BigRational.valueOf("42/12");
BigRational testTwo = BigRational.valueOf("7/2");
BigRational testThree = BigRational.valueOf("40/12");
BigRational testFour = BigRational.valueOf("48/12");
Assert.assertTrue(test.compareTo(testTwo) == 0);
Assert.assertTrue(test.compareTo(testThree) > 0);
Assert.assertTrue(test.compareTo(testFour) < 0);
Assert.assertTrue(testTwo.compareTo(test) == 0);
Assert.assertTrue(testThree.compareTo(test) < 0);
Assert.assertTrue(testFour.compareTo(test) > 0);
try {
test.compareTo(null);
Assert.fail("BigRational accepts null value for compareTo(BigRatio)");
} catch (IllegalArgumentException e) {
// EXPECTED
}
}
@Test
public void testReduce() {
BigRational reducable = new BigRational(2L, 4L);
BigRational reduced = reducable.reduce();
BigRational notreducable = reduced.reduce();
Assert.assertNotSame(reducable, reduced);
Assert.assertSame(reduced, notreducable);
Assert.assertEquals(new BigRational(1L, 2L), reduced);
}
@Test
public void testExtend() {
BigRational test = BigRational.valueOf("7/2");
BigRational temp = test.extend(5L);
Assert.assertNotSame(test, temp);
Assert.assertEquals("35/10", temp.toString());
Assert.assertEquals(BigInteger.valueOf(5L), temp.gcd());
test = BigRational.valueOf("5/1");
temp = test.extend(4L);
Assert.assertNotSame(test, temp);
Assert.assertEquals("20/4", temp.toString());
Assert.assertEquals(BigInteger.valueOf(4L), temp.gcd());
test = BigRational.valueOf("1/3");
temp = test.extend(7L);
Assert.assertNotSame(test, temp);
Assert.assertEquals("7/21", temp.toString());
Assert.assertEquals(BigInteger.valueOf(7L), temp.gcd());
try {
test.extend(null);
Assert.fail("BigRational.extend(BigRational) accepts null");
} catch (IllegalArgumentException e) {
// expected
}
try {
test.extend(BigInteger.ZERO);
Assert.fail("BigRational.extend(BigRational) accepts zero");
} catch (ArithmeticException e) {
// expected
}
test = BigRational.valueOf("1/3");
temp = BigRational.valueOf("1/6");
BigInteger lcm = test.lcm(temp);
Assert.assertEquals(BigInteger.valueOf(6L), lcm);
Assert.assertEquals(BigRational.valueOf("2/6"), test.extendToMultiple(lcm));
Assert.assertEquals(BigRational.valueOf("1/6"), temp.extendToMultiple(lcm));
try {
test.extendToMultiple(BigInteger.valueOf(7L));
Assert.fail("BigRational.extendToMultiple(BigInteger) accepts a value which is not a multiple of the denominator");
} catch (ArithmeticException e) {
// expected
}
try {
test.extendToMultiple(null);
Assert.fail("BigRational.extendToMultiple(BigInteger) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
try {
test.extendToMultiple(BigInteger.ONE);
Assert.fail("BigRational.extendToMultiple(BigInteger) accepts a value less than or equal to one");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testLcm() {
BigRational test = new BigRational(1L, 2L);
BigRational test2 = new BigRational(3L, 4L);
BigInteger lcm = test.lcm(test2);
Assert.assertEquals(BigInteger.valueOf(4L), lcm);
try {
test.lcm(null);
Assert.fail("BigRational.lcm(BigRational) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testGCD() {
BigRational test = new BigRational(2L, 4L);
BigInteger gcd = test.gcd();
Assert.assertEquals(BigInteger.valueOf(2L), gcd);
}
@Test
public void testReciprocal() {
BigRational test = BigRational.valueOf("1/5");
BigRational temp = test.reciprocal();
Assert.assertNotSame(test, temp);
Assert.assertEquals("5", temp.toString());
BigRational back = temp.reciprocal();
Assert.assertNotSame(test, back);
Assert.assertNotSame(temp, back);
Assert.assertEquals("1/5", back.toString());
}
@Test
public void testNegateAndDeMorgan() {
BigRational test = BigRational.valueOf("1/2");
Assert.assertEquals(test, test.negate().negate());
}
@Test
public void testAbs() {
BigRational test = BigRational.valueOf("-20/3");
Assert.assertEquals(new BigRational(20, 3), test.abs());
test = test.abs();
Assert.assertEquals(new BigRational(20, 3), test.abs());
Assert.assertSame(test, test.abs());
}
@Test
public void testAddNumerator() {
BigRational test = BigRational.valueOf("-15/7");
BigRational temp = test.addNumerator(BigInteger.valueOf(30L));
Assert.assertNotSame(test, temp);
Assert.assertEquals("15/7", temp.toString());
try {
test.addNumerator(null);
Assert.fail("BigRational.addNumerator(BigInteger) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testSubtractNumerator() {
BigRational test = BigRational.valueOf("15/7");
BigRational temp = test.subtractNumerator(BigInteger.valueOf(30L));
Assert.assertNotSame(test, temp);
Assert.assertEquals("-15/7", temp.toString());
try {
test.subtractNumerator(null);
Assert.fail("BigRational.subtractNumerator(BigInteger) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testAdd() {
BigRational test = BigRational.valueOf("1/1");
BigRational temp = test.add(BigRational.valueOf("2/3"));
Assert.assertNotSame(test, temp);
Assert.assertEquals("5/3", temp.toString());
BigRational temp2 = temp.add(BigRational.valueOf("1/3"));
Assert.assertNotSame(temp, temp2);
Assert.assertEquals("6/3", temp2.toString());
try {
test.add((BigRational) null);
Assert.fail("BigRational.add(BigRational) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
temp = test.add(BigInteger.valueOf(2L));
Assert.assertNotSame(test, temp);
Assert.assertEquals("3", temp.toString());
try {
test.add((BigInteger) null);
Assert.fail("BigRational.add(BigInteger) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
temp = test.add(new BigDecimal("2.5"));
Assert.assertNotSame(test, temp);
Assert.assertEquals("7/2", temp.toString());
try {
test.add((BigDecimal) null);
Assert.fail("BigRational.add(BigDecimal) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testSubtract() {
BigRational test = BigRational.valueOf("7/2");
BigRational temp = test.subtract(BigRational.valueOf("2/3"));
Assert.assertNotSame(test, temp);
Assert.assertEquals("17/6", temp.toString());
BigRational temp2 = temp.subtract(BigRational.valueOf("1/3"));
Assert.assertNotSame(temp, temp2);
Assert.assertEquals("15/6", temp2.toString());
try {
test.subtract((BigRational) null);
Assert.fail("BigRational.subtract(BigRational) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
temp = test.subtract(BigInteger.valueOf(2L));
Assert.assertNotSame(test, temp);
Assert.assertEquals("3/2", temp.toString());
try {
test.subtract((BigInteger) null);
Assert.fail("BigRational.subtract(BigInteger) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
temp = test.subtract(new BigDecimal("2.5"));
Assert.assertNotSame(test, temp);
Assert.assertEquals("2/2", temp.toString());
try {
test.subtract((BigDecimal) null);
Assert.fail("BigRational.subtract(BigDecimal) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testMultiply() {
BigRational test = new BigRational(2L, 3L);
BigRational temp = test.multiply(new BigRational(7L, 6L));
Assert.assertNotSame(test, temp);
Assert.assertEquals(BigRational.valueOf("14/18"), temp);
try {
test.multiply((BigRational) null);
Assert.fail("BigRational.multiply(BigRational) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
temp = test.multiply(new BigDecimal("1.75"));
Assert.assertEquals("14/12", temp.toString());
temp = test.multiply(BigInteger.valueOf(5L));
Assert.assertEquals("10/3", temp.toString());
try {
test.multiply((BigInteger) null);
Assert.fail("BigRational.multiply(BigInteger) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testDivide() {
BigRational test = new BigRational(2L, 3L);
BigRational temp = test.divide(new BigRational(7L, 6L));
Assert.assertNotSame(test, temp);
Assert.assertEquals(new BigRational(12, 21), temp);
try {
test.divide((BigRational) null);
Assert.fail("BigRational.divide(BigRational) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
temp = test.divide(new BigDecimal("1.75"));
Assert.assertEquals(new BigRational(8, 21), temp);
temp = test.divide(BigInteger.valueOf(5L));
Assert.assertEquals(new BigRational(2, 15), temp);
try {
test.divide((BigInteger) null);
Assert.fail("BigRational.divide(BigInteger) accepts null values");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testPow() {
BigRational test = new BigRational(2L, 3L);
Assert.assertEquals(new BigRational(4, 9), test.pow(2));
Assert.assertEquals(BigRational.ONE, test.pow(0));
Assert.assertEquals(BigRational.ONE, BigRational.ZERO.pow(100));
}
@Test
public void testRealUse() throws IOException {
final int steps = 13;
BigDecimal startX = new BigDecimal("3.54632");
BigRational x = BigRational.valueOf(startX);
BigInteger intPart = x.toBigInteger();
x = x.remainder().multiply(new BigRational(1, 64));
BigRational[] xPows = new BigRational[steps];
xPows[0] = BigRational.ONE;
xPows[1] = x;
for (int i = 2; i < steps; i++) {
xPows[i] = xPows[i - 1].multiply(x);
}
long start = System.currentTimeMillis();
BigRational[] results = new BigRational[steps];
results[0] = BigRational.ONE; // 1/0! is 1/1
for (int i = 1; i < results.length; i++) {
results[i] = results[i - 1].extend(i).extend(x.denominator()).addNumerator(xPows[i].numerator());
}
long first = System.currentTimeMillis() - start;
start = System.currentTimeMillis();
BigRational[] resultsSlow = new BigRational[steps];
resultsSlow[0] = BigRational.ONE;
BigRational faculty = new BigRational(1, 1);
for (int i = 1; i < resultsSlow.length; i++) {
faculty = faculty.multiply(new BigRational(1, i));
resultsSlow[i] = resultsSlow[i - 1].add(xPows[i].multiply(faculty)).reduce();
}
long second = System.currentTimeMillis() - start;
this.logger.info("Slow / Fast E calculation: " + second + "ms/" + first + "ms");
ByteArrayOutputStream slow = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(slow);
o.writeObject(resultsSlow);
o.close();
ByteArrayOutputStream fast = new ByteArrayOutputStream();
o = new ObjectOutputStream(fast);
o.writeObject(results);
o.close();
this.logger.info("Slow / Fast E calc total memory: " + slow.size() + "/" + fast.size());
for (int i = 0; i < 10; i++) {
this.logger.info("Slow / Fast values: " + resultsSlow[i] + " == " + results[i]);
}
this.logger.info("Slow / Fast rational value: " + resultsSlow[steps - 1].reduce() + " == "
+ results[steps - 1].reduce());
this.logger.info("Slow / Fast decimal value: " + resultsSlow[steps - 1].decimalValue(MathContext.DECIMAL128)
+ " == " + results[steps - 1].decimalValue(MathContext.DECIMAL128));
this.logger.info("Highest faculty: " + faculty.denominator());
this.logger.info("Highest faculty decimal: " + faculty.decimalValue(MathContext.DECIMAL128));
final BigRational endResult = results[steps - 1].reduce();
this.logger.info("Highest numerator : " + endResult.numerator() + " ("
+ endResult.numerator().toString().length() + ")");
this.logger.info("Highest denominator: " + endResult.denominator() + " ("
+ endResult.denominator().toString().length() + ")");
BigRational dist = xPows[steps - 1].multiply(faculty).reduce();
this.logger.info("Distance: " + dist.decimalValue(new MathContext(1)) + " (" + dist + ")");
dist = xPows[steps - 1].multiply(x).multiply(faculty).multiply(new BigRational(1, steps)).reduce();
this.logger.info("Distance Next: " + dist.decimalValue(new MathContext(1)) + " (" + dist + ")");
this.logger.info("Econst = " + BigDecimalMathLibrary.exp(startX).valueToPrecision(MathContext.DECIMAL128));
this.logger.info("Ecalc = "
+ results[steps - 1].reduce().pow(64).multiply(
BigRational.valueOf(
BigRationalTest.E.valueToPrecision(new MathContext(MathContext.DECIMAL128.getPrecision() + 3,
MathContext.DECIMAL128.getRoundingMode()))).pow(intPart.intValue()))
.decimalValue(MathContext.DECIMAL128));
BigInteger t = faculty.denominator();
long scale = 0;
while (t.signum() != 0) {
t = t.divide(BigInteger.TEN);
scale++;
this.logger.info("t = " + t);
}
this.logger.info("Num scale: " + scale);
}
}