-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Fraction numerator denominator helpers #3605
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 14 commits
9a0a1d1
df47a9e
071dbb2
0ab6886
83eaf63
801394f
ebb4c86
af362d7
bd2ceb3
4bb73ed
3168e3f
58ffac5
58c4828
844f91c
ecf6476
6400d48
320b719
89072d8
e5287e1
bdd563e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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'] | ||
| } |
| 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'] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| 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`. | ||
| * | ||
| * 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('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 | ||
| * | ||
| * @param {Fraction | BigNumber | Array | Matrix} x | ||
| * A fraction, BigNumber, or array with fractions | ||
| * @return {bigint | Array | Matrix} The denominator of x | ||
| */ | ||
| return typed(name, { | ||
| Fraction: (x) => x.d, | ||
| BigNumber: (x) => fraction(x).d, | ||
| 'Array | Matrix': typed.referToSelf((self) => (x) => deepMap(x, self)) | ||
| }) | ||
| } | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| 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`. | ||
| * | ||
| * 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('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 | ||
| * | ||
| * @param {Fraction | BigNumber | Array | Matrix} x | ||
| * A fraction, BigNumber, or array with fractions | ||
| * @return {bigint | Array | Matrix} The numerator of x | ||
| */ | ||
| 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)) | ||
| }) | ||
| } | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3181,3 +3181,63 @@ 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>() | ||
|
|
||
| // Array of fractions | ||
| const fractionArray = [math.fraction(1, 2), math.fraction(3, 4)] | ||
| expectTypeOf(math.num(fractionArray)).toMatchTypeOf<MathArray>() | ||
| expectTypeOf(math.den(fractionArray)).toMatchTypeOf<MathArray>() | ||
| 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>() | ||
| expectTypeOf(math.den(fractionMatrix)).toMatchTypeOf<Matrix>() | ||
| assert.deepStrictEqual( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And you can put the more specific
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great, thanks! Just need to also put the |
||
| 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)) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| 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 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 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)' | ||
| ) | ||
| }) | ||
gwhitney marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| import assert from 'assert' | ||
| import math from '../../../../src/defaultInstance.js' | ||
|
|
||
| describe('num', function () { | ||
| it('should return the numerator of a fraction', function () { | ||
| assert.strictEqual(math.num(math.fraction(2, 3)), 2n) | ||
| assert.strictEqual(math.num(math.fraction(4, 8)), 1n) // simplified to 1/2 | ||
| assert.strictEqual(math.num(math.fraction(5, 1)), 5n) | ||
| }) | ||
|
|
||
| it('should return the numerator of a negative fraction', function () { | ||
| assert.strictEqual(math.num(math.fraction(-2, 3)), -2n) | ||
| assert.strictEqual(math.num(math.fraction(2, -3)), -2n) | ||
| assert.strictEqual(math.num(math.fraction(-2, -3)), 2n) | ||
| }) | ||
|
|
||
| it('should return the numerator of a fraction string', function () { | ||
| assert.strictEqual(math.num(math.fraction('2/3')), 2n) | ||
| assert.strictEqual(math.num(math.fraction('5/8')), 5n) | ||
| assert.strictEqual(math.num(math.fraction('-5/8')), -5n) | ||
| }) | ||
|
|
||
| it('should return the numerator for each element in a matrix', function () { | ||
| assert.deepStrictEqual( | ||
| math.num([math.fraction('2/3'), math.fraction('5/8')]), | ||
| [2n, 5n] | ||
| ) | ||
| assert.deepStrictEqual( | ||
| math | ||
| .num(math.matrix([math.fraction('2/3'), math.fraction('5/8')])) | ||
| .valueOf(), | ||
| [2n, 5n] | ||
| ) | ||
| }) | ||
|
|
||
| it('should return the numerator of a BigNumber by converting to fraction', function () { | ||
| assert.strictEqual(math.num(math.bignumber('1')), 1n) | ||
| assert.strictEqual(math.num(math.bignumber('0.5')), 1n) // 0.5 = 1/2 | ||
| assert.strictEqual(math.num(math.bignumber('0.25')), 1n) // 0.25 = 1/4 | ||
| assert.strictEqual(math.num(math.bignumber('0.125')), 1n) // 0.125 = 1/8 | ||
| assert.strictEqual(math.num(math.bignumber('-0.5')), -1n) // -0.5 = -1/2 | ||
| assert.strictEqual(math.num(math.bignumber('0.75')), 3n) // 0.75 = 3/4 | ||
| assert.strictEqual(math.num(math.bignumber('0.2')), 1n) // 0.2 = 1/5 | ||
| assert.strictEqual(math.num(math.bignumber('-0.2')), -1n) // -0.2 = -1/5 | ||
| }) | ||
|
|
||
| it('should throw an error when called with an unsupported type of argument', function () { | ||
| assert.throws(function () { | ||
| math.num(new Date()) | ||
| }, /TypeError: Unexpected type of argument/) | ||
| assert.throws(function () { | ||
| math.num(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.num() | ||
| }, /TypeError: Too few arguments/) | ||
| assert.throws(function () { | ||
| math.num(math.fraction(1, 2), 2) | ||
| }, /TypeError: Too many arguments/) | ||
| }) | ||
|
|
||
| it('should LaTeX num', function () { | ||
| const expression = math.parse('num(fraction(1,2))') | ||
| assert.strictEqual( | ||
| expression.toTex(), | ||
| '\\mathrm{num}\\left(\\frac{1}{2}\\right)' | ||
| ) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you look throughout math.js, you will see the convention is to drop the parentheses in unary arrow-functions. E.g., this last one should be
self => x => deepMap(s, self), and so on for several other examples throughout this PR. Please correct, thanks.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still several examples of
(onlyOneArgument) => functionBodyrather thanonlyOneArgument => functionBodyin this PR; please convert to the latter format which is far more common throughout mathjs source code, thank you!