Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ Richard Taylor <richard.taylor@claconnect.com>
NilsDietrich <61544566+NilsDietrich@users.noreply.github.com>
anslem chibuike <144047596+AnslemHack@users.noreply.github.com>
Ayomide Bamigbade <iamtryve@gmail.com>
Chibuike Anselm <chibuikeanselm@Chibuikes-MacBook-Pro.local>
Dheemanth07 <dheemanth1007@gmail.com>
Anadian <willanad@yandex.com>

Expand Down
4 changes: 4 additions & 0 deletions src/expression/embeddedDocs/embeddedDocs.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ import { subtractDocs } from './function/arithmetic/subtract.js'
import { unaryMinusDocs } from './function/arithmetic/unaryMinus.js'
import { unaryPlusDocs } from './function/arithmetic/unaryPlus.js'
import { xgcdDocs } from './function/arithmetic/xgcd.js'
import { numDocs } from './function/fraction/num.js'
import { denDocs } from './function/fraction/den.js'
import { bitAndDocs } from './function/bitwise/bitAnd.js'
import { bitNotDocs } from './function/bitwise/bitNot.js'
import { bitOrDocs } from './function/bitwise/bitOr.js'
Expand Down Expand Up @@ -408,6 +410,8 @@ export const embeddedDocs = {
unaryPlus: unaryPlusDocs,
xgcd: xgcdDocs,
invmod: invmodDocs,
num: numDocs,
den: denDocs,

// functions - bitwise
bitAnd: bitAndDocs,
Expand Down
8 changes: 8 additions & 0 deletions src/expression/embeddedDocs/function/fraction/den.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const denDocs = {
name: 'den',
category: 'Fraction',
syntax: ['den(x)'],
description: 'Get the denominator of a fraction.',
examples: ['den(fraction(2, 3))', 'den(fraction(5, 8))'],
seealso: ['num', 'fraction']
}
8 changes: 8 additions & 0 deletions src/expression/embeddedDocs/function/fraction/num.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const numDocs = {
name: 'num',
category: 'Fraction',
syntax: ['num(x)'],
description: 'Get the numerator of a fraction.',
examples: ['num(fraction(2, 3))', 'num(fraction(5, 8))'],
seealso: ['den', 'fraction']
}
2 changes: 2 additions & 0 deletions src/factoriesAny.js
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,5 @@ export { createOrTransform } from './expression/transform/or.transform.js'
export { createNullishTransform } from './expression/transform/nullish.transform.js'
export { createBitAndTransform } from './expression/transform/bitAnd.transform.js'
export { createBitOrTransform } from './expression/transform/bitOr.transform.js'
export { createNum } from './function/fraction/num.js'
export { createDen } from './function/fraction/den.js'
56 changes: 56 additions & 0 deletions src/function/fraction/den.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { factory } from '../../utils/factory.js'
import { deepMap } from '../../utils/collection.js'

const name = 'den'
const dependencies = ['typed', 'fraction']

export const createDen = /* #__PURE__ */ factory(
name,
dependencies,
({ typed, fraction }) => {
/**
* Get the denominator of a fraction.
* For a fraction `a/b`, the function returns `b`.
*
* The result is always in lowest terms. For example, `den(fraction(8, 6))`
* returns `3n` because 8/6 simplifies to 4/3.
*
* For negative fractions like `-a/b` or `a/-b`, the denominator is
* always returned as a positive bigint. The sign is stored in the
* numerator. So `den(fraction(-2, 3))` and `den(fraction(2, -3))`
* both return `3n`.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.den(x)
*
* Examples:
*
* math.den(math.fraction(2, 3)) // returns 3n
* math.den(math.fraction(8, 6)) // returns 3n
* math.den(math.fraction('5/8')) // returns 8n
* math.den(math.fraction(-2, 3)) // returns 3n
* math.den(math.fraction(2, -3)) // returns 3n
* math.den(math.bignumber('0.5')) // returns 2n
*
* See also:
*
* num, fraction
*
* History:
*
* v15.2.0 Created
*
* @param {Fraction | BigNumber | Array | Matrix} x
* A fraction, BigNumber, or array with fractions
* @return {bigint | Array | Matrix} The denominator of x (in lowest terms)
*/
return typed(name, {
Fraction: x => x.d,
BigNumber: x => fraction(x).d,
'Array | Matrix': typed.referToSelf(self => x => deepMap(x, self))
})
}
)
58 changes: 58 additions & 0 deletions src/function/fraction/num.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { factory } from '../../utils/factory.js'
import { deepMap } from '../../utils/collection.js'

const name = 'num'
const dependencies = ['typed', 'fraction']

export const createNum = /* #__PURE__ */ factory(
name,
dependencies,
({ typed, fraction }) => {
/**
* Get the numerator of a fraction.
* For a fraction `a/b`, the function returns `a`.
*
* The result is always in lowest terms. For example, `num(fraction(8, 6))`
* returns `4n` because 8/6 simplifies to 4/3.
*
* For negative fractions like `-a/b` or `a/-b`, the sign is always
* included in the numerator. Both forms are normalized internally, so
* `num(fraction(-2, 3))` and `num(fraction(2, -3))` both return `-2`.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.num(x)
*
* Examples:
*
* math.num(math.fraction(2, 3)) // returns 2n
* math.num(math.fraction(8, 6)) // returns 4n
* math.num(math.fraction('5/8')) // returns 5n
* math.num(math.fraction(-2, 3)) // returns -2n
* math.num(math.fraction(2, -3)) // returns -2n
* math.num(math.bignumber('0.5')) // returns 1n
*
* See also:
*
* den, fraction
*
* History:
*
* v15.2.0 Created
*
* @param {Fraction | BigNumber | Array | Matrix} x
* A fraction, BigNumber, or array with fractions
* @return {bigint | Array | Matrix} The numerator of x (in lowest terms)
*/
return typed(name, {
Fraction: x => x.s * x.n,
BigNumber: x => {
const f = fraction(x)
return f.s * f.n
},
'Array | Matrix': typed.referToSelf(self => x => deepMap(x, self))
})
}
)
72 changes: 72 additions & 0 deletions test/typescript-tests/testTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3181,3 +3181,75 @@ Match types of exact positional arguments.
expectTypeOf(mixArray3).toMatchTypeOf<MathArray<MathScalarType>>()
expectTypeOf(unitArray3).toMatchTypeOf<MathArray<Unit>>()
}

