package jscl.math; import jscl.JsclMathEngine; import jscl.math.function.Constant; import jscl.mathml.MathML; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.math.BigInteger; import java.util.Collections; import java.util.Set; public final class JsclInteger extends Generic { public static final JsclInteger factory = new JsclInteger(BigInteger.valueOf(0)); public static final JsclInteger ZERO = new JsclInteger(BigInteger.valueOf(0)); public static final JsclInteger ONE = new JsclInteger(BigInteger.valueOf(1)); private final BigInteger content; public JsclInteger(BigInteger content) { this.content = content; } public static JsclInteger valueOf(long val) { switch ((int) val) { case 0: return ZERO; case 1: return ONE; default: return new JsclInteger(BigInteger.valueOf(val)); } } public static JsclInteger valueOf(String str) { return new JsclInteger(new BigInteger(str)); } public BigInteger content() { return content; } public JsclInteger add(JsclInteger integer) { return new JsclInteger(content.add(integer.content)); } @Nonnull public Generic add(@Nonnull Generic that) { if (isZero()) { return that; } if (that instanceof JsclInteger) { return add((JsclInteger) that); } else { return that.valueOf(this).add(that); } } private boolean isZero() { return content.equals(ZERO.content); } public JsclInteger subtract(JsclInteger that) { if(isZero()) { return that.negate(); } return new JsclInteger(content.subtract(that.content)); } @Nonnull public Generic subtract(@Nonnull Generic that) { if (that instanceof JsclInteger) { return subtract((JsclInteger) that); } else { return that.valueOf(this).subtract(that); } } public JsclInteger multiply(JsclInteger integer) { return new JsclInteger(content.multiply(integer.content)); } @Nonnull public Generic multiply(@Nonnull Generic that) { if (isOne()) { return that; } if (that instanceof JsclInteger) { return multiply((JsclInteger) that); } else { return that.multiply(this); } } private boolean isOne() { return content.equals(ONE.content); } public JsclInteger divide(@Nonnull JsclInteger that) { if (isZero()) { return ZERO; } JsclInteger e[] = divideAndRemainder(that); if (e[1].signum() == 0) { return e[0]; } else { throw new NotDivisibleException(); } } @Nonnull public Generic divide(@Nonnull Generic that) throws NotDivisibleException { if (that instanceof JsclInteger) { return divide((JsclInteger) that); } else { return that.valueOf(this).divide(that); } } @Nonnull private JsclInteger[] divideAndRemainder(@Nonnull JsclInteger that) { try { final BigInteger result[] = content.divideAndRemainder(that.content); return new JsclInteger[]{new JsclInteger(result[0]), new JsclInteger(result[1])}; } catch (ArithmeticException e) { throw new NotDivisibleException(); } } public Generic[] divideAndRemainder(@Nonnull Generic that) { if (that instanceof JsclInteger) { return divideAndRemainder((JsclInteger) that); } else { return that.valueOf(this).divideAndRemainder(that); } } public JsclInteger remainder(JsclInteger integer) throws ArithmeticException { return new JsclInteger(content.remainder(integer.content)); } public Generic remainder(Generic generic) throws ArithmeticException { if (generic instanceof JsclInteger) { return remainder((JsclInteger) generic); } else { return generic.valueOf(this).remainder(generic); } } @Nonnull public JsclInteger gcd(@Nonnull JsclInteger integer) { return new JsclInteger(content.gcd(integer.content)); } public Generic gcd(@Nonnull Generic generic) { if (generic instanceof JsclInteger) { return gcd((JsclInteger) generic); } else { return generic.valueOf(this).gcd(generic); } } @Nonnull public Generic gcd() { return new JsclInteger(BigInteger.valueOf(signum())); } public Generic pow(int exponent) { return new JsclInteger(content.pow(exponent)); } public JsclInteger negate() { return new JsclInteger(content.negate()); } public int signum() { return content.signum(); } public int degree() { return 0; } public JsclInteger mod(JsclInteger that) { return new JsclInteger(content.mod(that.content)); } public JsclInteger modPow(JsclInteger exponent, JsclInteger integer) { return new JsclInteger(content.modPow(exponent.content, integer.content)); } public JsclInteger modInverse(JsclInteger integer) { return new JsclInteger(content.modInverse(integer.content)); } public JsclInteger phi() { if (signum() == 0) return this; Generic a = factorize(); Generic p[] = a.productValue(); Generic s = JsclInteger.valueOf(1); for (int i = 0; i < p.length; i++) { Power o = p[i].powerValue(); Generic q = o.value(true); int c = o.exponent(); s = s.multiply(q.subtract(JsclInteger.valueOf(1)).multiply(q.pow(c - 1))); } return s.integerValue(); } public JsclInteger[] primitiveRoots() { JsclInteger phi = phi(); Generic a = phi.factorize(); Generic p[] = a.productValue(); JsclInteger d[] = new JsclInteger[p.length]; for (int i = 0; i < p.length; i++) { d[i] = phi.divide(p[i].powerValue().value(true).integerValue()); } int k = 0; JsclInteger n = this; JsclInteger m = JsclInteger.valueOf(1); JsclInteger r[] = new JsclInteger[phi.phi().intValue()]; while (m.compareTo(n) < 0) { boolean b = m.gcd(n).compareTo(JsclInteger.valueOf(1)) == 0; for (int i = 0; i < d.length; i++) { b = b && m.modPow(d[i], n).compareTo(JsclInteger.valueOf(1)) > 0; } if (b) r[k++] = m; m = m.add(JsclInteger.valueOf(1)); } return k > 0 ? r : new JsclInteger[0]; } public JsclInteger sqrt() { return nthrt(2); } public JsclInteger nthrt(int n) { // return JsclInteger.valueOf((int)Math.pow((double)intValue(),1./n)); if (signum() == 0) { return JsclInteger.valueOf(0); } else if (signum() < 0) { if (n % 2 == 0) { throw new ArithmeticException("Could not calculate root of negative argument: " + this + " of odd order: " + n); } else { return (JsclInteger) ((JsclInteger) negate()).nthrt(n).negate(); } } else { Generic x0; Generic x = this; do { x0 = x; x = divideAndRemainder(x.pow(n - 1))[0].add(x.multiply(JsclInteger.valueOf(n - 1))).divideAndRemainder(JsclInteger.valueOf(n))[0]; } while (x.compareTo(x0) < 0); return x0.integerValue(); } } public Generic antiDerivative(@Nonnull Variable variable) throws NotIntegrableException { return multiply(variable.expressionValue()); } public Generic derivative(@Nonnull Variable variable) { return JsclInteger.valueOf(0); } public Generic substitute(@Nonnull Variable variable, Generic generic) { return this; } public Generic expand() { return this; } public Generic factorize() { return Factorization.compute(this); } public Generic elementary() { return this; } public Generic simplify() { return this; } public Generic numeric() { return new NumericWrapper(this); } public Generic valueOf(Generic generic) { return new JsclInteger(((JsclInteger) generic).content); } public Generic[] sumValue() { if (content.signum() == 0) return new Generic[0]; else return new Generic[]{this}; } public Generic[] productValue() throws NotProductException { if (content.compareTo(BigInteger.valueOf(1)) == 0) return new Generic[0]; else return new Generic[]{this}; } public Power powerValue() throws NotPowerException { if (content.signum() < 0) throw new NotPowerException(); else return new Power(this, 1); } public Expression expressionValue() throws NotExpressionException { return Expression.valueOf(this); } public JsclInteger integerValue() throws NotIntegerException { return this; } @Override public boolean isInteger() { return true; } public Variable variableValue() throws NotVariableException { throw new NotVariableException(); } public Variable[] variables() { return new Variable[0]; } public boolean isPolynomial(@Nonnull Variable variable) { return true; } public boolean isConstant(@Nonnull Variable variable) { return true; } public int intValue() { return content.intValue(); } public int compareTo(JsclInteger integer) { return content.compareTo(integer.content); } public int compareTo(Generic generic) { if (generic instanceof JsclInteger) { return compareTo((JsclInteger) generic); } else { return generic.valueOf(this).compareTo(generic); } } public String toString() { return JsclMathEngine.getInstance().format(content); } public String toJava() { return "JsclDouble.valueOf(" + content + ")"; } public void toMathML(MathML element, @Nullable Object data) { int exponent = data instanceof Integer ? (Integer) data : 1; if (exponent == 1) bodyToMathML(element); else { MathML e1 = element.element("msup"); bodyToMathML(e1); MathML e2 = element.element("mn"); e2.appendChild(element.text(String.valueOf(exponent))); e1.appendChild(e2); element.appendChild(e1); } } @Nonnull @Override public Set<? extends Constant> getConstants() { return Collections.emptySet(); } void bodyToMathML(MathML element) { MathML e1 = element.element("mn"); e1.appendChild(element.text(String.valueOf(content))); element.appendChild(e1); } @Override public BigInteger toBigInteger() { return content; } @Override public double doubleValue() throws NotDoubleException { return content.doubleValue(); } }