From de71f691f260381edf3aa05203e553bbc399a487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E4=91=93=E5=A6=82=E6=B3=95?= Date: Wed, 13 Nov 2024 22:21:57 +0900 Subject: [PATCH] Change Fraction to RationalNumber --- src/components/continued-fraction.test.js | 4 +- src/components/fraction.js | 348 ---------------------- src/components/time.js | 25 -- 3 files changed, 2 insertions(+), 375 deletions(-) delete mode 100644 src/components/fraction.js diff --git a/src/components/continued-fraction.test.js b/src/components/continued-fraction.test.js index b162bd60..becabd9b 100644 --- a/src/components/continued-fraction.test.js +++ b/src/components/continued-fraction.test.js @@ -1,8 +1,8 @@ -import { Fraction } from './fraction'; +import RationalNumber from './rational-number'; import { continuedFraction } from './continued-fraction'; test('0.5 is 1/2', () => { - const a = new Fraction(1, 2); + const a = new RationalNumber(1, 2); const b = continuedFraction(0.5, 5); expect(b.toString()).toBe(a.toString()) }) diff --git a/src/components/fraction.js b/src/components/fraction.js deleted file mode 100644 index 78311cc1..00000000 --- a/src/components/fraction.js +++ /dev/null @@ -1,348 +0,0 @@ -/* -fraction.js -A Javascript fraction library. - -Copyright (c) 2009 Erik Garrison - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -*/ - - -/* Fractions */ -/* - * - * Fraction objects are comprised of a numerator and a denomenator. These - * values can be accessed at fraction.numerator and fraction.denomenator. - * - * Fractions are always returned and stored in lowest-form normalized format. - * This is accomplished via Fraction.normalize. - * - * The following mathematical operations on fractions are supported: - * - * Fraction.equals - * Fraction.add - * Fraction.subtract - * Fraction.multiply - * Fraction.divide - * - * These operations accept both numbers and fraction objects. (Best results - * are guaranteed when the input is a fraction object.) They all return a new - * Fraction object. - * - * Usage: - * - * TODO - * - */ - -/* - * The Fraction constructor takes one of: - * an explicit numerator (integer) and denominator (integer), - * a string representation of the fraction (string), - * or a floating-point number (float) - * - * These initialization methods are provided for convenience. Because of - * rounding issues the best results will be given when the fraction is - * constructed from an explicit integer numerator and denomenator, and not a - * decimal number. - * - * - * e.g. new Fraction(1, 2) --> 1/2 - * new Fraction('1/2') --> 1/2 - * new Fraction('2 3/4') --> 11/4 (prints as 2 3/4) - * - */ -var Fraction = function (numerator, denominator) { - /* double argument invocation */ - if (typeof numerator !== 'undefined' && denominator) { - if (typeof (numerator) === 'number' && typeof (denominator) === 'number') { - this.numerator = numerator; - this.denominator = denominator; - } else if (typeof (numerator) === 'string' && typeof (denominator) === 'string') { - // what are they? - // hmm.... - // assume they are ints? - this.numerator = parseInt(numerator); - this.denominator = parseInt(denominator); - } - /* single-argument invocation */ - } else if (typeof denominator === 'undefined') { - num = numerator; // swap variable names for legibility - if (typeof (num) === 'number') { // just a straight number init - this.numerator = num; - this.denominator = 1; - } else if (typeof (num) === 'string') { - var a, b; // hold the first and second part of the fraction, e.g. a = '1' and b = '2/3' in 1 2/3 - // or a = '2/3' and b = undefined if we are just passed a single-part number - var arr = num.split(' ') - if (arr[0]) a = arr[0] - if (arr[1]) b = arr[1] - /* compound fraction e.g. 'A B/C' */ - // if a is an integer ... - if (a % 1 === 0 && b && b.match('/')) { - return (new Fraction(a)).add(new Fraction(b)); - } else if (a && !b) { - /* simple fraction e.g. 'A/B' */ - if (typeof (a) === 'string' && a.match('/')) { - // it's not a whole number... it's actually a fraction without a whole part written - var f = a.split('/'); - this.numerator = f[0]; this.denominator = f[1]; - /* string floating point */ - } else if (typeof (a) === 'string' && a.match('\.')) { - return new Fraction(parseFloat(a)); - /* whole number e.g. 'A' */ - } else { // just passed a whole number as a string - this.numerator = parseInt(a); - this.denominator = 1; - } - } else { - return undefined; // could not parse - } - } - } - this.normalize(); -} - - -Fraction.prototype.clone = function () { - return new Fraction(this.numerator, this.denominator); -} - - -/* pretty-printer, converts fractions into whole numbers and fractions */ -Fraction.prototype.toString = function () { - if (this.denominator === 'NaN') return 'NaN' - var wholepart = (this.numerator / this.denominator > 0) ? - Math.floor(this.numerator / this.denominator) : - Math.ceil(this.numerator / this.denominator) - var numerator = this.numerator % this.denominator - var denominator = this.denominator; - var result = []; - if (wholepart != 0) - result.push(wholepart); - if (numerator != 0) - result.push(((wholepart === 0) ? numerator : Math.abs(numerator)) + '/' + denominator); - return result.length > 0 ? result.join(' ') : 0; -} - - -/* destructively rescale the fraction by some integral factor */ -Fraction.prototype.rescale = function (factor) { - this.numerator *= factor; - this.denominator *= factor; - return this; -} - - -Fraction.prototype.add = function (b) { - var a = this.clone(); - if (b instanceof Fraction) { - b = b.clone(); - } else { - b = new Fraction(b); - } - td = a.denominator; - a.rescale(b.denominator); - b.rescale(td); - - a.numerator += b.numerator; - - return a.normalize(); -} - - -Fraction.prototype.subtract = function (b) { - var a = this.clone(); - if (b instanceof Fraction) { - b = b.clone(); // we scale our argument destructively, so clone - } else { - b = new Fraction(b); - } - td = a.denominator; - a.rescale(b.denominator); - b.rescale(td); - - a.numerator -= b.numerator; - - return a.normalize(); -} - - -Fraction.prototype.multiply = function (b) { - var a = this.clone(); - if (b instanceof Fraction) { - a.numerator *= b.numerator; - a.denominator *= b.denominator; - } else if (typeof b === 'number') { - a.numerator *= b; - } else { - return a.multiply(new Fraction(b)); - } - return a.normalize(); -} - -Fraction.prototype.divide = function (b) { - var a = this.clone(); - if (b instanceof Fraction) { - a.numerator *= b.denominator; - a.denominator *= b.numerator; - } else if (typeof b === 'number') { - a.denominator *= b; - } else { - return a.divide(new Fraction(b)); - } - return a.normalize(); -} - -Fraction.prototype.equals = function (b) { - if (!(b instanceof Fraction)) { - b = new Fraction(b); - } - // fractions that are equal should have equal normalized forms - var a = this.clone().normalize(); - var b = b.clone().normalize(); - return (a.numerator === b.numerator && a.denominator === b.denominator); -} - - -/* Utility functions */ - -/* Destructively normalize the fraction to its smallest representation. - * e.g. 4/16 -> 1/4, 14/28 -> 1/2, etc. - * This is called after all math ops. - */ -Fraction.prototype.normalize = (function () { - - var isFloat = function (n) { - return (typeof (n) === 'number' && - ((n > 0 && n % 1 > 0 && n % 1 < 1) || - (n < 0 && n % -1 < 0 && n % -1 > -1)) - ); - } - - var roundToPlaces = function (n, places) { - if (!places) { - return Math.round(n); - } else { - var scalar = Math.pow(10, places); - return Math.round(n * scalar) / scalar; - } - } - - return (function () { - - // XXX hackish. Is there a better way to address this issue? - // - /* first check if we have decimals, and if we do eliminate them - * multiply by the 10 ^ number of decimal places in the number - * round the number to nine decimal places - * to avoid js floating point funnies - */ - if (isFloat(this.denominator)) { - var rounded = roundToPlaces(this.denominator, 9); - var scaleup = Math.pow(10, rounded.toString().split('.')[1].length); - this.denominator = Math.round(this.denominator * scaleup); // this !!! should be a whole number - //this.numerator *= scaleup; - this.numerator *= scaleup; - } - if (isFloat(this.numerator)) { - var rounded = roundToPlaces(this.numerator, 9); - var scaleup = Math.pow(10, rounded.toString().split('.')[1].length); - this.numerator = Math.round(this.numerator * scaleup); // this !!! should be a whole number - //this.numerator *= scaleup; - this.denominator *= scaleup; - } - var gcf = Fraction.gcf(this.numerator, this.denominator); - this.numerator /= gcf; - this.denominator /= gcf; - if ((this.numerator < 0 && this.denominator < 0) || (this.numerator > 0 && this.denominator < 0)) { - this.numerator *= -1; - this.denominator *= -1; - } - return this; - }); - -})(); - - -/* Takes two numbers and returns their greatest common factor. - */ -Fraction.gcf = function (a, b) { - - var common_factors = []; - var fa = Fraction.primeFactors(a); - var fb = Fraction.primeFactors(b); - // for each factor in fa - // if it's also in fb - // put it into the common factors - fa.forEach(function (factor) { - var i = fb.indexOf(factor); - if (i >= 0) { - common_factors.push(factor); - fb.splice(i, 1); // remove from fb - } - }); - - if (common_factors.length === 0) - return 1; - - var gcf = (function () { - var r = common_factors[0]; - var i; - for (i = 1; i < common_factors.length; i++) { - r = r * common_factors[i]; - } - return r; - })(); - - return gcf; - -}; - - -// Adapted from: -// http://www.btinternet.com/~se16/js/factor.htm -Fraction.primeFactors = function (n) { - - var num = Math.abs(n); - var factors = []; - var _factor = 2; // first potential prime factor - - while (_factor * _factor <= num) // should we keep looking for factors? - { - if (num % _factor === 0) // this is a factor - { - factors.push(_factor); // so keep it - num = num / _factor; // and divide our search point by it - } - else { - _factor++; // and increment - } - } - - if (num != 1) // If there is anything left at the end... - { // ...this must be the last prime factor - factors.push(num); // so it too should be recorded - } - - return factors; // Return the prime factors -} - -export { Fraction } diff --git a/src/components/time.js b/src/components/time.js index 152560e2..a5560f83 100644 --- a/src/components/time.js +++ b/src/components/time.js @@ -1,24 +1,3 @@ -//import Fraction from './fraction' -import { Fraction } from './fraction' -import RationalNumber from './rational-number' - -function continuedFraction(x) { - var a = x; - var b = a % 1; - var a0 = Math.round(a - b); - if (b == 0) - return a0; - a = 1/b; - b = a % 1; - var a1 = Math.round(a - b); - if (b == 0) - return new Fraction(a0*a1+1, a1); - a = 1/b; - b = a % 1; - var a2 = Math.round(a - b); - return new Fraction(a0*(a1*a2+1)+a2, a1*a2+1); -} - const Time = class { constructor(date) { this.date = date; @@ -39,10 +18,6 @@ const Time = class { } else { this.humanString = "紀元前 " + Math.ceil(-d) + " 年" } - - this.fraction = continuedFraction(this.ratio); - this.numerator = this.fraction.numerator; - this.denominator = this.fraction.denominator; } static getDateString(dt) {