123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695 |
- package me.hammerle.snuviscript.math;
- import java.util.TreeSet;
- public final class Fraction extends Number implements Comparable<Fraction>
- {
- private final long numerator;
- private final long denominator;
-
- public Fraction(long numerator, long denominator)
- {
- if(denominator == 0)
- {
- throw new ArithmeticException();
- }
-
- if(denominator != 1)
- {
- long divisor = getGreatestCommonDivisor(numerator, denominator);
- if(divisor != 1)
- {
- denominator /= divisor;
- numerator /= divisor;
- }
- }
-
- // short fraction
- if(denominator < 0)
- {
- this.denominator = -denominator;
- this.numerator = -numerator;
- }
- else
- {
- this.denominator = denominator;
- this.numerator = numerator;
- }
- }
-
- public Fraction(long numerator)
- {
- this(numerator, 1);
- }
-
- private static final long DOUBLE_FACTOR = 10000000000l;
-
- public static Fraction fromDouble(double d)
- {
- if(d == (long) d)
- {
- return new Fraction((long) d);
- }
- return new Fraction(Math.round(d * DOUBLE_FACTOR), DOUBLE_FACTOR);
- }
-
- // -------------------------------------------------------------------------
- // constants, chain fractions
- // -------------------------------------------------------------------------
-
- public static final Fraction PI = Fraction.getChainFraction(
- //3,7,15,1,292,1,1,1,2,1,3,1,14,2,1,1,2,2,2,2,1,84,2,1,1,15,3,13,1,4,2,6,6
- 3,7,15,1,292,1,1,1,2,1,3,1,14,2,1);
- public static final Fraction E = Fraction.getChainFraction(
- //2,1,2,1,1,4,1,1,6,1,1,8,1,1,10,1,1,12,1,1,14,1,1,16,...
- 2,1,2,1,1,4,1,1,6,1,1,8,1,1,10,1,1,12,1,1,14);
-
- public static Fraction getChainFraction(int... ints)
- {
- if(ints.length == 1)
- {
- return new Fraction(ints[0]);
- }
- return new Fraction(ints[0]).add(getChainFractionPart(1, ints));
- }
-
- private static Fraction getChainFractionPart(int index, int... ints)
- {
- if(index + 1 == ints.length)
- {
- return new Fraction(1, ints[index]);
- }
- return new Fraction(ints[index]).add(getChainFractionPart(index + 1, ints)).invert();
- }
-
- // -------------------------------------------------------------------------
- // basic calculating
- // -------------------------------------------------------------------------
-
- public Fraction add(Fraction f)
- {
- if(denominator == f.denominator)
- {
- return new Fraction(Math.addExact(numerator, f.numerator), denominator);
- }
- else
- {
- long l = getLeastCommonMultiple(denominator, f.denominator);
- return new Fraction(Math.addExact(Math.multiplyExact(numerator, l / denominator),
- Math.multiplyExact(f.numerator, l / f.denominator)), l);
- }
- }
-
- public Fraction mul(Fraction f)
- {
- return new Fraction(Math.multiplyExact(numerator, f.numerator),
- Math.multiplyExact(denominator, f.denominator));
- }
-
- public Fraction sub(Fraction f)
- {
- return add(f.invertSign());
- }
-
- public Fraction div(Fraction f)
- {
- return mul(f.invert());
- }
-
- // -------------------------------------------------------------------------
- // roots, power
- // -------------------------------------------------------------------------
-
- private static long power(long l, long power)
- {
- if(power == 1)
- {
- return l;
- }
- else if(power == 2)
- {
- return l * l;
- }
- long factor = 1;
- if(l < 0)
- {
- factor = (power & 1) == 1 ? -1 : 1;
- l = -l;
- }
- long prod = 1;
- while(power > 0)
- {
- if((power & 1) == 1)
- {
- prod = Math.multiplyExact(prod, l);
- }
- power = power >> 1;
- l = Math.multiplyExact(l, l);
- }
- if(factor == -1)
- {
- return -prod;
- }
- return prod;
- }
-
- private static long rootOfLong(long l, long root)
- {
- if(l == 0 || l == 1)
- {
- return l;
- }
- try
- {
- TreeSet<Long> tree = new TreeSet<>();
- long currentValue = l >> 1; // taking half as start value
- long an = currentValue;
- do
- {
- tree.add(currentValue);
- currentValue = currentValue - (currentValue / root) + an / power(currentValue, root - 1);
- }
- while(!tree.contains(currentValue));
- return currentValue;
- }
- catch(ArithmeticException ex)
- {
- return 0;
- }
- }
-
- private Fraction root(long root)
- {
- if(root == 1)
- {
- return this.copy();
- }
- // Getting nice start value
- Fraction currentValue;
- Fraction n = new Fraction(root);
- Fraction an = this.div(n);
- Fraction newFraction = new Fraction(rootOfLong(numerator, root), rootOfLong(denominator, root));
- root--;
- try
- {
- do
- {
- currentValue = newFraction;
- newFraction = currentValue.sub(currentValue.div(n)).add(an.div(currentValue.power(root)));
- }
- while(!newFraction.equals(currentValue));
- }
- catch(ArithmeticException ex)
- {
- }
- return newFraction;
- }
-
- public Fraction power(Fraction f)
- {
- if(numerator < 0 && f.denominator != 1)
- {
- throw new ArithmeticException("root of negative fraction");
- }
- try
- {
- if(f.numerator == 0)
- {
- return new Fraction(1);
- }
- else if(f.numerator < 0)
- {
- return this.invert().power(-f.numerator).root(f.denominator);
- }
- return this.power(f.numerator).root(f.denominator);
- }
- catch(ArithmeticException ex)
- {
- return fromDouble(Math.pow(this.doubleValue(), f.doubleValue()));
- }
- }
-
- private Fraction power(long p)
- {
- if(p < 0)
- {
- p = -p;
- invert();
- }
- else if(p == 1)
- {
- return this.copy();
- }
- long prodn = 1;
- long prodd = 1;
- long n = numerator;
- long d = denominator;
- while(p > 0)
- {
- if((p & 1) == 1)
- {
- prodn = Math.multiplyExact(prodn, n);
- prodd = Math.multiplyExact(prodd, d);
- }
- p = p >> 1;
- n = Math.multiplyExact(n, n);
- d = Math.multiplyExact(d, d);
- }
- return new Fraction(prodn, prodd);
- }
-
- // -------------------------------------------------------------------------
- // inverting
- // -------------------------------------------------------------------------
-
- public Fraction invertSign()
- {
- return new Fraction(-numerator, denominator);
- }
-
- public Fraction invert()
- {
- if(numerator == 0)
- {
- throw new ArithmeticException();
- }
- else if(numerator < 0)
- {
- return new Fraction(-denominator, -numerator);
- }
- return new Fraction(denominator, numerator);
- }
-
- public boolean isNegative()
- {
- return numerator < 0;
- }
-
- public boolean isLong()
- {
- return denominator == 1;
- }
-
- // -------------------------------------------------------------------------
- // functions from math library
- // -------------------------------------------------------------------------
-
- public Fraction abs()
- {
- return new Fraction(Math.abs(numerator), denominator);
- }
-
- public Fraction acos()
- {
- return Fraction.fromDouble(Math.acos(doubleValue()));
- }
-
- public Fraction asin()
- {
- return Fraction.fromDouble(Math.asin(doubleValue()));
- }
-
- public Fraction atan()
- {
- return Fraction.fromDouble(Math.atan(doubleValue()));
- }
-
- public Fraction atan2(Fraction f)
- {
- return Fraction.fromDouble(Math.atan2(doubleValue(), f.doubleValue()));
- }
-
- public Fraction cbrt()
- {
- return this.power(new Fraction(1, 3));
- }
-
- public Fraction ceil()
- {
- return Fraction.fromDouble(Math.ceil(doubleValue()));
- }
-
- public Fraction cos()
- {
- return Fraction.fromDouble(Math.cos(doubleValue()));
- }
-
- public Fraction cosh()
- {
- return Fraction.fromDouble(Math.cosh(doubleValue()));
- }
-
- public Fraction floor()
- {
- return Fraction.fromDouble(Math.floor(doubleValue()));
- }
-
- public Fraction log()
- {
- return Fraction.fromDouble(Math.log(doubleValue()));
- }
-
- public Fraction log10()
- {
- return Fraction.fromDouble(Math.log10(doubleValue()));
- }
-
- public Fraction log1p()
- {
- return Fraction.fromDouble(Math.log1p(doubleValue()));
- }
-
- public Fraction max(Fraction f)
- {
- if(this.compareTo(f) < 0)
- {
- return f;
- }
- return this;
- }
-
- public Fraction min(Fraction f)
- {
- if(this.compareTo(f) > 0)
- {
- return f;
- }
- return this;
- }
-
- public Fraction rint()
- {
- return Fraction.fromDouble(Math.rint(doubleValue()));
- }
-
- public Fraction round()
- {
- return Fraction.fromDouble(Math.round(doubleValue()));
- }
-
- public Fraction round(int times)
- {
- if(times < 0)
- {
- throw new IllegalArgumentException("a positive number of decimal points is needed");
- }
- int factor = 1;
- while(times > 0)
- {
- factor *= 10;
- times--;
- }
- double d = doubleValue() * factor;
- return new Fraction(Math.round(d), factor);
- }
-
- public Fraction signum()
- {
- if(numerator < 0)
- {
- return new Fraction(-1);
- }
- return new Fraction(1);
- }
-
- public Fraction sin()
- {
- return Fraction.fromDouble(Math.sin(doubleValue()));
- }
-
- public Fraction sinh()
- {
- return Fraction.fromDouble(Math.sinh(doubleValue()));
- }
-
- public Fraction tan()
- {
- return Fraction.fromDouble(Math.tan(doubleValue()));
- }
-
- public Fraction tanh()
- {
- return Fraction.fromDouble(Math.tanh(doubleValue()));
- }
-
- public Fraction toDegrees()
- {
- return Fraction.fromDouble(Math.toDegrees(doubleValue()));
- }
-
- public Fraction toRadians()
- {
- return Fraction.fromDouble(Math.toRadians(doubleValue()));
- }
-
- // -------------------------------------------------------------------------
- // simplifying
- // -------------------------------------------------------------------------
-
- public Fraction simplify(int times)
- {
- if(denominator == 1)
- {
- return this.copy();
- }
- long d = denominator;
- long n = numerator;
- int switcher = -1;
- for(int i = 0; i < times; i++)
- {
- if(switcher == -1 && d == 1)
- {
- d = (d & 1) == 1 ? d + 1: d;
- n = (n & 1) == 1 ? n + 1 : n;
- switcher = -1;
- }
- else if(switcher == 1 && d == -1)
- {
- d = (d & 1) == 1 ? d - 1: d;
- n = (n & 1) == 1 ? n - 1 : n;
- switcher = 1;
- }
- else
- {
- d = (d & 1) == 1 ? d + switcher: d;
- n = (n & 1) == 1 ? n + switcher : n;
- switcher = -switcher;
- }
-
- if(d != 1)
- {
- long divisor = getGreatestCommonDivisor(n, d);
- //System.out.println("DIV: " + divisor);
- if(divisor != 1)
- {
- d /= divisor;
- n /= divisor;
- }
- }
- }
- return new Fraction(n, d);
- }
-
- private long getGreatestCommonDivisor(long i, long n)
- {
- if(i == 0)
- {
- return n;
- }
- if(i < 0)
- {
- i = -i;
- }
- if(n < 0)
- {
- n = -n;
- }
- long helper;
- while(true)
- {
- if(i < n)
- {
- helper = i;
- i = n;
- n = helper;
- }
- i = i % n;
- if(i == 0)
- {
- return n;
- }
- }
- }
-
- private long getLeastCommonMultiple(long i, long n)
- {
- return Math.abs(Math.multiplyExact(i, n)) / getGreatestCommonDivisor(i, n);
- }
-
- // -------------------------------------------------------------------------
- // basic stuff
- // -------------------------------------------------------------------------
-
- @Override
- public String toString()
- {
- if(denominator == 1)
- {
- return String.valueOf(numerator);
- }
- return numerator + " / " + denominator;
- }
- private Fraction copy()
- {
- return new Fraction(numerator, denominator);
- }
- @Override
- public boolean equals(Object o)
- {
- if(o == null)
- {
- return false;
- }
- else if(o.getClass() != Fraction.class)
- {
- return false;
- }
- Fraction f = (Fraction) o;
- return numerator == f.numerator && denominator == f.denominator;
- }
- @Override
- public int hashCode()
- {
- int hash = 3;
- hash = 97 * hash + (int) (this.numerator ^ (this.numerator >>> 32));
- hash = 97 * hash + (int) (this.denominator ^ (this.denominator >>> 32));
- return hash;
- }
- @Override
- public int compareTo(Fraction f)
- {
- if(f.numerator < 0 && numerator >= 0)
- {
- return 1;
- }
- else if(f.numerator >= 0 && numerator < 0)
- {
- return -1;
- }
- else
- {
- long i = f.sub(this).numerator;
- if(i == 0)
- {
- return 0;
- }
- else if(i < 0)
- {
- return 1;
- }
- else
- {
- return -1;
- }
- }
- }
-
- // -------------------------------------------------------------------------
- // bit stuff
- // -------------------------------------------------------------------------
-
- private void noFraction()
- {
- if(denominator != 1 && numerator != 0)
- {
- throw new UnsupportedOperationException("the number must not be a fraction");
- }
- }
-
- public Fraction rightShift(int times)
- {
- noFraction();
- return new Fraction(numerator >> times);
- }
-
- public Fraction leftShift(int times)
- {
- noFraction();
- return new Fraction(numerator << times);
- }
-
- public Fraction and(Fraction f)
- {
- noFraction();
- return new Fraction(numerator & f.numerator);
- }
-
- public Fraction or(Fraction f)
- {
- noFraction();
- return new Fraction(numerator | f.numerator);
- }
-
- public Fraction xor(Fraction f)
- {
- noFraction();
- return new Fraction(numerator ^ f.numerator);
- }
-
- public Fraction invertBits()
- {
- noFraction();
- return new Fraction(~numerator);
- }
- public Fraction setBit(int n)
- {
- noFraction();
- return new Fraction(numerator | 1 << n);
- }
-
- public Fraction unsetBit(int n)
- {
- noFraction();
- return new Fraction(numerator & ~(1 << n));
- }
-
- public boolean getBit(int n)
- {
- noFraction();
- return (numerator & (1 << n)) != 0;
- }
-
- // -------------------------------------------------------------------------
- // number stuff
- // -------------------------------------------------------------------------
- @Override
- public int intValue()
- {
- return (int) longValue();
- }
- @Override
- public long longValue()
- {
- return numerator / denominator;
- }
- @Override
- public float floatValue()
- {
- return (float) doubleValue();
- }
- @Override
- public double doubleValue()
- {
- return numerator / (double) denominator;
- }
- }
|