/*
Numerator and Denominator examples
*/
{
const math = create(all, {})

// Basic fraction tests
assert.strictEqual(math.num(math.fraction(2, 3)), BigInt(2))
assert.strictEqual(math.num(math.fraction(5, 8)), BigInt(5))
assert.strictEqual(math.den(math.fraction(2, 3)), BigInt(3))
assert.strictEqual(math.den(math.fraction(5, 8)), BigInt(8))
// BigNumber tests
assert.strictEqual(math.num(math.bignumber('0.5')), BigInt(1))
assert.strictEqual(math.den(math.bignumber('0.5')), BigInt(2))
assert.strictEqual(math.num(math.bignumber('1.5')), BigInt(3))
assert.strictEqual(math.den(math.bignumber('1.5')), BigInt(2))
expectTypeOf(math.num(math.bignumber('0.5'))).toMatchTypeOf<bigint>()
expectTypeOf(math.den(math.bignumber('0.5'))).toMatchTypeOf<bigint>()

// Negative fractions - sign is always in numerator
assert.strictEqual(math.num(math.fraction(-2, 3)), BigInt(-2))
assert.strictEqual(math.num(math.fraction(2, -3)), BigInt(-2))
assert.strictEqual(math.den(math.fraction(-2, 3)), BigInt(3))
assert.strictEqual(math.den(math.fraction(2, -3)), BigInt(3))

// Type checks
expectTypeOf(math.num(math.fraction(2, 3))).toMatchTypeOf<bigint>()
expectTypeOf(math.den(math.fraction(2, 3))).toMatchTypeOf<bigint>()

// Number and bigint examples (automatic conversion to fraction)
assert.strictEqual(math.num(0.5), BigInt(1)) // 0.5 = 1/2
assert.strictEqual(math.den(0.5), BigInt(2))
assert.strictEqual(math.num(1.5), BigInt(3)) // 1.5 = 3/2
assert.strictEqual(math.den(1.5), BigInt(2))
assert.strictEqual(math.num(BigInt(3)), BigInt(3)) // 3 = 3/1
assert.strictEqual(math.den(BigInt(3)), BigInt(1))
expectTypeOf(math.num(0.5)).toMatchTypeOf<bigint>()
expectTypeOf(math.den(0.5)).toMatchTypeOf<bigint>()
expectTypeOf(math.num(BigInt(3))).toMatchTypeOf<bigint>()
expectTypeOf(math.den(BigInt(3))).toMatchTypeOf<bigint>()

// Array of fractions
const fractionArray = [math.fraction(1, 2), math.fraction(3, 4)]
expectTypeOf(math.num(fractionArray)).toMatchTypeOf<MathArray<bigint>>()
expectTypeOf(math.den(fractionArray)).toMatchTypeOf<MathArray<bigint>>()
assert.deepStrictEqual(math.num(fractionArray), [BigInt(1), BigInt(3)])
assert.deepStrictEqual(math.den(fractionArray), [BigInt(2), BigInt(4)])

// Matrix of fractions
const fractionMatrix = math.matrix([math.fraction(1, 2), math.fraction(3, 4)])
expectTypeOf(math.num(fractionMatrix)).toMatchTypeOf<Matrix<bigint>>()
expectTypeOf(math.den(fractionMatrix)).toMatchTypeOf<Matrix<bigint>>()
assert.deepStrictEqual(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And you can put the more specific Matrix<bigint> type here, and add an example where you call num/den on an ordinary number or bigint. Thanks!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks! Just need to also put the <bigint> into .toMatchTypeOf<MathArray>() in a couple of places above as well.

math.num(fractionMatrix),
math.matrix([BigInt(1), BigInt(3)])
)
assert.deepStrictEqual(
math.den(fractionMatrix),
math.matrix([BigInt(2), BigInt(4)])
)

// Chaining
expectTypeOf(
math.chain(math.fraction(2, 3)).num().done()
).toMatchTypeOf<bigint>()
expectTypeOf(
math.chain(math.fraction(2, 3)).den().done()
).toMatchTypeOf<bigint>()
assert.strictEqual(math.chain(math.fraction(2, 3)).num().done(), BigInt(2))
assert.strictEqual(math.chain(math.fraction(2, 3)).den().done(), BigInt(3))
}
98 changes: 98 additions & 0 deletions test/unit-tests/function/fraction/den.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import assert from 'assert'
import math from '../../../../src/defaultInstance.js'

