Skip to content

Commit

Permalink
ability to parse from string
Browse files Browse the repository at this point in the history
  • Loading branch information
vladkens committed May 31, 2023
1 parent d85d3a3 commit 2725021
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 20 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "fractions-math",
"version": "1.0.0",
"version": "1.1.0",
"author": "Vlad Pronsky <[email protected]>",
"repository": "vladkens/fractions-math",
"description": "Implementing fractions module from The Python Standard Library on TypeScript",
Expand All @@ -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": {
Expand Down
27 changes: 16 additions & 11 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -86,46 +91,46 @@ 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"
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`
58 changes: 58 additions & 0 deletions src/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
32 changes: 30 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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]
Expand All @@ -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)
}

Expand Down

0 comments on commit 2725021

Please sign in to comment.