Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'Undo' of https://github.com/Veeloxfire/mathjs into Veel…
…oxfire-Undo
- Loading branch information
Showing
7 changed files
with
468 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
export const diffDocs = { | ||
name: 'diff', | ||
category: 'Matrix', | ||
syntax: [ | ||
'diff(arr)', | ||
'diff(arr, dim)' | ||
], | ||
description: [ | ||
'Create a new matrix or array with the difference of the passed matrix or array.', | ||
'Dim parameter is optional and used to indicant the dimension of the array/matrix to apply the difference', | ||
'If no dimension parameter is passed it is assumed as dimension 0', | ||
'Dimension is zero-based in javascript and one-based in the parser', | ||
'Arrays must be \'rectangular\' meaning arrays like [1, 2]', | ||
'If something is passed as a matrix it will be returned as a matrix but other than that all matrices are converted to arrays' | ||
], | ||
examples: [ | ||
'diff([1, 2, 4, 7, 0])', | ||
'diff([1, 2, 4, 7, 0], 0)', | ||
'diff(matrix([1, 2, 4, 7, 0]))', | ||
'diff([[1, 2], [3, 4]])', | ||
'diff([[1, 2], [3, 4]], 0)', | ||
'diff([[1, 2], [3, 4]], 1)', | ||
'diff([[1, 2], [3, 4]], bignumber(1))', | ||
'diff(matrix([[1, 2], [3, 4]]), 1)', | ||
'diff([[1, 2], matrix([3, 4])], 1)' | ||
|
||
], | ||
seealso: ['subtract', 'partitionSelect'] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { isBigNumber, isCollection, isNumber } from '../../utils/is' | ||
import { factory } from '../../utils/factory' | ||
import { errorTransform } from './utils/errorTransform' | ||
import { createDiff } from '../../function/matrix/diff' | ||
|
||
const name = 'diff' | ||
const dependencies = ['typed', 'matrix', 'subtract', 'number', 'bignumber'] | ||
|
||
export const createDiffTransform = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, subtract, number, bignumber }) => { | ||
const diff = createDiff({ typed, matrix, subtract, number, bignumber }) | ||
|
||
/** | ||
* Attach a transform function to math.diff | ||
* Adds a property transform containing the transform function. | ||
* | ||
* This transform creates a range which includes the end value | ||
*/ | ||
return typed(name, { | ||
'...any': function (args) { | ||
// change last argument dim from one-based to zero-based | ||
if (args.length === 2 && isCollection(args[0])) { | ||
const dim = args[1] | ||
if (isNumber(dim)) { | ||
args[1] = dim - 1 | ||
} else if (isBigNumber(dim)) { | ||
args[1] = dim.minus(1) | ||
} | ||
} | ||
|
||
try { | ||
return diff.apply(null, args) | ||
} catch (err) { | ||
throw errorTransform(err) | ||
} | ||
} | ||
}) | ||
}, { isTransformFunction: true }) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
import { factory } from '../../utils/factory' | ||
import { isInteger } from '../../utils/number' | ||
import { isMatrix } from '../../utils/is' | ||
|
||
const name = 'diff' | ||
const dependencies = ['typed', 'matrix', 'subtract', 'number', 'bignumber'] | ||
|
||
export const createDiff = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, subtract, number, bignumber }) => { | ||
/** | ||
* Create a new matrix or array of the difference between elements of the given array | ||
* The optional dim parameter lets you specify the dimension to evaluate the difference of | ||
* If no dimension parameter is passed it is assumed as dimension 0 | ||
* | ||
* Dimension is zero-based in javascript and one-based in the parser and can be a number or bignumber | ||
* Arrays must be 'rectangular' meaning arrays like [1, 2] | ||
* If something is passed as a matrix it will be returned as a matrix but other than that all matrices are converted to arrays | ||
* | ||
* Syntax: | ||
* | ||
* math.diff(arr) | ||
* math.diff(arr, dim) | ||
* | ||
* Examples: | ||
* | ||
* const arr = [1, 2, 4, 7, 0] | ||
* math.diff(arr) // returns [1, 2, 3, -7] (no dimension passed so 0 is assumed) | ||
* math.diff(math.matrix(arr)) // returns math.matrix([1, 2, 3, -7]) | ||
* | ||
* const arr = [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [9, 8, 7, 6, 4]] | ||
* math.diff(arr) // returns [[0, 0, 0, 0, 0], [8, 6, 4, 2, -1]] | ||
* math.diff(arr, 0) // returns [[0, 0, 0, 0, 0], [8, 6, 4, 2, -1]] | ||
* math.diff(arr, 1) // returns [[1, 1, 1, 1], [1, 1, 1, 1], [-1, -1, -1, -2]] | ||
* math.diff(arr, math.bignumber(1)) // returns [[1, 1, 1, 1], [1, 1, 1, 1], [-1, -1, -1, -2]] | ||
* | ||
* math.diff(arr, 2) // throws RangeError as arr is 2 dimensional not 3 | ||
* math.diff(arr, -1) // throws RangeError as negative dimensions are not allowed | ||
* | ||
* // These will all produce the same result | ||
* math.diff([[1, 2], [3, 4]]) | ||
* math.diff([math.matrix([1, 2]), math.matrix([3, 4])]) | ||
* math.diff([[1, 2], math.matrix([3, 4])]) | ||
* math.diff([math.matrix([1, 2]), [3, 4]]) | ||
* // They do not produce the same result as math.diff(math.matrix([[1, 2], [3, 4]])) as this returns a matrix | ||
* | ||
* See Also: | ||
* | ||
* Subtract | ||
* PartitionSelect | ||
* | ||
* @param {Array | Matrix} arr An array or matrix | ||
* @param {number} dim Dimension | ||
* @return {Array | Matrix} Difference between array elements in given dimension | ||
*/ | ||
return typed(name, { | ||
'Array | Matrix': function (arr) { // No dimension specified => assume dimension 0 | ||
if (isMatrix(arr)) { | ||
return matrix(_diff(arr.toArray())) | ||
} else { | ||
return _diff(arr) | ||
} | ||
}, | ||
'Array | Matrix, number': function (arr, dim) { | ||
if (!isInteger(dim)) throw RangeError('Dimension must be a whole number') | ||
if (isMatrix(arr)) { | ||
return matrix(_recursive(arr.toArray(), dim)) | ||
} else { | ||
return _recursive(arr, dim) | ||
} | ||
}, | ||
'Array | Matrix, BigNumber': function (arr, dim) { | ||
const maxInt = bignumber(Number.MAX_SAFE_INTEGER) | ||
const minInt = bignumber(Number.MIN_SAFE_INTEGER) | ||
if (dim > maxInt || dim < minInt) throw RangeError('The array does not have more than 2^53 dimensions') | ||
if (!dim.isInt()) throw RangeError('Dimension must be a whole number') | ||
if (isMatrix(arr)) { | ||
return matrix(_recursive(arr.toArray(), number(dim))) | ||
} else { | ||
return _recursive(arr, number(dim)) | ||
} | ||
} | ||
}) | ||
|
||
/** | ||
* Recursively find the correct dimension in the array/matrix | ||
* Then Apply _diff to that dimension | ||
* | ||
* @param {Array} arr The array | ||
* @param {number} dim Dimension | ||
* @return {Array} resulting array | ||
*/ | ||
function _recursive (arr, dim) { | ||
if (isMatrix(arr)) { | ||
arr = arr.toArray() // Makes sure arrays like [ matrix([0, 1]), matrix([1, 0]) ] are processed properly | ||
} | ||
if (!Array.isArray(arr)) { | ||
throw RangeError('Array/Matrix does not have that many dimensions') | ||
} | ||
if (dim > 0) { | ||
const result = [] | ||
arr.forEach(element => { | ||
result.push(_recursive(element, dim - 1)) | ||
}) | ||
return result | ||
} else if (dim === 0) { | ||
return _diff(arr) | ||
} else { | ||
throw RangeError('Cannot have negative dimension') | ||
} | ||
} | ||
|
||
/** | ||
* Difference between elements in the array | ||
* | ||
* @param {Array} arr An array | ||
* @return {Array} resulting array | ||
*/ | ||
function _diff (arr) { | ||
const result = [] | ||
const size = arr.length | ||
if (size < 2) { | ||
return arr | ||
} | ||
for (let i = 1; i < size; i++) { | ||
result.push(_ElementDiff(arr[i - 1], arr[i])) | ||
} | ||
return result | ||
} | ||
|
||
/** | ||
* Difference between 2 objects | ||
* | ||
* @param {Object} obj1 First object | ||
* @param {Object} obj2 Second object | ||
* @return {Array} resulting array | ||
*/ | ||
function _ElementDiff (obj1, obj2) { | ||
// Convert matrices to arrays | ||
if (isMatrix(obj1)) obj1 = obj1.toArray() | ||
if (isMatrix(obj2)) obj2 = obj2.toArray() | ||
|
||
const obj1IsArray = Array.isArray(obj1) | ||
const obj2IsArray = Array.isArray(obj2) | ||
if (obj1IsArray && obj2IsArray) { | ||
return _ArrayDiff(obj1, obj2) | ||
} | ||
if (!obj1IsArray && !obj2IsArray) { | ||
return subtract(obj2, obj1) // Difference is (second - first) NOT (first - second) | ||
} | ||
throw TypeError('Cannot calculate difference between 1 array and 1 non-array') | ||
} | ||
|
||
/** | ||
* Difference of elements in 2 arrays | ||
* | ||
* @param {Array} arr1 Array 1 | ||
* @param {Array} arr2 Array 2 | ||
* @return {Array} resulting array | ||
*/ | ||
function _ArrayDiff (arr1, arr2) { | ||
if (arr1.length !== arr2.length) { | ||
throw RangeError('Not all sub-arrays have the same length') | ||
} | ||
const result = [] | ||
const size = arr1.length | ||
for (let i = 0; i < size; i++) { | ||
result.push(_ElementDiff(arr1[i], arr2[i])) | ||
} | ||
return result | ||
} | ||
}) |
36 changes: 36 additions & 0 deletions
36
test/unit-tests/expression/transform/diff.transform.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import assert from 'assert' | ||
import math from '../../../../src/bundleAny' | ||
|
||
const diff = math.expression.transform.diff | ||
|
||
describe('diff.transform', function () { | ||
it('Should use one-based indexing for dimensions with arrays', function () { | ||
// With Dim = 1 | ||
assert.deepStrictEqual(diff([1, 2, 4, 7, 0], 1), [1, 2, 3, -7]) | ||
assert.deepStrictEqual(diff([1, 2, 4, 7, 0], math.bignumber(1)), [1, 2, 3, -7]) | ||
|
||
// Without Dim = 1 | ||
assert.deepStrictEqual(diff([1, 2, 4, 7, 0]), [1, 2, 3, -7]) | ||
}) | ||
|
||
it('Should use one-based indexing for dimensions with matrices', function () { | ||
// With Dim = 1 | ||
assert.deepStrictEqual(diff(math.matrix([1, 2, 4, 7, 0]), 1), math.matrix([1, 2, 3, -7])) | ||
assert.deepStrictEqual(diff(math.matrix([1, 2, 4, 7, 0]), math.bignumber(1)), math.matrix([1, 2, 3, -7])) | ||
|
||
// Without Dim = 1 | ||
assert.deepStrictEqual(diff(math.matrix([1, 2, 4, 7, 0])), math.matrix([1, 2, 3, -7])) | ||
}) | ||
|
||
it('should throw an error if the dimension is below the range for one based indices', function () { | ||
assert.throws(function () { diff(math.matrix([1, 2, 4, 7, 0]), 0) }, Error) | ||
}) | ||
|
||
it('should throw an error if the dimension is above the range for one based indices', function () { | ||
assert.throws(function () { diff(math.matrix([1, 2, 4, 7, 0]), math.bignumber(0)) }, Error) | ||
}) | ||
|
||
it('should work with the parser', function () { | ||
assert.deepStrictEqual(math.evaluate('diff([1, 2, 4, 7, 0], 1)'), math.matrix([1, 2, 3, -7])) | ||
}) | ||
}) |
Oops, something went wrong.