describe('den', function () {
it('should return the denominator of a fraction', function () {
assert.strictEqual(math.den(math.fraction(2, 3)), 3n)
assert.strictEqual(math.den(math.fraction(4, 8)), 2n) // simplified to 1/2n
assert.strictEqual(math.den(math.fraction(5, 1)), 1n)
})

it('should return the denominator in lowest terms', function () {
assert.strictEqual(math.den(math.fraction(8, 6)), 3n) // 8/6 = 4/3 in lowest terms
assert.strictEqual(math.den(math.fraction(12, 9)), 3n) // 12/9 = 4/3 in lowest terms
assert.strictEqual(math.den(math.fraction(15, 10)), 2n) // 15/10 = 3/2 in lowest terms
assert.strictEqual(math.den(math.fraction(20, 15)), 3n) // 20/15 = 4/3 in lowest terms
})

it('should return the denominator of a negative fraction', function () {
assert.strictEqual(math.den(math.fraction(-2, 3)), 3n)
assert.strictEqual(math.den(math.fraction(2, -3)), 3n)
assert.strictEqual(math.den(math.fraction(-2, -3)), 3n)
})

it('should return the denominator of a fraction string', function () {
assert.strictEqual(math.den(math.fraction('2/3')), 3n)
assert.strictEqual(math.den(math.fraction('5/8')), 8n)
assert.strictEqual(math.den(math.fraction('-5/8')), 8n)
})

it('should return the denominator for each element in a matrix', function () {
assert.deepStrictEqual(
math.den([math.fraction('2/3'), math.fraction('5/8')]),
[3n, 8n]
)
assert.deepStrictEqual(
math
.den(math.matrix([math.fraction('2/3'), math.fraction('5/8')]))
.valueOf(),
[3n, 8n]
)
})

it('should return the denominator of a BigNumber by converting to fraction', function () {
assert.strictEqual(math.den(math.bignumber('1')), 1n)
assert.strictEqual(math.den(math.bignumber('0.5')), 2n) // 0.5 = 1/2
assert.strictEqual(math.den(math.bignumber('0.25')), 4n) // 0.25 = 1/4
assert.strictEqual(math.den(math.bignumber('0.125')), 8n) // 0.125 = 1/8
assert.strictEqual(math.den(math.bignumber('-0.5')), 2n) // -0.5 = -1/2
assert.strictEqual(math.den(math.bignumber('0.75')), 4n) // 0.75 = 3/4
assert.strictEqual(math.den(math.bignumber('0.2')), 5n) // 0.2 = 1/5
assert.strictEqual(math.den(math.bignumber('-0.2')), 5n) // -0.2 = -1/5
})

it('should return the denominator of a number by converting to fraction', function () {
assert.strictEqual(math.den(1), 1n) // 1 = 1/1
assert.strictEqual(math.den(0.5), 2n) // 0.5 = 1/2
assert.strictEqual(math.den(0.25), 4n) // 0.25 = 1/4
assert.strictEqual(math.den(0.125), 8n) // 0.125 = 1/8
assert.strictEqual(math.den(-0.5), 2n) // -0.5 = -1/2
assert.strictEqual(math.den(0.75), 4n) // 0.75 = 3/4
assert.strictEqual(math.den(0.2), 5n) // 0.2 = 1/5
assert.strictEqual(math.den(-0.2), 5n) // -0.2 = -1/5
assert.strictEqual(math.den(1.5), 2n) // 1.5 = 3/2
})

it('should return the denominator of a bigint by converting to fraction', function () {
assert.strictEqual(math.den(BigInt(1)), 1n) // 1 = 1/1
assert.strictEqual(math.den(BigInt(3)), 1n) // 3 = 3/1
assert.strictEqual(math.den(BigInt(-5)), 1n) // -5 = -5/1
assert.strictEqual(math.den(BigInt(0)), 1n) // 0 = 0/1
})

it('should throw an error when called with an unsupported type of argument', function () {
assert.throws(function () {
math.den(new Date())
}, /TypeError: Unexpected type of argument/)
assert.throws(function () {
math.den(math.complex(2, 3))
}, /TypeError: Unexpected type of argument/)
})

it('should throw an error in case of invalid number of arguments', function () {
assert.throws(function () {
math.den()
}, /TypeError: Too few arguments/)
assert.throws(function () {
math.den(math.fraction(1, 2), 2)
}, /TypeError: Too many arguments/)
})

it('should LaTeX denominator', function () {
const expression = math.parse('den(fraction(1,2))')
assert.strictEqual(
expression.toTex(),
'\\mathrm{den}\\left(\\frac{1}{2}\\right)'
)
})
})
Loading