Skip to content

Instantly share code, notes, and snippets.

@Darksonn
Created December 13, 2013 12:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Darksonn/7943674 to your computer and use it in GitHub Desktop.
Save Darksonn/7943674 to your computer and use it in GitHub Desktop.
Calculates the logarithm of an argument arguments: number [precision, default is 250] [base, default is e]
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.WeakHashMap;
/**
* Stores 2 {@link BigInteger}s, one numerator and one denominator, these two values then define a fraction.<br>
* Creating a BigFraction which has another instance with the same value somewhere else, a new one will not be created but the other value is returned.<br>
* Because of this, == can be used for comparing the fractions and yield correct results.
* @author Kristoffer
* @version 1.0
* @since 1.0
*/
public final class BigFraction implements Comparable<BigFraction> {
public final BigInteger numerator;
public final BigInteger denominator;
//weak so it wont cause memory problems
private static final WeakHashMap<BigInteger, WeakHashMap<BigInteger, BigFraction>> fractions = new WeakHashMap<>();
public static final BigFraction ONE = valueOf(1);
public static final BigFraction ZERO = valueOf(0);
public static final BigFraction TWO = valueOf(2);
public static final BigFraction PI = valueOf(884279719003555l, 281474976710656l);
public static final BigFraction E = valueOf(6121026514868073l, 2251799813685248l);
public static BigFraction valueOf(BigInteger n, BigInteger d) {
if (d.equals(BigInteger.ZERO))
throw new ArithmeticException("Divide by 0");
if (d.compareTo(BigInteger.ZERO) < 0) {
n = n.negate();
d = d.negate();
}
BigInteger gcd = gcd(n, d);
n = n.divide(gcd);
d = d.divide(gcd);
synchronized (fractions) {
if (fractions.containsKey(n)) {
WeakHashMap<BigInteger, BigFraction> f = fractions.get(n);
if (f!=null)//It might become null between check and get
if (f.containsKey(d)) {
BigFraction x = f.get(d);
if (x!=null) {//It might become null between check and get
return x;
}
} else {
BigFraction x = new BigFraction(n, d);
f.put(d, x);
return x;
}
}
BigFraction x = new BigFraction(n, d);
WeakHashMap<BigInteger, BigFraction> f = new WeakHashMap<BigInteger, BigFraction>();
f.put(d, x);
fractions.put(n, f);
return x;
}
}
public static BigFraction valueOf(BigInteger n) {
return valueOf(n, BigInteger.ONE);
}
public static BigFraction valueOf(long n, long d) {
return valueOf(BigInteger.valueOf(n), BigInteger.valueOf(d));
}
public static BigFraction valueOf(long n) {
return valueOf(BigInteger.valueOf(n), BigInteger.ONE);
}
public static BigFraction valueOf(BigInteger n, long d) {
return valueOf(n, BigInteger.valueOf(d));
}
public static BigFraction valueOf(long n, BigInteger d) {
return valueOf(BigInteger.valueOf(n), d);
}
public static BigFraction valueOf(BigDecimal n) {
return valueOf(n.toString());
}
public static BigFraction valueOf(double n) {
return valueOf(new BigDecimal(n));
}
/**
* Can parse whole numbers, decimal numbers, fractions of any types accepted by this parser.<br>
* This means that fractions can be nested.<br>
* @param str The string to parse.
* @return The fraction parsed from the string.
* @throws NumberFormatException If the string was formatted wrongly.
*/
public static BigFraction valueOf(String str) throws NumberFormatException {
str = str.trim();
if (str.length() == 0)
throw new NumberFormatException("Zero Length BigFraction");
int dotlocation = str.indexOf('.');
int slashlocation = str.indexOf('/');
if (slashlocation > -1) {//Fraction.
BigFraction f1 = valueOf(str.substring(0, slashlocation));
BigFraction f2 = valueOf(str.substring(slashlocation+1));
return f1.divide(f2);
}
if (dotlocation == -1)//Whole number
return valueOf(new BigInteger(str));
//Decimal
BigInteger num = new BigInteger(str.replaceFirst("\\.", ""));
int p = str.length()-dotlocation-1;
return valueOf(num, BigInteger.TEN.pow(p));
}
private BigFraction(BigInteger n, BigInteger d) {//The fraction is shortened in valueOf, so there is no need to do it in the constructor
if (d.equals(BigInteger.ZERO))
throw new ArithmeticException("Division with 0");
numerator = n;
denominator = d;
}
//Iterative to prevent overflow stuff
private static BigInteger gcd(BigInteger a, BigInteger b) {
while (!a.equals(BigInteger.ZERO)) {
BigInteger c = a;
a = mod(b, a);
b = c;
}
return b;
}
private static BigInteger mod(BigInteger a, BigInteger b) {
if (b.compareTo(BigInteger.ZERO) < 0)
return a.mod(b.negate()).negate();
return a.mod(b);
}
public BigFraction add(BigFraction n) {
BigInteger deno = denominator.multiply(n.denominator);
BigInteger thisnum = numerator.multiply(n.denominator);
BigInteger nnum = n.numerator.multiply(denominator);
return valueOf(thisnum.add(nnum), deno);
}
public BigFraction add(long n) {
return valueOf(numerator.add(BigInteger.valueOf(n).multiply(denominator)), denominator);
}
public BigFraction subtract(BigFraction n) {
return valueOf(numerator.multiply(n.denominator).subtract(n.numerator.multiply(denominator)), denominator.multiply(n.denominator));
}
public BigFraction subtract(long n) {
return valueOf(numerator.subtract(BigInteger.valueOf(n).multiply(denominator)), denominator);
}
public BigFraction multiply(BigFraction n) {
return valueOf(n.numerator.multiply(numerator), denominator.multiply(n.denominator));
}
public BigFraction multiply(long n) {
return valueOf(numerator.multiply(BigInteger.valueOf(n)), denominator);
}
public BigFraction divide(BigFraction n) {
return multiply(n.reciprocal());
}
public BigFraction divide(long n) {
return multiply(valueOf(BigInteger.ONE, BigInteger.valueOf(n)));
}
public BigFraction reciprocal() {
return valueOf(denominator, numerator);
}
public boolean largerThan(BigFraction other) {
return (compareTo(other) > 0);
}
public boolean smallerThan(BigFraction other) {
return (compareTo(other) < 0);
}
public BigFraction floor() {
return valueOf(numerator.divide(denominator));
}
public BigFraction ceil() {
return valueOf(numerator.add(denominator).subtract(BigInteger.ONE).divide(denominator));
}
public boolean isWhole() {
return denominator.compareTo(BigInteger.ONE) == 0 || denominator.compareTo(BigInteger.valueOf(-1)) == 0;
}
/**
* @return 1 if this > 0<br>0 if this == 0<br>-1 if this < 0
*/
public byte sgn() {
int compare = compareTo(ZERO);
if (compare > 0)
return 1;
if (compare < 0)
return -1;
return 0;
}
public BigFraction decimalPart() {
return valueOf(numerator.mod(denominator), denominator);
}
private final WeakHashMap<BigFraction, Touple2<BigFraction, Integer>> powers = new WeakHashMap<>();
public BigFraction pow(long power) {
if (power < 0)
return pow(-power).reciprocal();
if (power == 0) {
if (this == ZERO)
throw new ArithmeticException("0 pow 0 is undefined");
return ONE;
}
if (power > 15) {
Touple2<BigFraction, Integer> pow = powers.get(valueOf(power));
if (pow != null) {
return pow.a;
}
}
BigFraction r = this;
long i = 1;
for (; i*2 < power; i *= 2) {
r = r.multiply(r);
}
for (; i < power; i++) {
r = r.multiply(this);
}
Touple2<BigFraction, Integer> pow = new Touple2<>(r, -1);
powers.put(valueOf(power), pow);
return r;
}
public BigFraction pow(BigInteger n) {
{
int compare = n.compareTo(BigInteger.ZERO);
if (compare < 0)
return pow(n.abs()).reciprocal();
if (compare == 0) {
if (this == ZERO)
throw new ArithmeticException("0 pow 0 is undefined");
return ONE;
}
}
if (n.compareTo(BigInteger.valueOf(15)) > 0) {
Touple2<BigFraction, Integer> pow = powers.get(valueOf(n));
if (pow != null) {
return pow.a;
}
}
BigFraction result = this;
BigInteger i = BigInteger.ONE;
for (; i.compareTo(n) < 0; i = i.add(BigInteger.ONE)) {
result = result.multiply(result);
}
for (; i.compareTo(n) < 0; i = i.add(BigInteger.ONE)) {
result = result.multiply(this);
}
Touple2<BigFraction, Integer> pow = new Touple2<>(result, -1);
powers.put(valueOf(n), pow);
return result;
}
public BigFraction pow(BigFraction n) {
return pow(n, 11);
}
public BigFraction pow(BigFraction n, int digits) {
{
int compare = n.compareTo(ZERO);
if (compare < 0)
return pow(n.abs()).reciprocal();
if (compare == 0) {
if (this == ZERO)
throw new ArithmeticException("0 pow 0 is undefined");
return ONE;
}
}
BigFraction guess = null;
if (n.compareTo(BigFraction.valueOf(15)) > 0) {
Touple2<BigFraction, Integer> pow = powers.get(n);
if (pow != null) {
if (digits >= pow.b || pow.b == -1) {
return pow.a;
}
guess = pow.a;
}
}
if (n.isWhole())
return pow(n.numerator);
if (guess == null)
guess = multiply(n);
BigFraction thjs = this.pow(n.numerator);
do {
guess = guess.subtract(guess.divide(thjs).subtract(guess.pow(n.denominator.subtract(BigInteger.ONE)).reciprocal()));
if (guess.sizeInBytes() > 64) {
guess = valueOf(guess.toDecimalString(digits+10));
}
} while (!(guess.pow(n.denominator).toDecimalString(digits).equals(thjs.toDecimalString(digits))));
BigFraction ret; {
if (guess.pow(n.denominator).equals(thjs))
ret = guess;
else
ret = valueOf(guess.toDecimalString(digits));
}
if (ret.pow(n.denominator).equals(thjs))
digits = -1;
Touple2<BigFraction, Integer> touple = new Touple2<>(ret, digits);
powers.put(n, touple);
return ret;
}
public BigFraction ln() {
return ln(230);
}
public static final BigFraction log2 = valueOf("0.6931471805599453094172321214581765680755001343602552541206800094933936219696947156058633269964186875420014810205706857336855202357581305570326707516350759619307275708283714351903070386238916734711233501153644979552391204751726815749320651555247341395258829504530070953263666426541042391578149520437404303855008019441706416715186447128399681717845469570262716310645461502572074024816377733896385506952606683411372738737229289564935470257626520988596932019650585547647033067936544325476327449512504060694381471046899465062201677204245245296126879465461931651746813926725041038025462596568691441928716082938031727143677826548775664850856740776484514644399404614226031930967354025744460703080960850474866385231381816767514386674766478908814371419854942315199735488037516586127535291661000710535582498794147295092931138971559982056543928717000721808576102523688921324497138932037843935308877482597017155910708823683627589842589185353024363421436706118923678919237231467232172053401649256872747782344535347648114941864238677677440606956265737960086707625719918473402265146283790488306203306114463007371948900274364396500258093651944304119115060809487930678651588709006052034684297361938412896525565396860221941229242075743217574890977067526871158170511370091589426654785959648906530584602586683829400228330053820740056770530467870018416240441883323279838634900156312188956065055315127219939833203075140842609147900126516824344389357247278820548627155274187724300248979454019618723398086083166481149093066751933931289043164137068139777649817697486890388778999129650361927071088926410523092478391737350122984242049956893599220660220465494151061391878857442455775102068370308666194808964121868077902081815885800016881159730561866761991873952007667192145922367206025395954365416553112951759899400560003665135675690512459268257439464831683326249018038242408242314523061409638057007025513877026817851630690255137032340538021450190153740295099422629957796474271381573638017298739407042421799722669629799393127069357472404933865308797587216996451294464918837711567016785988049818388967841349383140140731664727653276359192335112333893387095132090592721854713289754707978913844454666761927028855334234298993218037691549733402675467588732367783429161918104301160916952655478597328917635455567428638774639871019124317542558883012067792102803412068797591430812833072303008834947057924965910058600123415617574132724659430684354652111350215443415399553818565227502214245664400062761833032064727257219751529082785684213207959886389672771195522188190466039570097747065126195052789322960889314056254334425523920620303439417773579455921259019925591148440242390125542590031295370519220615064345837878730020354144217857580132364516607099143831450049858966885772221486528821694181270488607589722032166631283783291567630749872985746389282693735098407780493950049339987626475507031622161390348452994249172483734061366226383493681116841670569252147513839306384553718626877973288955588716344297562447553923663694888778238901749810273565524050518547730619440524232212559024833082778888890596291197299545744156245124859268311260746797281638090250005655999146128332543581114048482060640824224792403855764762350311003242597091425011146155848306700125831821915347207474111940098355732728261442738213970704779562596705790230338480617134555536855375810657497344479225111965461618278960100685129653954796586637835224736245460935850360506784143911445231457780335917921127955705055554514387888188153519485934467246429498640506265184244753956637833734822075332944813064933603546101017746493267877167198612073968320123596077290246830459403130563776313240108042028543590269450940307400149339507673160285028697303187182399843352574354995608502566089783395564211494807339362607510238183314110047089039501343302974134748405406158775396888381540769801776730369991074924697847843128430364112892028012272563468391623354787727340063958657179819069358127");
/**
* Calculates the natural logarithm of this<br>
* Higher values of intervals gives more precise results, and longer computation times.
*/
public BigFraction ln(int intervals) {
if (this == TWO)
return log2;
long div2Times = 0;
BigFraction thjs = this;
while (thjs.compareTo(TWO) > 0) {
div2Times++;
thjs = thjs.divide(2);
}
BigFraction h = thjs.subtract(1).divide(intervals).divide(2);
BigFraction totalArea = ZERO;
for (int i = 0; i < intervals; ++i) {
BigFraction x1 = ONE.add(TWO.multiply(i).multiply(h));
BigFraction x2 = x1.add(h);
BigFraction x3 = x2.add(h);
BigFraction y1 = x1.reciprocal();
BigFraction y2 = x2.reciprocal();
BigFraction y3 = x3.reciprocal();
totalArea = totalArea.add(h.multiply(y1.add(y2.multiply(4)).add(y3)).divide(3));
}
BigFraction log2times = log2.multiply(div2Times);
return log2times.add(totalArea);
}
public BigFraction log(BigFraction base) {
if (base == null)
return ln();
BigFraction t = base;
for (int i = 1; i < 10; i++) {
if (t == this)
return valueOf(i);
t = t.multiply(base);
}
return ln().divide(base.ln());
}
public BigFraction log(BigFraction base, int intervals) {
if (base == null)
return ln(intervals);
BigFraction t = base;
for (int i = 1; i < 10; i++) {
if (t == this)
return valueOf(i);
t = t.multiply(base);
}
return ln(intervals).divide(base.ln(intervals));
}
public BigFraction log(long base) {
return ln().divide(valueOf(base).ln());
}
public BigFraction log(long base, int intervals) {
return ln(intervals).divide(valueOf(base).ln(intervals));
}
public BigFraction abs() {
if (compareTo(ZERO) < 0)
return negate();
return this;
}
public BigFraction negate() {
return valueOf(numerator.negate(), denominator);
}
public BigFraction square() {
return valueOf(numerator.multiply(numerator), denominator.multiply(denominator));
}
/**
* Computes the square root of this to 25 digits.
* @return A fraction equal to the square root of this to 25 digits in base 10.
*/
public BigFraction sqrt() {
return pow(ONE.divide(TWO));
}
public int sizeInBytes() {
return numerator.toByteArray().length + denominator.toByteArray().length;
}
/**
* @param digits Rounds this fraction at <code>digits</code> digits.
* @return A cut bigfraction
*/
public BigFraction round(int digits) {
return valueOf(toDecimalString(digits));
}
public BigFraction round() {
return round(0);
}
@Override
public String toString() {
if (denominator.equals(BigInteger.ONE) || denominator.equals(BigInteger.valueOf(-1)))
return numerator.divide(denominator).toString();
return numerator.toString() + "/" + denominator.toString();
}
public String toDecimalString() {
if (denominator.equals(BigInteger.ONE) || denominator.equals(BigInteger.valueOf(-1)))
return numerator.divide(denominator).toString();
BigDecimal n = new BigDecimal(numerator);
BigDecimal d = new BigDecimal(denominator);
n = n.setScale(1000);
String s = n.divide(d, BigDecimal.ROUND_HALF_UP).toString();
s = s.replaceAll("[\\.]?0+$", "");
return s;
}
/**
* @param maxDigits Maximum amount of digits after the .
* @return A decimal string representing this value.
*/
public String toDecimalString(int maxDigits) {
if (denominator.equals(BigInteger.ONE))
return numerator.toString();
BigDecimal n = new BigDecimal(numerator);
BigDecimal d = new BigDecimal(denominator);
n = n.setScale(maxDigits);
String s = n.divide(d, BigDecimal.ROUND_HALF_UP).toString();
s = s.replaceAll("[\\.]?0+$", "");
return s;
}
@Override
public int hashCode() {
long hash = numerator.hashCode();
hash += (1<<31)-1;
hash *= denominator.hashCode();
return (int) hash;
}
//Works because there can't exists different objects with the same value.
@Override
public boolean equals(Object obj) {
return obj == this;
}
public double doubleValue() {
return Double.parseDouble(toDecimalString());
}
public float floatValue() {
return Float.parseFloat(toDecimalString());
}
public int intValue() {
return numerator.divide(denominator).intValue();
}
public long longValue() {
return numerator.divide(denominator).longValue();
}
@Override
public int compareTo(BigFraction other) {
BigInteger othernum = other.numerator.multiply(denominator);
BigInteger thisnum = numerator.multiply(other.denominator);
return thisnum.compareTo(othernum);
}
/**
* Writes the fraction to the output stream, does not flush or close the stream.<br>
* This is equivalent to {@link #write(BigFraction, java.io.OutputStream) write(BigFraction, OutputStream)}<br>
* The size of the written data is equal to {@link #sizeInBytes()}+8
* @param out The stream to write to
* @throws IOException If an io exception occurs.
*/
public void write(java.io.OutputStream out) throws IOException {
byte[] n = numerator.toByteArray();
byte[] d = denominator.toByteArray();
int nl = n.length-1;
int dl = d.length-1;
byte[] bytes = new byte[nl+dl+8];
bytes[0] = (byte)(nl >>> 24);
bytes[1] = (byte)(nl >>> 16);
bytes[2] = (byte)(nl >>> 8);
bytes[3] = (byte)nl;
bytes[4] = (byte)(dl >>> 24);
bytes[5] = (byte)(dl >>> 16);
bytes[6] = (byte)(dl >>> 8);
bytes[7] = (byte)dl;
System.arraycopy(bytes, 8, n, 0, n.length);
System.arraycopy(bytes, 8 + n.length, d, 0, d.length);
out.write(bytes);
}
/**
* Writes the fraction to the output stream, does not flush or close the stream.<br>
* This is equivalent to n.{@link #write(java.io.OutputStream) write(OutputStream)}
* @param n The fraction to write.
* @param out The stream to write the fraction to.
* @throws IOException If an io exception occurs.
*/
public static void write(BigFraction n, java.io.OutputStream out) throws IOException {
n.write(out);
}
/**
* Reads a bigfraction from the input stream.
* @param in The input stream to read from.
* @return The read big fraction
* @throws IOException If an io exception occurs.
*/
public static BigFraction read(java.io.InputStream in) throws IOException {
byte[] l1a = new byte[4];
byte[] l2a = new byte[4];
in.read(l1a);
in.read(l2a);
int l1 = l1a[0] << 24 | (l1a[1] & 0xFF) << 16 | (l1a[2] & 0xFF) << 8 | (l1a[3] & 0xFF);
int l2 = l2a[0] << 24 | (l2a[1] & 0xFF) << 16 | (l2a[2] & 0xFF) << 8 | (l2a[3] & 0xFF);
byte[] num = new byte[l1+1];
byte[] den = new byte[l2+1];
in.read(num);
in.read(den);
return valueOf(new BigInteger(num), new BigInteger(den));
}
}
public class Main {
public static void main(String[] args) throws IOException {
/*Constant three = new Constant(BigFraction.valueOf(3));
Equation r1 = Power.power(new Variable('x'), new Constant(BigFraction.valueOf(6)));
Equation r2 = Reciprocal.divide(r1, three);
System.out.println(r2);
System.out.println(r2.derivative());*/
if (args.length < 1) {
System.out.println("arguments: number [precision, default is 250] [base, default is e]");
return;
}
int precision = 250;
if (args.length >= 2) {
try {
precision = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
System.out.println(args[1] + " is not a number");
return;
}
}
BigFraction base = null;
if (args.length >= 3) {
try {
base = BigFraction.valueOf(args[2]);
} catch (NumberFormatException nfe) {
System.out.println(args[2] + " is not a number");
return;
}
}
try {
BigFraction bf = BigFraction.valueOf(args[0]).log(base, precision);
System.out.println(bf.toDecimalString());
} catch (NumberFormatException nfe) {
System.out.println(args[0] + " is not a number");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment