/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. */ package org.geogebra.common.kernel.algos; import org.geogebra.common.kernel.Construction; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.arithmetic.MyDouble; import org.geogebra.common.kernel.commands.Commands; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoNumberValue; import org.geogebra.common.kernel.geos.GeoText; /** * Algo for converting decimals to fractions * */ public class AlgoFractionText extends AlgoElement { private GeoNumberValue num; // input private GeoText text; // output private double frac[] = { 0, 0 }; private StringBuilder sb = new StringBuilder(); /** * @param cons * construction * @param num * input number */ public AlgoFractionText(Construction cons, GeoNumberValue num) { super(cons); this.num = num; text = new GeoText(cons); text.setIsTextCommand(true); // stop editing as text text.setLaTeX(true, false); setInputOutput(); compute(); } @Override public Commands getClassName() { return Commands.FractionText; } @Override protected void setInputOutput() { input = new GeoElement[1]; input[0] = num.toGeoElement(); setOutputLength(1); setOutput(0, text); setDependencies(); // done by AlgoElement } /** * @return resulting text */ public GeoText getResult() { return text; } @Override public void compute() { // StringTemplate tpl = // StringTemplate.get(app.getFormulaRenderingType()); StringTemplate tpl = text.getStringTemplate(); if (input[0].isDefined()) { frac = decimalToFraction(num.getDouble(), Kernel.STANDARD_PRECISION); sb.setLength(0); appendFormula(sb, frac, tpl, kernel); text.setTextString(sb.toString()); /* * break; case LATEX: sb.setLength(0); appendLaTeX(sb, frac, tpl, * kernel); text.setTextString(sb.toString()); break; * * } */ text.setLaTeX(true, false); } else { text.setTextString("?"); } } // https://web.archive.org/web/20111027100847/http://homepage.smc.edu/kennedy_john/DEC2FRAC.PDF /** * Algorithm To Convert A Decimal To A Fraction by John Kennedy Mathematics * Department Santa Monica College 1900 Pico Blvd. Santa Monica, CA 90405 * http://homepage.smc.edu/kennedy_john/DEC2FRAC.PDF * * @param decimal * to be converted to fraction * @param AccuracyFactor * accuracy * @return [numerator, denominator] */ public static double[] decimalToFraction(double decimal, double AccuracyFactor) { double fractionNumerator, fractionDenominator; double decimalSign; double Z; double PreviousDenominator; double ScratchValue; double ret[] = { 0, 0 }; if (Double.isNaN(decimal)) { return ret; // return 0/0 } if (decimal == Double.POSITIVE_INFINITY) { ret[0] = 1; ret[1] = 0; // 1/0 return ret; } if (decimal == Double.NEGATIVE_INFINITY) { ret[0] = -1; ret[1] = 0; // -1/0 return ret; } if (decimal < 0.0) { decimalSign = -1.0; } else { decimalSign = 1.0; } double decimalAbs = Math.abs(decimal); if (Math.abs(decimalAbs - Math.floor(decimalAbs)) < AccuracyFactor) { // handles // exact // integers // including // 0 fractionNumerator = decimalAbs * decimalSign; fractionDenominator = 1.0; ret[0] = fractionNumerator; ret[1] = fractionDenominator; return ret; } if (decimalAbs < 1.0E-19) { // X = 0 already taken care of fractionNumerator = decimalSign; fractionDenominator = 9999999999999999999.0; ret[0] = fractionNumerator; ret[1] = fractionDenominator; return ret; } if (decimalAbs > 1.0E19) { fractionNumerator = 9999999999999999999.0 * decimalSign; fractionDenominator = 1.0; ret[0] = fractionNumerator; ret[1] = fractionDenominator; return ret; } Z = decimalAbs; PreviousDenominator = 0.0; fractionDenominator = 1.0; do { Z = 1.0 / (Z - Math.floor(Z)); ScratchValue = fractionDenominator; fractionDenominator = fractionDenominator * Math.floor(Z) + PreviousDenominator; PreviousDenominator = ScratchValue; fractionNumerator = Math.floor(decimalAbs * fractionDenominator + 0.5); // Rounding // Function } while (Math .abs((decimalAbs - (fractionNumerator / fractionDenominator))) > AccuracyFactor && !MyDouble.exactEqual(Z, Math.floor(Z))); fractionNumerator = decimalSign * fractionNumerator; ret[0] = fractionNumerator; ret[1] = fractionDenominator; return ret; } @Override public boolean isLaTeXTextCommand() { return true; } /** * @param sb * builder * @param tpl * template * @param left * numerator * @param right * denominator */ public static void appendFraction(StringBuilder sb, StringTemplate tpl, String left, String right) { sb.append(" \\frac{ "); sb.append(left); sb.append(" }{ "); sb.append(right); sb.append(" } "); } /** * Appends plus or minus infinity to sb * * @param sb * builder * @param tpl * template * @param numer * numerator (to decide +-) */ public static void appendInfinity(StringBuilder sb, StringTemplate tpl, double numer) { if (numer > 0) { sb.append(" \\infty "); } else { sb.append(" - \\infty "); } } /** * @param sb * builder * @param frac * [numerator, denominator] * @param tpl * output template * @param kernel * kernel */ public static void appendFormula(StringBuilder sb, double[] frac, StringTemplate tpl, Kernel kernel) { if (frac[1] == 1) { // integer sb.append(kernel.format(frac[0], tpl)); } else if (frac[1] == 0) { // 1 / 0 or -1 / 0 appendInfinity(sb, tpl, frac[0]); } else { appendFraction(sb, tpl, kernel.format(Kernel.checkDecimalFraction(frac[0]), tpl), kernel.format(Kernel.checkDecimalFraction(frac[1]), tpl)); } } }