package org.matheclipse.core.expression;
import static org.matheclipse.core.expression.F.List;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.math3.fraction.BigFraction;
import org.matheclipse.core.eval.EvalEngine;
import org.matheclipse.core.form.output.OutputFormFactory;
import org.matheclipse.core.interfaces.IAST;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.core.interfaces.IInteger;
import org.matheclipse.core.interfaces.INumber;
import org.matheclipse.core.interfaces.ISignedNumber;
import org.matheclipse.core.interfaces.ISymbol;
import org.matheclipse.core.visit.IVisitor;
import org.matheclipse.core.visit.IVisitorBoolean;
import org.matheclipse.core.visit.IVisitorInt;
/**
* IInteger implementation which simply delegates most of the methods to the BigInteger methods
*/
public class IntegerSym extends ExprImpl implements IInteger {
/**
* The BigInteger constant minus one.
*
*/
public static final BigInteger BI_MINUS_ONE = BigInteger.valueOf(-1L);
/**
* Be cautious with this method, no new internal BigInteger is created
*
* @param value
* @return
*/
protected static IntegerSym newInstance(final BigInteger value) {
IntegerSym z = new IntegerSym();
z.fInteger = value;
return z;
}
public static IntegerSym valueOf(final long value) {
IntegerSym z = new IntegerSym();
z.fInteger = BigInteger.valueOf(value);
return z;
}
/**
* Returns the IntegerImpl for the specified character sequence stated in the specified radix. The characters must all be digits
* of the specified radix, except the first character which may be a plus sign '+' or a minus sign '-'
* .
*
* @param chars
* the character sequence to parse.
* @param radix
* the radix to be used while parsing.
* @return the corresponding large integer.
* @throws NumberFormatException
* if the specified character sequence does not contain a parsable large integer.
*/
public static IntegerSym valueOf(final String integerString, final int radix) {
IntegerSym z = new IntegerSym();
z.fInteger = new BigInteger(integerString, radix);
return z;
}
/**
*
*/
private static final long serialVersionUID = 6389228668633533063L;
/* package private */BigInteger fInteger;
private transient int fHashValue = 0;
/**
* do not use directly, needed for XML transformations
*
*/
private IntegerSym() {
fInteger = null;
}
@Override
public boolean equalsInt(final int i) {
return fInteger.equals(BigInteger.valueOf(i));
}
/** {@inheritDoc} */
@Override
public IExpr evaluate(EvalEngine engine) {
if (engine.isNumericMode()) {
return numericNumber();
}
return null;
}
public final INumber numericNumber() {
return F.num(this);
}
@Override
public int hierarchy() {
return INTEGERID;
}
/**
* @param val
* @return
*/
@Override
public IInteger add(final IInteger val) {
return newInstance(fInteger.add(val.getBigNumerator()));
}
/**
* @param val
* @return
*/
@Override
public IInteger multiply(final IInteger val) {
return newInstance(fInteger.multiply(val.getBigNumerator()));
}
@Override
public boolean equals(final Object obj) {
if (hashCode() != obj.hashCode()) {
return false;
}
if (obj instanceof IntegerSym) {
if (this == obj) {
return true;
}
return fInteger.equals(((IntegerSym) obj).fInteger);
}
return false;
}
/**
* @param bigInteger
* @return
*/
public static IntegerSym valueOf(final BigInteger bigInteger) {
return newInstance(bigInteger);
}
/**
* @return
*/
@Override
public IntegerSym eabs() {
return newInstance(fInteger.abs());
}
/**
* @param that
* @return
*/
public IntegerSym add(final IntegerSym that) {
return newInstance(fInteger.add(that.fInteger));
}
/**
* @return
*/
public int bitLength() {
return fInteger.bitLength();
}
/**
* @param val
* @return
*/
public BigInteger divide(final long val) {
return fInteger.divide(BigInteger.valueOf(val));
}
/**
* @param that
* @return
*/
public BigInteger divide(final BigInteger that) {
return fInteger.divide(that);
}
public IntegerSym quotient(final IntegerSym that) {
return newInstance(fInteger.divide(that.fInteger));
}
/**
* @return
*/
@Override
public double doubleValue() {
return fInteger.doubleValue();
}
/**
* Returns the greatest common divisor of this large integer and the one specified.
*
*/
public IntegerSym gcd(final IntegerSym that) {
return newInstance(fInteger.gcd(that.fInteger));
}
/**
* Returns the greatest common divisor of this large integer and the one specified.
*
*/
@Override
public IInteger gcd(final IInteger that) {
return newInstance(fInteger.gcd(((IntegerSym) that).fInteger));
}
/**
* Returns the least common multiple of this large integer and the one specified.
*
*/
public IntegerSym lcm(final IntegerSym that) {
if (this.isZero() && that.isZero()) {
return (IntegerSym) F.C0;
}
BigInteger a = fInteger.abs();
BigInteger b = that.fInteger.abs();
BigInteger gcd = fInteger.gcd(b);
BigInteger lcm = (a.multiply(b)).divide(gcd);
return newInstance(lcm);
}
/**
* Returns the least common multiple of this large integer and the one specified.
*
*/
@Override
public IInteger lcm(final IInteger that) {
return lcm((IntegerSym) that);
}
/** {@inheritDoc} */
@Override
public final int hashCode() {
if (fHashValue == 0) {
fHashValue = fInteger.hashCode();
}
return fHashValue;
}
/**
* @param that
* @return
*/
public boolean isLargerThan(final BigInteger that) {
return fInteger.compareTo(that) > 0;
}
/** {@inheritDoc} */
@Override
public boolean isNegative() {
return fInteger.compareTo(BigInteger.ZERO) < 0;
}
/** {@inheritDoc} */
@Override
public boolean isNumEqualInteger(IInteger ii) throws ArithmeticException {
return equals(ii);
}
/** {@inheritDoc} */
@Override
public boolean isPositive() {
return fInteger.compareTo(BigInteger.ZERO) > 0;
}
@Override
public boolean isZero() {
return fInteger.equals(BigInteger.ZERO);
}
@Override
public boolean isOne() {
return fInteger.equals(BigInteger.ONE);
}
@Override
public boolean isMinusOne() {
return fInteger.equals(BI_MINUS_ONE);
}
@Override
public int intValue() {
return (int) fInteger.longValue();
}
/**
*
* @return
*/
@Override
public long longValue() {
return fInteger.longValue();
}
public IntegerSym mod(final IntegerSym that) {
return newInstance(fInteger.mod(that.fInteger));
}
/**
* @param that
* @return
*/
public IntegerSym multiply(final IntegerSym that) {
return newInstance(fInteger.multiply(that.fInteger));
}
/**
* @param val
* @return
*/
public BigInteger multiply(final long val) {
return fInteger.multiply(BigInteger.valueOf(val));
}
/**
* @return
*/
@Override
public ISignedNumber negate() {
return newInstance(fInteger.negate());
}
/**
* @return
*/
@Override
public ISignedNumber opposite() {
return newInstance(fInteger.negate());
}
@Override
public IExpr plus(final IExpr that) {
if (that instanceof IntegerSym) {
return this.add((IntegerSym) that);
}
if (isZero()) {
return that;
}
if (that instanceof FractionSym) {
return FractionSym.valueOf(fInteger).add((FractionSym) that);
}
return super.plus(that);
}
@Override
public ISignedNumber divideBy(ISignedNumber that) {
if (that instanceof IntegerSym) {
return FractionSym.valueOf(fInteger).divideBy(that);
}
if (that instanceof FractionSym) {
return FractionSym.valueOf(fInteger).divideBy(that);
}
return Num.valueOf(fInteger.doubleValue() / that.doubleValue());
}
@Override
public ISignedNumber subtractFrom(ISignedNumber that) {
if (that instanceof IntegerSym) {
return this.add((IntegerSym) that.negate());
}
if (isZero()) {
return that.negate();
}
if (that instanceof FractionSym) {
return FractionSym.valueOf(fInteger).subtractFrom(that);
}
return Num.valueOf(fInteger.doubleValue() - that.doubleValue());
}
/**
* @param exp
* @return
*/
@Override
public IntegerSym pow(final int exp) {
return newInstance(fInteger.pow(exp));
}
/**
* @return
*/
@Override
public ISignedNumber inverse() {
if (NumberUtil.isNegative(fInteger)) {
return FractionSym.valueOf(BigInteger.valueOf(-1), fInteger.negate());
}
return FractionSym.valueOf(BigInteger.ONE, fInteger);
}
/**
* @param n
* @return
*/
public BigInteger shiftLeft(final int n) {
return fInteger.shiftLeft(n);
}
/**
* @param n
* @return
*/
public BigInteger shiftRight(final int n) {
return fInteger.shiftRight(n);
}
/**
* @param that
* @return
*/
public BigInteger subtract(final BigInteger that) {
return fInteger.subtract(that);
}
@Override
public IInteger subtract(final IInteger that) {
return newInstance(fInteger.subtract(that.getBigNumerator()));
}
/**
* @param that
* @return
*/
@Override
public IExpr times(final IExpr that) {
if (that instanceof IntegerSym) {
return this.multiply((IntegerSym) that);
}
if (isZero()) {
return F.C0;
}
if (that instanceof FractionSym) {
return FractionSym.valueOf(fInteger).multiply((FractionSym) that);
}
return super.times(that);
}
public byte[] toByteArray() {
return fInteger.toByteArray();
}
/** {@inheritDoc} */
@Override
public BigInteger getBigNumerator() {
return fInteger;
}
/** {@inheritDoc} */
@Override
public IInteger getNumerator() {
return this;
}
/** {@inheritDoc} */
@Override
public IInteger getDenominator() {
return F.C1;
}
/** {@inheritDoc} */
@Override
public BigFraction getFraction() {
return new BigFraction(fInteger);
}
/**
* Get the factors of this integer
*
* @return
*/
public List factorize() {
final ArrayList result = new ArrayList();
IntegerSym b = this;
if (sign() < 0) {
b = b.multiply(IntegerSym.valueOf(-1));
result.add(IntegerSym.valueOf(-1));
} else if (b.fInteger.equals(BigInteger.ZERO)) {
result.add(IntegerSym.valueOf(0));
return result;
} else if (b.fInteger.equals(BigInteger.ONE)) {
result.add(IntegerSym.valueOf(1));
return result;
}
Map map = new TreeMap();
BigInteger rest = Primality.countPrimes1021(b.fInteger, map);
for (Map.Entry entry : map.entrySet()) {
int key = entry.getKey();
IntegerSym is = valueOf(key);
for (int i = 0; i < entry.getValue(); i++) {
result.add(is);
}
}
if (rest.equals(BigInteger.ONE)) {
return result;
}
b = valueOf(rest);
Map bigMap = new TreeMap();
Primality.pollardRhoFactors(b.getBigNumerator(), bigMap);
for (Map.Entry entry : bigMap.entrySet()) {
BigInteger key = entry.getKey();
IntegerSym is = valueOf(key);
for (int i = 0; i < entry.getValue(); i++) {
result.add(is);
}
}
//
// if (b.fInteger.isProbablePrime(32)) {
// result.add(b);
// return result;
// }
// TODO improve performance
// IntegerSym p = IntegerSym.valueOf(1023);
// while (true) {
// final IntegerSym q[] = b.divideAndRemainder(p);
// if (q[0].compareTo(p) < 0) {
// result.add(b);
// break;
// }
// if (q[1].sign() == 0) {
// result.add(p);
// b = q[0];
// } else {
// // test only odd integers
// p = p.add(IntegerSym.valueOf(2));
// }
// }
return result;
}
public IAST factorInteger() {
IInteger factor;
IInteger last = IntegerSym.valueOf(-2);
int count = 0;
final List iFactors = factorize();
final IAST list = List();
IAST subList = null;
for (int i = 0; i < iFactors.size(); i++) {
factor = iFactors.get(i);
if (!last.equals(factor)) {
if (subList != null) {
subList.add(IntegerSym.valueOf(count));
list.add(subList);
}
count = 0;
subList = List(factor);
}
count++;
last = factor;
}
if (subList != null) {
subList.add(IntegerSym.valueOf(count));
list.add(subList);
}
return list;
}
public IInteger eulerPhi() throws ArithmeticException {
IAST ast = factorInteger();
IInteger phi = IntegerSym.valueOf(1);
for (int i = 1; i < ast.size(); i++) {
IAST element = (IAST) ast.get(i);
IntegerSym q = (IntegerSym) element.get(1);
int c = ((IInteger) element.get(2)).toInt();
if (c == 1) {
phi = phi.multiply(q.subtract(IntegerSym.valueOf(1)));
} else {
phi = phi.multiply(q.subtract(IntegerSym.valueOf(1)).multiply(q.pow(c - 1)));
}
}
return phi;
}
public IntegerSym moebiusMu() {
if (this.compareTo(IntegerSym.valueOf(1)) == 0) {
return IntegerSym.valueOf(1);
}
IAST ast = factorInteger();
IntegerSym max = IntegerSym.valueOf(1);
for (int i = 1; i < ast.size(); i++) {
IAST element = (IAST) ast.get(i);
IntegerSym c = (IntegerSym) element.get(2);
if (c.compareTo(max) > 0) {
max = c;
}
}
if (max.compareTo(IntegerSym.valueOf(1)) > 0) {
return IntegerSym.valueOf(0);
}
if (((ast.size() - 1) & 0x00000001) == 0x00000001) {
// odd number
return IntegerSym.valueOf(-1);
}
return IntegerSym.valueOf(1);
}
private IntegerSym jacobiSymbolF() {
IntegerSym a = mod(IntegerSym.valueOf(8));
if (a.compareTo(IntegerSym.valueOf(1)) == 0) {
return IntegerSym.valueOf(1);
}
if (a.compareTo(IntegerSym.valueOf(7)) == 0) {
return IntegerSym.valueOf(1);
}
return IntegerSym.valueOf(-1);
}
private IntegerSym jacobiSymbolG(IntegerSym i2) {
IntegerSym a = mod(IntegerSym.valueOf(4));
if (a.compareTo(IntegerSym.valueOf(1)) == 0) {
return IntegerSym.valueOf(1);
}
IntegerSym b = mod(IntegerSym.valueOf(4));
if (b.compareTo(IntegerSym.valueOf(1)) == 0) {
return IntegerSym.valueOf(1);
}
return IntegerSym.valueOf(-1);
}
public IntegerSym jacobiSymbol(IntegerSym b) {
if (this.isOne()) {
return IntegerSym.valueOf(1);
}
if (this.compareTo(IntegerSym.valueOf(2)) == 0) {
return b.jacobiSymbolF();
}
if (!isOdd()) {
return this.quotient(IntegerSym.valueOf(2)).jacobiSymbol(b).multiply(IntegerSym.valueOf(2).jacobiSymbol(b));
}
return b.quotient(this).jacobiSymbol(this).multiply(jacobiSymbolG(b));
}
/**
* The primitive roots of this integer number
*
* @return the primitive roots
* @throws ArithmeticException
*/
public IInteger[] primitiveRoots() throws ArithmeticException {
IntegerSym phi = (IntegerSym) eulerPhi();
int size = phi.eulerPhi().toInt();
if (size <= 0) {
return null;
}
IAST ast = phi.factorInteger();
IntegerSym d[] = new IntegerSym[ast.size() - 1];
IAST element;
for (int i = 1; i < ast.size(); i++) {
element = (IAST) ast.get(i);
IntegerSym q = (IntegerSym) element.get(1);
d[i - 1] = phi.quotient(q);
}
int k = 0;
IntegerSym n = this;
IntegerSym m = IntegerSym.valueOf(1);
IntegerSym resultArray[] = new IntegerSym[size];
boolean b;
while (m.compareTo(n) < 0) {
b = m.gcd(n).compareTo(IntegerSym.valueOf(1)) == 0;
for (int i = 0; i < d.length; i++) {
b = b && m.modPow(d[i], n).compareTo(IntegerSym.valueOf(1)) > 0;
}
if (b) {
resultArray[k++] = m;
}
m = m.add(IntegerSym.valueOf(1));
}
if (resultArray[0] == null) {
return new IntegerSym[0];
}
return resultArray;
}
public int compareTo(final IntegerSym that) {
return fInteger.compareTo(that.fInteger);
}
public IntegerSym[] divideAndRemainder(final IntegerSym that) {
final IntegerSym[] res = new IntegerSym[2];
BigInteger[] largeRes = fInteger.divideAndRemainder(that.fInteger);
res[0] = newInstance(largeRes[0]);
res[1] = newInstance(largeRes[1]);
return res;
}
@Override
public boolean isEven() {
return NumberUtil.isEven(fInteger);
}
@Override
public boolean isOdd() {
return NumberUtil.isOdd(fInteger);
}
public IntegerSym modInverse(final IntegerSym m) {
return newInstance(fInteger.modInverse(m.fInteger));
}
public IntegerSym modPow(final IntegerSym exp, final IntegerSym m) {
return newInstance(fInteger.modPow(exp.fInteger, m.fInteger));
}
/**
* {@inheritDoc}
*/
@Override
public int toInt() throws ArithmeticException {
return NumberUtil.toInt(fInteger);
}
/**
* {@inheritDoc}
*/
@Override
public long toLong() throws ArithmeticException {
return NumberUtil.toLong(fInteger);
}
@Override
public int sign() {
return fInteger.signum();
}
/**
* Returns the integer square root of this integer.
*
* @return k such as k^2 <= this < (k + 1)^2
* @throws ArithmeticException
* if this integer is negative.
*/
public IInteger sqrt() throws ArithmeticException {
return nthRoot(2);
}
/**
* Returns the nth-root of this integer.
*
* @return k such as k^n <= this < (k + 1)^n
* @throws ArithmeticException
* if this integer is negative and n is even.
*/
@Override
public IInteger nthRoot(int n) throws ArithmeticException {
if (sign() == 0) {
return IntegerSym.valueOf(0);
} else if (sign() < 0) {
if (n % 2 == 0) {
// even exponent n
throw new ArithmeticException();
} else {
// odd exponent n
return (IntegerSym) ((IntegerSym) negate()).nthRoot(n).negate();
}
} else {
IntegerSym result;
IntegerSym temp = this;
do {
result = temp;
temp = divideAndRemainder(temp.pow(n - 1))[0].add(temp.multiply(IntegerSym.valueOf(n - 1))).divideAndRemainder(
IntegerSym.valueOf(n))[0];
} while (temp.compareTo(result) < 0);
return result;
}
}
/**
* Split this integer into the nth-root (with prime factors less equal 1021) and the "rest-factor", so that
* this== (nth-root)^n + rest
*
* @return {nth-root, rest}
*/
@Override
public IInteger[] nthRootSplit(int n) throws ArithmeticException {
IInteger[] result = new IInteger[2];
if (sign() == 0) {
result[0] = IntegerSym.valueOf(0);
result[1] = IntegerSym.valueOf(1);
return result;
} else if (sign() < 0) {
if (n % 2 == 0) {
// even exponent n
throw new ArithmeticException();
} else {
// odd exponent n
result = ((IntegerSym) negate()).nthRootSplit(n);
result[1] = (IInteger) result[1].negate();
return result;
}
}
IntegerSym b = this;
Map map = new TreeMap();
BigInteger rest = Primality.countPrimes1021(b.fInteger, map);
IntegerSym nthRoot = IntegerSym.valueOf(1);
IntegerSym restFactors = IntegerSym.valueOf(rest);
for (Map.Entry entry : map.entrySet()) {
IntegerSym primeLE1021 = valueOf(entry.getKey());
int primeCounter = entry.getValue();
int div = primeCounter / n;
if (div > 0) {
// build nth-root
nthRoot = nthRoot.multiply(primeLE1021.pow(div));
}
int mod = primeCounter % n;
if (mod > 0) {
// build rest factor
restFactors = restFactors.multiply(primeLE1021.pow(mod));
}
}
result[0] = nthRoot;
result[1] = restFactors;
return result;
}
@Override
public int complexSign() {
return sign();
}
@Override
public IInteger ceil() {
return this;
}
@Override
public IInteger floor() {
return this;
}
@Override
public IInteger round() {
return this;
}
/**
* Compares this expression with the specified expression for order. Returns a negative integer, zero, or a positive integer as
* this expression is canonical less than, equal to, or greater than the specified expression.
*/
@Override
public int compareTo(final IExpr obj) {
if (obj instanceof IntegerSym) {
return fInteger.compareTo(((IntegerSym) obj).fInteger);
}
if (obj instanceof FractionSym) {
return -((FractionSym) obj).fRational.compareTo(new BigFraction(fInteger, BigInteger.ONE));
}
return (hierarchy() - (obj).hierarchy());
}
@Override
public boolean isLessThan(ISignedNumber obj) {
if (obj instanceof IntegerSym) {
return fInteger.compareTo(((IntegerSym) obj).fInteger) < 0;
}
if (obj instanceof FractionSym) {
return -((FractionSym) obj).fRational.compareTo(new BigFraction(fInteger, BigInteger.ONE)) < 0;
}
return fInteger.doubleValue() < obj.doubleValue();
}
@Override
public boolean isGreaterThan(ISignedNumber obj) {
if (obj instanceof IntegerSym) {
return fInteger.compareTo(((IntegerSym) obj).fInteger) > 0;
}
if (obj instanceof FractionSym) {
return -((FractionSym) obj).fRational.compareTo(new BigFraction(fInteger, BigInteger.ONE)) > 0;
}
return fInteger.doubleValue() < obj.doubleValue();
}
@Override
public ISymbol head() {
return F.IntegerHead;
}
@Override
public String toString() {
try {
StringBuilder sb = new StringBuilder();
OutputFormFactory.get().convertInteger(sb, this, Integer.MIN_VALUE);
return sb.toString();
} catch (Exception e1) {
}
// fall back to simple output format
return fInteger.toString();
}
@Override
public String internalFormString(boolean symbolsAsFactoryMethod, int depth) {
int value = NumberUtil.toInt(fInteger);
switch (value) {
case -1:
return "CN1";
case -2:
return "CN2";
case -3:
return "CN3";
case -4:
return "CN4";
case -5:
return "CN5";
case 0:
return "C0";
case 1:
return "C1";
case 2:
return "C2";
case 3:
return "C3";
case 4:
return "C4";
case 5:
return "C5";
}
return "ZZ(" + value + "L)";
}
/** {@inheritDoc} */
@Override
public T accept(IVisitor visitor) {
return visitor.visit(this);
}
/**
* {@inheritDoc}
*/
@Override
public boolean accept(IVisitorBoolean visitor) {
return visitor.visit(this);
}
/**
* {@inheritDoc}
*/
@Override
public int accept(IVisitorInt visitor) {
return visitor.visit(this);
}
/** {@inheritDoc} */
@Override
public ISignedNumber getIm() {
return F.C0;
}
/** {@inheritDoc} */
@Override
public ISignedNumber getRe() {
return this;
}
}
Ads help maintain this website.