From 272502112e828e863a1db03000344bbc7308ef29 Mon Sep 17 00:00:00 2001 From: Vlad Pronsky Date: Thu, 1 Jun 2023 00:03:17 +0300 Subject: [PATCH] ability to parse from string --- .github/workflows/publish.yml | 3 +- .github/workflows/test.yml | 4 +-- package.json | 5 +-- readme.md | 27 +++++++++------- src/main.test.ts | 58 +++++++++++++++++++++++++++++++++++ src/main.ts | 32 +++++++++++++++++-- 6 files changed, 109 insertions(+), 20 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1f3cebc..daa9f23 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,8 +16,7 @@ jobs: registry-url: "https://registry.npmjs.org" - run: yarn install --frozen-lockfile - - run: yarn test - - run: yarn build + - run: yarn ci - run: npm publish env: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1bb58a6..194179f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,4 @@ jobs: cache: "yarn" - run: yarn install --frozen-lockfile - - run: yarn format-check - - run: yarn test-cov - - run: yarn build + - run: yarn ci diff --git a/package.json b/package.json index dc7ed7b..0276a57 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "type": "module", "name": "fractions-math", - "version": "1.0.0", + "version": "1.1.0", "author": "Vlad Pronsky ", "repository": "vladkens/fractions-math", "description": "Implementing fractions module from The Python Standard Library on TypeScript", @@ -26,7 +26,8 @@ "test-cov": "c8 --include=src yarn test", "test-watch": "watchexec -c -e ts 'clear && yarn test'", "format": "prettier --write 'src/**/*.ts'", - "format-check": "prettier --check 'src/**/*.ts'" + "format-check": "prettier --check 'src/**/*.ts'", + "ci": "yarn format-check && yarn test-cov && yarn build" }, "dependencies": {}, "devDependencies": { diff --git a/readme.md b/readme.md index c1bba3b..0d6bde2 100644 --- a/readme.md +++ b/readme.md @@ -40,12 +40,17 @@ const f5 = f4.div(f1).toString() // -> "6" (3 / 0.5) ## API -### `fraq(...args) / Fraction.make(...args)` +### `Fraction.make(...args: [Fraq] | [number, number])` Helper function to create fraction from various inputs. +Also alias available: `fraq` ```typescript -fraq(1, 2).toPair() // -> [1, 2] +type Fraq = Fraction | [number, number] | number | string + +Fraction.make(1, 2).toPair() // -> [1, 2] +fraq(1, 2).toPair() // ALIAS + fraq(2).toPair() // -> [2, 1] fraq([2, 1]).toPair() // -> [2, 1] fraq(0.5).toPair() // -> [1, 2] @@ -86,28 +91,28 @@ fraq(Math.PI).limit(1000).toString() // -> "355/113" fraq(1.1).limit().toString() // -> "11/10" ``` -### `.add(b: Fraction | number | [number, number])` +### `.add(b: Fraq)` ```typescript fraq(1, 2).add([-1, 2]).toString() // -> "0" fraq(1, 2).add(1).toString() // -> "3/2" ``` -### `.sub(b: Fraction | number | [number, number])` +### `.sub(b: Fraq)` ```typescript fraq(1, 2).sub([1, 2]).toString() // -> "0" fraq(1, 2).sub(1).toString() // -> "-1/2" ``` -### `.mul(b: Fraction | number | [number, number])` +### `.mul(b: Fraq)` ```typescript fraq(1, 2).mul([1, 2]).toString() // -> "1/4" fraq(1, 2).mul(1.5).toString() // -> "3/4" ``` -### `.div(b: Fraction | number | [number, number])` +### `.div(b: Fraq)` ```typescript fraq(1, 2).div([1, 2]).toString() // -> "1" @@ -115,17 +120,17 @@ fraq(1, 2).div(1.5).toString() // -> "1/3" fraq(1, 2).div(0).toString() // throws Error ``` -### `.eq(b: Fraction | number | [number, number])` +### `.eq(b: Fraq)` ```typescript fraq(1, 2).eq(0.5) // -> true fraq(1, 2).eq([1, 3]) // -> false ``` -### `.lt(b: Fraction | number | [number, number]) -> boolean` +### `.lt(b: Fraq) -> boolean` -### `.lte(b: Fraction | number | [number, number]) -> boolean` +### `.lte(b: Fraq) -> boolean` -### `.gt(b: Fraction | number | [number, number]) -> boolean` +### `.gt(b: Fraq) -> boolean` -### `.gte(b: Fraction | number | [number, number]) -> boolean` +### `.gte(b: Fraq) -> boolean` diff --git a/src/main.test.ts b/src/main.test.ts index c8dc443..28d267d 100644 --- a/src/main.test.ts +++ b/src/main.test.ts @@ -184,4 +184,62 @@ test("other", () => { equal(new Fraction(0, -1).toParts(), { s: 1, c: 0, n: 0, d: 1 }) }) +test("should parse from string", () => { + equal(fraq("0").toPair(), [0, 1]) + equal(fraq("1").toPair(), [1, 1]) + equal(fraq("2").toPair(), [2, 1]) + + equal(fraq("-0").toPair(), [0, 1]) + equal(fraq("-1").toPair(), [-1, 1]) + equal(fraq("-2").toPair(), [-2, 1]) + + equal(fraq("1/1").toPair(), [1, 1]) + equal(fraq("1/2").toPair(), [1, 2]) + equal(fraq("1/3").toPair(), [1, 3]) + equal(fraq("3/2").toPair(), [3, 2]) + equal(fraq("3/4").toPair(), [3, 4]) + equal(fraq("-1/2").toPair(), [-1, 2]) + equal(fraq("1/-2").toPair(), [-1, 2]) + throws(() => fraq("1/0").toPair(), /ZeroDivisionError/i) + + equal(fraq("1 1/2").toPair(), [3, 2]) + equal(fraq("1 1/3").toPair(), [4, 3]) + equal(fraq("2 1/2").toPair(), [5, 2]) + equal(fraq("2 1/3").toPair(), [7, 3]) + equal(fraq("-1 1/2").toPair(), [-3, 2]) + equal(fraq("-1 -1/2").toPair(), [3, 2]) + equal(fraq("-1 -1/-2").toPair(), [-3, 2]) + throws(() => fraq("1 1/0").toPair(), /ZeroDivisionError/i) + + equal(fraq("2 9/3").toPair(), [5, 1]) + equal(fraq("3 27/9").toPair(), [6, 1]) + + equal(fraq("0.5").toPair(), [1, 2]) + equal(fraq("1.5").toPair(), [3, 2]) + equal(fraq("-0.5").toPair(), [-1, 2]) + equal(fraq("-1.5").toPair(), [-3, 2]) + equal(fraq(".5").toPair(), [1, 2]) + equal(fraq("-.5").toPair(), [-1, 2]) + equal(fraq(".0").toPair(), [0, 1]) + equal(fraq("-.0").toPair(), [0, 1]) + + equal(fraq("1.33").toPair(), [133, 100]) + equal(fraq("1.333").toPair(), [1333, 1000]) + equal(fraq("1.3333").toPair(), [13333, 10000]) + + equal(fraq(" 1/2 ").toPair(), [1, 2]) + equal(fraq(" 1 1/2 ").toPair(), [3, 2]) + equal(fraq(" -1/2 ").toPair(), [-1, 2]) + equal(fraq(" -1 -1/-2 ").toPair(), [-3, 2]) + + equal(fraq(" 1 / 2 ").toPair(), [1, 2]) + equal(fraq(" 1 1 / 2 ").toPair(), [3, 2]) + equal(fraq(" -1 1 / -2 ").toPair(), [3, 2]) + + throws(() => fraq("1 abc").toPair(), /ValueError/i) + throws(() => fraq("1 1/a").toPair(), /ValueError/i) + throws(() => fraq("a/1").toPair(), /ValueError/i) + throws(() => fraq("1/a").toPair(), /ValueError/i) +}) + test.run() diff --git a/src/main.ts b/src/main.ts index f68454b..00b311e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,7 @@ * https://github.com/python/cpython/blob/3.11/Lib/fractions.py */ -export type Fraq = Fraction | [number, number] | number +export type Fraq = Fraction | [number, number] | number | string type Parts = { s: 1 | -1; c: Number; n: Number; d: Number } export const gcd = (a: number, b: number): number => { @@ -46,7 +46,7 @@ export class Fraction { this.d = b / g } - static make(...args: [Fraq] | [number, number]) { + static make(...args: [Fraq] | [number, number]): Fraction { if (args.length === 2) return new Fraction(...args) const val = args[0] @@ -61,6 +61,34 @@ export class Fraction { return new Fraction(n, d) } + if (typeof val === "string") { + const m1 = /^([-]?\d+)\s+([-]?\d+)\s*\/\s*([-]?\d+)$/.exec(val.trim()) + if (m1) { + const [c, n, d] = m1.slice(1).map((x) => parseInt(x, 10)) + const s = Math.sign(c) * Math.sign(n) * Math.sign(d) + + const b = Math.abs(d) + const a = (Math.abs(c) * b + Math.abs(n)) * s + return new Fraction(a, b) + } + + const m2 = /^([-]?\d+)\s*\/\s*([-]?\d+)$/.exec(val.trim()) + if (m2) { + const [n, d] = m2.slice(1).map((x) => parseInt(x, 10)) + return new Fraction(n, d) + } + + if (/^([-]?\d+)$/.test(val)) { + return new Fraction(parseInt(val, 10), 1) + } + + if (/^([-]?\d*)\.(\d+)$/.test(val)) { + return Fraction.make(parseFloat(val)) + } + + throw new Error("ValueError") + } + return new Fraction(...val) }