/*
* 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.arbitrary;
import org.jbasics.arrays.ArrayConstants;
import org.jbasics.arrays.IntArrayComparator;
import org.jbasics.math.arbitrary.internal.InternalCalculation;
import org.jbasics.math.arbitrary.internal.MagnitudeHelper;
import org.jbasics.math.obsolete.NumberConvert;
import java.math.BigInteger;
public class ArbitraryInteger implements ArbitraryNumber {
public static final ArbitraryInteger ZERO = new ArbitraryInteger(false, null);
public static final ArbitraryInteger ONE = new ArbitraryInteger(false, new int[]{1});
public static final ArbitraryInteger TWO = new ArbitraryInteger(false, new int[]{2});
public static final ArbitraryInteger MINUS_ONE = new ArbitraryInteger(true, new int[]{1});
private static final int[] INT_ARRAY_ONE = new int[]{1};
private final boolean negativ;
private final int[] magnitude;
// Construction
private ArbitraryInteger(boolean negativ, int[] magnitude) {
if (magnitude == null) {
this.negativ = false;
this.magnitude = ArrayConstants.ZERO_LENGTH_INT_ARRAY;
} else {
this.negativ = negativ;
this.magnitude = magnitude;
}
}
public static ArbitraryInteger valueOf(byte[] twoComplementByteArray) {
if (twoComplementByteArray == null || twoComplementByteArray.length == 0) {
return ZERO;
} else if (twoComplementByteArray.length == 1) {
switch (twoComplementByteArray[0]) {
case -1:
return MINUS_ONE;
case 0:
return ZERO;
case 1:
return ONE;
case 2:
return TWO;
}
}
boolean negativ = twoComplementByteArray[0] < 0;
int[] magnitude = NumberConvert.convert(twoComplementByteArray);
if (negativ) {
magnitude = NumberConvert.complement(magnitude);
}
return new ArbitraryInteger(negativ, magnitude);
}
public static ArbitraryInteger valueOf(int value) {
switch (value) {
case -1:
return MINUS_ONE;
case 0:
return ZERO;
case 1:
return ONE;
case 2:
return TWO;
default:
if (value < 0) {
return new ArbitraryInteger(true, new int[]{-value});
}
return new ArbitraryInteger(false, new int[]{value});
}
}
// Converting
public BigInteger toNumber() {
return new BigInteger(this.signum(), MagnitudeHelper.convertMagnitude(this.magnitude));
}
// Checking
public int signum() {
return this.magnitude.length == 0 ? 0 : this.negativ ? -1 : 1;
}
public boolean isNegativ() {
return this.negativ;
}
public boolean isPositiv() {
return this.magnitude.length != 0 && !this.negativ;
}
public boolean isZero() {
return this.magnitude.length == 0;
}
public ArbitraryInteger abs() {
return new ArbitraryInteger(false, this.magnitude);
}
// Unary Operations
public ArbitraryInteger negate() {
return new ArbitraryInteger(!this.negativ, this.magnitude);
}
public ArbitraryRational reciprocal() {
// We doing this in a special case for now since we didn't yet had a chance to find a way to
// use the special constants
if (this.negativ) {
return ArbitraryRational.valueOf(MINUS_ONE, this.negate());
} else {
return ArbitraryRational.valueOf(ONE, this);
}
}
public ArbitraryInteger square() {
return multiply(this);
}
public ArbitraryNumber increment() {
if (isZero()) {
return ONE;
} else if (this == MINUS_ONE) {
return ZERO;
} else if (this == ONE) {
return TWO;
} else if (this.magnitude.length == 1) {
switch (this.magnitude[0]) {
case -1:
return ZERO;
case 0:
return ONE;
case 1:
return TWO;
}
}
if (this.negativ) {
return new ArbitraryInteger(this.negativ, InternalCalculation.IMPL.subtract(this.magnitude, INT_ARRAY_ONE));
} else {
return new ArbitraryInteger(this.negativ, InternalCalculation.IMPL.add(this.magnitude, INT_ARRAY_ONE));
}
}
public ArbitraryNumber decrement() {
if (isZero()) {
return MINUS_ONE;
} else if (this == ONE) {
return ZERO;
} else if (this == TWO) {
return ONE;
} else if (this.magnitude.length == 1) {
switch (this.magnitude[0]) {
case 0:
return MINUS_ONE;
case 1:
return ZERO;
case 2:
return ONE;
case 3:
return TWO;
}
}
if (this.negativ) {
return new ArbitraryInteger(this.negativ, InternalCalculation.IMPL.add(this.magnitude, INT_ARRAY_ONE));
} else {
return new ArbitraryInteger(this.negativ, InternalCalculation.IMPL.subtract(this.magnitude, INT_ARRAY_ONE));
}
}
public ArbitraryInteger add(ArbitraryInteger that) {
if (this.isZero()) {
return that;
} else if (that.isZero()) {
return this;
} else if (this.negativ == that.negativ) {
return new ArbitraryInteger(this.negativ, InternalCalculation.IMPL.add(this.magnitude, that.magnitude));
} else {
int t = IntArrayComparator.compareArrays(this.magnitude, that.magnitude);
switch (t) {
case -1:
return new ArbitraryInteger(that.negativ, InternalCalculation.IMPL.subtract(that.magnitude,
this.magnitude));
case 0:
return ZERO;
case 1:
return new ArbitraryInteger(this.negativ, InternalCalculation.IMPL.subtract(this.magnitude,
that.magnitude));
default:
throw new IllegalStateException("Compares results in a number not in rang [-1, 1]");
}
}
}
// Binary integer operations
public ArbitraryInteger subtract(ArbitraryInteger that) {
if (this.isZero()) {
return new ArbitraryInteger(!that.negativ, that.magnitude);
} else if (that.isZero()) {
return this;
} else if (this.negativ != that.negativ) {
return new ArbitraryInteger(this.negativ, InternalCalculation.IMPL.add(this.magnitude, that.magnitude));
} else {
int t = IntArrayComparator.compareArrays(this.magnitude, that.magnitude);
switch (t) {
case -1:
return new ArbitraryInteger(!this.negativ, InternalCalculation.IMPL.subtract(that.magnitude,
this.magnitude));
case 0:
return ZERO;
case 1:
return new ArbitraryInteger(this.negativ, InternalCalculation.IMPL.subtract(this.magnitude,
that.magnitude));
default:
throw new IllegalStateException("Compares results in a number not in rang [-1, 1]");
}
}
}
public ArbitraryInteger multiply(ArbitraryInteger that) {
if (this.isZero() || that.isZero()) {
return ZERO;
} else if (this.negativ == that.negativ) {
return new ArbitraryInteger(false, InternalCalculation.IMPL.multiply(this.magnitude, that.magnitude));
} else {
return new ArbitraryInteger(true, InternalCalculation.IMPL.multiply(this.magnitude, that.magnitude));
}
}
public ArbitraryRational divide(ArbitraryInteger divisor) {
return ArbitraryRational.valueOf(this, divisor);
}
public ArbitraryRational add(ArbitraryRational summand) {
return summand.add(this);
}
public ArbitraryRational subtract(ArbitraryRational subtrahend) {
return subtrahend.negate().add(this);
}
// Binary rational operations
public ArbitraryRational multiply(ArbitraryRational factor) {
return factor.multiply(this);
}
public ArbitraryRational divide(ArbitraryRational divisor) {
return divisor.reciprocal().multiply(this);
}
public int bitLength() {
int result = this.magnitude.length * 32;
int t = this.magnitude[0];
int x = 1 << 31;
for (int i = 0; i < 32; i++) {
if ((t & x) != 0) {
break;
}
result--;
x = x >> 1;
}
return result;
}
@SuppressWarnings("all" /* since we want to allow the assignment of the parameter here */)
public ArbitraryInteger pow(int n) {
if (n < 0) {
throw new UnsupportedOperationException("negativ exponent currently unsupported since would lead to rational number x^-y = 1/x^y");
} else if (this.signum() == 0) {
return n == 0 ? ONE : ZERO;
} else if (n == 1) {
return this;
} else {
ArbitraryInteger a = this;
ArbitraryInteger b = n % 2 == 0 ? ONE : this;
while ((n >>>= 1) > 0) {
a = a.multiply(a);
if (n % 2 == 1) {
b = b.multiply(a);
}
}
return b;
}
}
}