Created
December 13, 2013 12:41
-
-
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]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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