Skip to content

Commit

Permalink
feat: upgrade to fraction.js@5 (#3283)
Browse files Browse the repository at this point in the history
  • Loading branch information
josdejong authored Nov 6, 2024
1 parent 0d2b8eb commit 9b81230
Show file tree
Hide file tree
Showing 24 changed files with 77 additions and 52 deletions.
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"complex.js": "^2.2.5",
"decimal.js": "^10.4.3",
"escape-latex": "^1.2.0",
"fraction.js": "^4.3.7",
"fraction.js": "^5.0.4",
"javascript-natural-sort": "^0.7.1",
"seedrandom": "^3.0.5",
"tiny-emitter": "^2.1.0",
Expand Down
2 changes: 1 addition & 1 deletion src/core/function/typed.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ export const createTyped = /* #__PURE__ */ factory('typed', dependencies, functi
throwNoFraction(x)
}

return new Fraction(x.toString())
return new Fraction(x)
}
}, {
from: 'Fraction',
Expand Down
8 changes: 5 additions & 3 deletions src/function/algebra/simplify.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const dependencies = [
'ObjectNode',
'OperatorNode',
'ParenthesisNode',
'SymbolNode'
'SymbolNode',
'replacer'
]

export const createSimplify = /* #__PURE__ */ factory(name, dependencies, (
Expand All @@ -40,7 +41,8 @@ export const createSimplify = /* #__PURE__ */ factory(name, dependencies, (
ObjectNode,
OperatorNode,
ParenthesisNode,
SymbolNode
SymbolNode,
replacer
}
) => {
const { hasProperty, isCommutative, isAssociative, mergeContext, flatten, unflattenr, unflattenl, createMakeNodeFunction, defaultContext, realContext, positiveContext } =
Expand Down Expand Up @@ -816,7 +818,7 @@ export const createSimplify = /* #__PURE__ */ factory(name, dependencies, (
const uniqueSets = []
const unique = {}
for (let i = 0; i < sets.length; i++) {
const s = JSON.stringify(sets[i])
const s = JSON.stringify(sets[i], replacer)
if (!unique[s]) {
unique[s] = true
uniqueSets.push(sets[i])
Expand Down
21 changes: 10 additions & 11 deletions src/function/algebra/simplifyConstant.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,18 +211,17 @@ export const createSimplifyConstant = /* #__PURE__ */ factory(name, dependencies
}

function _fractionToNode (f) {
let n
const vn = f.s * f.n
if (vn < 0) {
n = new OperatorNode('-', 'unaryMinus', [new ConstantNode(-vn)])
} else {
n = new ConstantNode(vn)
}
// note: we convert await from bigint values, because bigint values gives issues with divisions: 1n/2n=0n and not 0.5
const fromBigInt = (value) => config.number === 'BigNumber' && bignumber ? bignumber(value) : Number(value)

if (f.d === 1) {
return n
}
return new OperatorNode('/', 'divide', [n, new ConstantNode(f.d)])
const numeratorValue = f.s * f.n
const numeratorNode = (numeratorValue < 0n)
? new OperatorNode('-', 'unaryMinus', [new ConstantNode(-fromBigInt(numeratorValue))])
: new ConstantNode(fromBigInt(numeratorValue))

return (f.d === 1n)
? numeratorNode
: new OperatorNode('/', 'divide', [numeratorNode, new ConstantNode(fromBigInt(f.d))])
}

/* Handles constant indexing of ArrayNodes, matrices, and ObjectNodes */
Expand Down
4 changes: 2 additions & 2 deletions src/function/arithmetic/fix.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ export const createFix = /* #__PURE__ */ factory(name, dependencies, ({ typed, C
},

Fraction: function (x) {
return x.s < 0 ? x.ceil() : x.floor()
return x.s < 0n ? x.ceil() : x.floor()
},

'Fraction, number | BigNumber': function (x, n) {
return x.s < 0 ? ceil(x, n) : floor(x, n)
return x.s < 0n ? ceil(x, n) : floor(x, n)
},

'Array | Matrix': typed.referToSelf(self => (x) => {
Expand Down
4 changes: 2 additions & 2 deletions src/function/arithmetic/pow.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ export const createPow = /* #__PURE__ */ factory(name, dependencies, ({ typed, c
const yFrac = fraction(y)
const yNum = number(yFrac)
if (y === yNum || Math.abs((y - yNum) / y) < 1e-14) {
if (yFrac.d % 2 === 1) {
return (yFrac.n % 2 === 0 ? 1 : -1) * Math.pow(-x, y)
if (yFrac.d % 2n === 1n) {
return ((yFrac.n % 2n === 0n) ? 1 : -1) * Math.pow(-x, y)
}
}
} catch (ex) {
Expand Down
2 changes: 1 addition & 1 deletion src/function/arithmetic/sign.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const createSign = /* #__PURE__ */ factory(name, dependencies, ({ typed,
},

Fraction: function (x) {
return new Fraction(x.s, 1)
return new Fraction(x.s)
},

// deep map collection, skip zeros since sign(0) = 0
Expand Down
2 changes: 1 addition & 1 deletion src/function/utils/isInteger.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const createIsInteger = /* #__PURE__ */ factory(name, dependencies, ({ ty
},

Fraction: function (x) {
return x.d === 1 && isFinite(x.n)
return x.d === 1n
},

'Array | Matrix': typed.referToSelf(self => x => deepMap(x, self))
Expand Down
2 changes: 1 addition & 1 deletion src/function/utils/isNegative.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const createIsNegative = /* #__PURE__ */ factory(name, dependencies, ({ t

bigint: x => x < 0n,

Fraction: x => x.s < 0, // It's enough to decide on the sign
Fraction: x => x.s < 0n, // It's enough to decide on the sign

Unit: typed.referToSelf(self =>
x => typed.find(self, x.valueType())(x.value)),
Expand Down
2 changes: 1 addition & 1 deletion src/function/utils/isPositive.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const createIsPositive = /* #__PURE__ */ factory(name, dependencies, ({ t

bigint: x => x > 0n,

Fraction: x => x.s > 0 && x.n > 0,
Fraction: x => x.s > 0n && x.n > 0n,

Unit: typed.referToSelf(self =>
x => typed.find(self, x.valueType())(x.value)),
Expand Down
4 changes: 2 additions & 2 deletions src/type/bignumber/function/bignumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ export const createBignumber = /* #__PURE__ */ factory(name, dependencies, ({ ty
}),

Fraction: function (x) {
return new BigNumber(x.n).div(x.d).times(x.s)
return new BigNumber(String(x.n)).div(String(x.d)).times(String(x.s))
},

null: function (x) {
null: function (_x) {
return new BigNumber(0)
},

Expand Down
8 changes: 4 additions & 4 deletions src/type/fraction/Fraction.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@ export const createFractionClass = /* #__PURE__ */ factory(name, dependencies, (
/**
* Get a JSON representation of a Fraction containing type information
* @returns {Object} Returns a JSON object structured as:
* `{"mathjs": "Fraction", "n": 3, "d": 8}`
* `{"mathjs": "Fraction", "n": "3", "d": "8"}`
*/
Fraction.prototype.toJSON = function () {
return {
mathjs: 'Fraction',
n: this.s * this.n,
d: this.d
n: String(this.s * this.n),
d: String(this.d)
}
}

/**
* Instantiate a Fraction from a JSON object
* @param {Object} json a JSON object structured as:
* `{"mathjs": "Fraction", "n": 3, "d": 8}`
* `{"mathjs": "Fraction", "n": "3", "d": "8"}`
* @return {BigNumber}
*/
Fraction.fromJSON = function (json) {
Expand Down
4 changes: 4 additions & 0 deletions src/type/fraction/function/fraction.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ export const createFraction = /* #__PURE__ */ factory(name, dependencies, ({ typ
return new Fraction(numerator, denominator)
},

'bigint, bigint': function (numerator, denominator) {
return new Fraction(numerator, denominator)
},

null: function (x) {
return new Fraction(0)
},
Expand Down
2 changes: 1 addition & 1 deletion src/type/unit/Unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2951,7 +2951,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({
*/
Unit.typeConverters = {
BigNumber: function (x) {
if (x?.isFraction) return new BigNumber(x.n).div(x.d).times(x.s)
if (x?.isFraction) return new BigNumber(String(x.n)).div(String(x.d)).times(String(x.s))
return new BigNumber(x + '') // stringify to prevent constructor error
},

Expand Down
8 changes: 4 additions & 4 deletions src/utils/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function _format (value, options) {
if (looksLikeFraction(value)) {
if (!options || options.fraction !== 'decimal') {
// output as ratio, like '1/3'
return (value.s * value.n) + '/' + value.d
return `${value.s * value.n}/${value.d}`
} else {
// output as decimal, like '0.(3)'
return value.toString()
Expand Down Expand Up @@ -191,9 +191,9 @@ function formatArray (array, options) {
function looksLikeFraction (value) {
return (value &&
typeof value === 'object' &&
typeof value.s === 'number' &&
typeof value.n === 'number' &&
typeof value.d === 'number') || false
typeof value.s === 'bigint' &&
typeof value.n === 'bigint' &&
typeof value.d === 'bigint') || false
}

/**
Expand Down
1 change: 1 addition & 0 deletions test/unit-tests/function/algebra/simplify.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ describe('simplify', function () {
const bigmath = math.create({ number: 'bigint' })
assert.deepStrictEqual(bigmath.simplify('70000000000000000123 + 1').evaluate(), 70000000000000000124n)
assert.deepStrictEqual(bigmath.simplify('70000000000000000123 + 5e3').evaluate(), 70000000000000010000)
assert.deepStrictEqual(bigmath.simplify('70000000000000000123 + bigint(5000)').evaluate(), 70000000000000005123n)
})

it('should not change the value of numbers when converting to fractions (1)', function () {
Expand Down
2 changes: 1 addition & 1 deletion test/unit-tests/json/replacer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('replacer', function () {

it('should stringify a Fraction', function () {
const b = new math.Fraction(0.375)
const json = '{"mathjs":"Fraction","n":3,"d":8}'
const json = '{"mathjs":"Fraction","n":"3","d":"8"}'

assert.deepStrictEqual(JSON.stringify(b), json)
assert.deepStrictEqual(JSON.stringify(b, replacer), json)
Expand Down
14 changes: 13 additions & 1 deletion test/unit-tests/json/reviver.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('reviver', function () {
assert.deepStrictEqual(JSON.parse(json, reviver), 12345678901234567890n)
})

it('should parse a stringified Fraction', function () {
it('should parse a stringified Fraction (fraction.js v4 with numbers)', function () {
const json = '{"mathjs":"Fraction","n":3,"d":8}'
const b = new math.Fraction(0.375)

Expand All @@ -56,6 +56,18 @@ describe('reviver', function () {
assert.strictEqual(obj.d, b.d)
})

it('should parse a stringified Fraction', function () {
const json = '{"mathjs":"Fraction","n":{"mathjs":"bigint","value":"3"},"d":{"mathjs":"bigint","value":"8"}}'
const b = new math.Fraction(0.375)

const obj = JSON.parse(json, reviver)

assert(obj instanceof math.Fraction)
assert.strictEqual(obj.s, b.s)
assert.strictEqual(obj.n, b.n)
assert.strictEqual(obj.d, b.d)
})

it('should parse a stringified Range', function () {
const json = '{"mathjs":"Range","start":2,"end":10}'
const r = new math.Range(2, 10)
Expand Down
4 changes: 2 additions & 2 deletions test/unit-tests/type/fraction/Fraction.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ describe('Fraction', function () {
})

it('toJSON', function () {
assert.deepStrictEqual(new math.Fraction(0.375).toJSON(), { mathjs: 'Fraction', n: 3, d: 8 })
assert.deepStrictEqual(new math.Fraction(-0.375).toJSON(), { mathjs: 'Fraction', n: -3, d: 8 })
assert.deepStrictEqual(new math.Fraction(0.375).toJSON(), { mathjs: 'Fraction', n: '3', d: '8' })
assert.deepStrictEqual(new math.Fraction(-0.375).toJSON(), { mathjs: 'Fraction', n: '-3', d: '8' })
})

it('fromJSON', function () {
Expand Down
6 changes: 5 additions & 1 deletion test/unit-tests/type/fraction/function/fraction.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ describe('fraction', function () {
})

it('should create a fraction from a bigint', function () {
equalFraction(math.fraction(42n), new Fraction('42'))
equalFraction(math.fraction(42), new Fraction(42))
equalFraction(math.fraction(42n), new Fraction(42))
equalFraction(math.fraction(1n, 3n), new Fraction(1, 3))
equalFraction(math.fraction(1n, 3), new Fraction(1, 3))
equalFraction(math.fraction(1, 3n), new Fraction(1, 3))
})

it('should convert the number value of a Unit to Fraction', function () {
Expand Down
6 changes: 4 additions & 2 deletions test/unit-tests/type/numeric.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ describe('numeric', function () {
})

it('should convert a BigNumber to a Fraction', function () {
assert.deepStrictEqual(numeric(math.bignumber(-0.125), 'Fraction'), math.fraction(-1, 8))
assert.deepStrictEqual(numeric(math.bignumber('0.142857142857142857142857'), 'Fraction'), math.fraction(1, 7))
assert.deepStrictEqual(numeric(math.bignumber('-0.125'), 'Fraction'), math.fraction(-1, 8))
assert.deepStrictEqual(numeric(math.bignumber('0.142857142857142857142857'), 'Fraction'),
math.fraction(142857142857142857142857n, 1000000000000000000000000n)
)
})

it('should convert a BigNumber to a number', function () {
Expand Down
2 changes: 1 addition & 1 deletion test/unit-tests/type/unit/Unit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ describe('Unit', function () {
})

const str = JSON.stringify(new Unit(math.fraction(0.375), 'cm'))
assert.deepStrictEqual(str, '{"mathjs":"Unit","value":{"mathjs":"Fraction","n":3,"d":8},"unit":"cm","fixPrefix":false}')
assert.deepStrictEqual(str, '{"mathjs":"Unit","value":{"mathjs":"Fraction","n":"3","d":"8"},"unit":"cm","fixPrefix":false}')

const cmpx = JSON.stringify(new Unit(math.complex(2, 4), 'g'))
assert.strictEqual(cmpx, '{"mathjs":"Unit","value":{"mathjs":"Complex","re":2,"im":4},"unit":"g","fixPrefix":false}')
Expand Down
7 changes: 4 additions & 3 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,7 @@ export interface MathJsInstance extends MathJsFactory {
* fraction
* @returns Returns a fraction
*/
fraction(numerator: bigint, denominator: bigint): Fraction
fraction(numerator: number, denominator: number): Fraction

/**
Expand Down Expand Up @@ -4016,9 +4017,9 @@ export interface MatrixCtor {
export interface BigNumber extends Decimal {}

export interface Fraction {
s: number
n: number
d: number
s: bigint
n: bigint
d: bigint
}

export interface Complex {
Expand Down

0 comments on commit 9b81230

Please sign in to comment.