diff --git a/docs/datatypes/matrices.md b/docs/datatypes/matrices.md
index 01178f7413..cb0803f226 100644
--- a/docs/datatypes/matrices.md
+++ b/docs/datatypes/matrices.md
@@ -3,9 +3,11 @@
Math.js supports multidimensional matrices and arrays. Matrices can be
created, manipulated, and used in calculations. Both regular JavaScript
arrays and the matrix type implemented by math.js can be used
-interchangeably in all relevant math.js functions. math.js supports both
-dense and sparse matrices.
-
+interchangeably in all relevant math.js functions. math.js supports dense
+matrices (in which each entry is stored in memory), sparse matrices (in
+which only the non-zero entries are stored, with information about the
+indices at which they occur), and "Range" matrices whose entries are generated
+on the fly by arithmetic sequences.
## Arrays and matrices
@@ -13,13 +15,15 @@ Math.js supports two types of matrices:
- `Array`, a regular JavaScript array. A multidimensional array can be created
by nesting arrays.
-- `Matrix`, a matrix implementation by math.js. A `Matrix` is an object wrapped
- around a regular JavaScript `Array`, providing utility functions for easy
- matrix manipulation such as `subset`, `size`, `resize`, `clone`, and more.
+- `Matrix`, a matrix implementation by math.js. A `Matrix` is an object that
+ provides utility functions for easy matrix manipulation such as `subset`,
+ `size`, `resize`, `clone`, and more. There are multiple concrete
+ implementations for this `Matrix` api (see below); for example, the
+ `DenseMatrix` is a wrapper around a possibly multidimensional `Array`.
In most cases, the type of matrix output from functions is determined by the
-function input: An `Array` as input will return an `Array`, a `Matrix` as input
-will return a `Matrix`. In case of mixed input, a `Matrix` is returned.
+function input: An `Array` as input will return an `Array` and a `Matrix` as
+input will return a `Matrix`. In case of mixed input, a `Matrix` is returned.
For functions where the type of output cannot be determined from the
input, the output is determined by the configuration option `matrix`,
which can be a string `'Matrix'` (default) or `'Array'`. The function `size` is
@@ -124,6 +128,16 @@ math.range(0, 8, 2) // [0, 2, 4, 6]
math.range(3, -1, -1) // [3, 2, 1, 0]
```
+A range can also be created by passing a plain object of attributes, with any
+or all of the properties `start`, `step`, `length` (number of entries),
+`end` (exclusive limit), or `last` (inclusive limit). For example,
+```js
+math.range({start: 2, length: 3}) // [2, 3, 4]
+math.range({start: math.fraction(0), last: math.fraction(1), length: 4})
+ // Fractions [0, 1/3, 2/3, 1]
+math.range({start: zeros(3), step: [1, 2, 3], length: 3})
+ // [[0, 0, 0], [1, 2, 3], [2, 4, 6]]
+```
## Calculations
@@ -270,7 +284,10 @@ Matrices have a `subset` function, which is applied to the matrix itself:
`Matrix.subset(index [, replacement])`. For both matrices and arrays,
the static function `subset(matrix, index [, replacement])` can be used.
When parameter `replacement` is provided, the function will replace a subset
-in the matrix, and if not, a subset of the matrix will be returned.
+in the matrix, and if not, a subset of the matrix will be returned. Note that
+Ranges are immutable (since their entries are defined by a specific
+mathematical relation) and hence only allow their subsets to be read, not
+replaced.
A subset can be defined using an `Index`. An `Index` contains a single value
or a set of values for each dimension of a matrix. An `Index` can be
@@ -370,7 +387,7 @@ const m = math.matrix([[1, 2, 3], [4, 5, 6]])
There are two methods available on matrices that allow to get or set a single
value inside a matrix. It is important to note that the `set` method will
-mutate the matrix.
+mutate the matrix (and so is disallowed on Ranges).
```js
const p = math.matrix([[1, 2], [3, 4]])
@@ -504,10 +521,18 @@ At this moment `forEach` doesn't include the same functionality.
Math.js supports both dense matrices and sparse matrices. Sparse matrices are efficient for matrices largely containing zeros. In that case they save a lot of memory, and calculations can be much faster than for dense matrices.
-Math.js supports two type of matrices:
+Math.js supports three types of matrices:
-- Dense matrix (`'dense'`, `default`) A regular, dense matrix, supporting multidimensional matrices. This is the default matrix type.
+- Dense matrix (`'dense'`, `default`) A regular, dense matrix, supporting
+ multidimensional matrices. This is the default matrix type.
- Sparse matrix (`'sparse'`): A two dimensional sparse matrix implementation.
+- Range ('range'): A matrix that has entries specified by an arithmetic
+ sequence. Note that it is possible for a Range to be multidimensional, e.g.
+ via
+```js
+const r2d = math.range([1, 11, 21], [4, 14, 24], [1, 1, 1])
+// returns a Range representing [[1, 11, 21], [2, 12, 22], [3, 13, 23]]
+```
The type of matrix can be selected when creating a matrix using the construction functions `matrix`, `diag`, `identity`, `ones`, and `zeros`.
@@ -515,6 +540,8 @@ The type of matrix can be selected when creating a matrix using the construction
// create sparse matrices
const m1 = math.matrix([[0, 1], [0, 0]], 'sparse')
const m2 = math.identity(1000, 1000, 'sparse')
+// create a range matrix
+const m3 = math.ones(3, 4, 'range')
```
You can also coerce an array or matrix into sparse storage format with the
@@ -523,7 +550,6 @@ You can also coerce an array or matrix into sparse storage format with the
const md = math.matrix([[0, 1], [0,0]]) // dense
const ms = math.sparse(md) // sparse
```
-
Caution: `sparse` called on a JavaScript array of _n_ plain numbers produces
a matrix with one column and _n_ rows -- in contrast to `matrix`, which
produces a 1-dimensional matrix object with _n_ entries, i.e., a vector
@@ -534,6 +560,13 @@ const mv = math.matrix([0, 0, 1]) // Has size [3]
const mc = math.sparse([0, 0, 1]) // A "column vector," has size [3, 1]
```
+And you can create Ranges directly with the `range` function.
+```js
+const mr = math.range(2.5, 8.5, 1.5) // Range [2.5, 4, 5.5, 7]
+const mr = math.range({start: 3n, step: 2n, length: 3})
+ // Range [3n, 5n, 7n]
+```
+
## API
All relevant functions in math.js support Matrices and Arrays. Functions like `math.add` and `math.subtract`, `math.sqrt` handle matrices element wise. There is a set of functions specifically for creating or manipulating matrices, such as:
@@ -542,9 +575,17 @@ All relevant functions in math.js support Matrices and Arrays. Functions like `m
- Functions like `math.subset` and `math.index` to get or replace a part of a matrix
- Functions like `math.transpose` and `math.diag` to manipulate matrices.
-A full list of matrix functions is available on the [functions reference page](../reference/functions.md#matrix-functions).
+A full list of matrix functions is available on the
+[functions reference page](../reference/functions.md#matrix-functions).
+
+The common `Matrix` interface implemented by all Matrix classes has its own
+[documentation page](../reference/classes/matrix.md).
-Two types of matrix classes are available in math.js, for storage of dense and sparse matrices. Although they contain public functions documented as follows, using the following API directly is *not* recommended. Prefer using the functions in the "math" namespace wherever possible.
+Three types of Matrix classes are available in math.js, for storage of dense,
+sparse, and range matrices. Although they contain public functions documented
+as follows, using the following APIs directly is *not* recommended. Prefer
+using the functions in the "math" namespace wherever possible.
- [DenseMatrix](../reference/classes/densematrix.md)
- [SparseMatrix](../reference/classes/sparsematrix.md)
+- [Range](../reference/classes/range.md)
diff --git a/docs/deprecation_status.md b/docs/deprecation_status.md
new file mode 100644
index 0000000000..992360bffc
--- /dev/null
+++ b/docs/deprecation_status.md
@@ -0,0 +1,32 @@
+# Deprecated and discontinued features
+
+On rare occasions, to make the design of mathjs more systematic or to smoothly
+accommodate new features, it is necessary to remove previous features. In such
+cases, there is generally first discussion on the
+[Github repository](https://github.com/josdejong/mathjs) about the need for
+deprecation. Then in some release (most likely a major-version release), the
+feature is placed into a deprecated state, in which it still works, typically
+exactly as it had worked, but a JavaScript console
+warning is issued when the feature is used. Finally, no sooner than the second
+major-version release later, the feature is discontinued, meaning that it no
+longer functions, although attempts to use it _may_ still throw a JavaScript
+error giving information about the prior functionality.
+
+All documented features not listed on this page are not deprecated and may be
+relied upon to continue operating for at least three major versions.
+Undocumented features are subject to change on any release and should not be
+relied upon.
+
+## Currently deprecated features
+
+|Feature type | Feature| First deprecated in|Comments|
+|-------------|--------|--------------------|--------|
+|Configuration option|`config.compatibility.subset`|v16.0.0|reverts a breaking change to the behavior of `math.subset()` implemented in v15.0.0|
+|Library function |`math.apply()` |v14.2.0|use synonymous `math.mapSlices()` instead|
+|Class method |`Range.parse()` |v16.0.0|use library function `math.parse()` instead|
+
+## Discontinued features
+
+|Feature type|Feature|First deprecated in|Discontinued as of|
+|------------|-------|-------------------|------------------|
+|Configuration option|`config.epsilon`|v13.0.0|v16.0.0 |
diff --git a/docs/expressions/syntax.md b/docs/expressions/syntax.md
index 2b8b39d8cc..060ec96697 100644
--- a/docs/expressions/syntax.md
+++ b/docs/expressions/syntax.md
@@ -14,13 +14,15 @@ the lower level syntax of math.js. Differences are:
- No need to prefix functions and constants with the `math.*` namespace,
you can just enter `sin(pi / 4)`.
-- Matrix indexes are one-based instead of zero-based.
+- By default, bracket notation `[1, 2, 3]` produces a Matrix object rather
+ than an Array; and an Array can be written with list notation, as `(1, 2, 3)`.
+- Matrix and Array indexes are one-based instead of zero-based.
- There are index and range operators which allow more conveniently getting
and setting matrix indexes, like `A[2:4, 1]`.
- Both indexes and ranges and have the upper-bound included.
- There is a differing syntax for defining functions. Example: `f(x) = x^2`.
- There are custom operators like `x + y` instead of `add(x, y)`.
-- Some operators are different. For example `^` is used for exponentiation,
+- Some operators are different. For example, `^` is used for exponentiation,
not bitwise xor.
- Implicit multiplication, like `2 pi`, is supported and has special rules.
- Relational operators (`<`, `>`, `<=`, `>=`, `==`, and `!=`) are chained, so the expression `5 < x < 10` is equivalent to `5 < x and x < 10`.
@@ -58,7 +60,8 @@ Functions below.
Operator | Name | Syntax | Associativity | Example | Result
----------- |-----------------------------|-------------| ------------- |-----------------------| ---------------
`(`, `)` | Grouping | `(x)` | None | `2 * (3 + 4)` | `14`
-`[`, `]` | Matrix, Index | `[...]` | None | `[[1,2],[3,4]]` | `[[1,2],[3,4]]`
+ | Array, function arguments | `(x, y,...)`| None | `((), (1,), (1,2))` | `[[], [1], [1,2]]`
+`[`, `]` | Matrix, Index | `[...]` | None | `[[1,2],[3,4]]` | `matrix([[1,2],[3,4]])`
`{`, `}` | Object | `{...}` | None | `{a: 1, b: 2}` | `{a: 1, b: 2}`
`,` | Parameter separator | `x, y` | Left to right | `max(2, 1, 5)` | `5`
`.` | Property accessor | `obj.prop` | Left to right | `obj={a: 12}; obj.a` | `12`
@@ -112,7 +115,7 @@ The operators have the following precedence, from highest to lowest:
Operators | Description
--------------------------------- | --------------------
-`(...)`
`[...]`
`{...}` | Grouping
Matrix
Object
+`(...)`
`[...]`
`{...}` | Grouping/Array
Matrix
Object
`x(...)`
`x[...]`
`obj.prop`
`:`| Function call
Matrix index
Property accessor
Key/value separator
`'` | Matrix transpose
`!` | Factorial
diff --git a/docs/index.md b/docs/index.md
index 47e0340a56..89c9061997 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -38,3 +38,4 @@ Math.js can be used in the browser, in node.js and in any JavaScript engine. Ins
- [Custom bundling](custom_bundling.md)
- [Command Line Interface](command_line_interface.md)
- [History](../HISTORY.md)
+- [Deprecation status](deprecation_status.md) of features
diff --git a/docs/reference/classes/densematrix.md b/docs/reference/classes/densematrix.md
index 8c9f3770ae..69e2ec441d 100644
--- a/docs/reference/classes/densematrix.md
+++ b/docs/reference/classes/densematrix.md
@@ -1,247 +1,87 @@
## DenseMatrix
-Dense Matrix implementation. This type implements an efficient Array format
-for dense matrices.
-
-* _instance_
- * [.storage()](#DenseMatrix+storage) ⇒ string
- * [.datatype()](#DenseMatrix+datatype) ⇒ string
- * [.create(data, [datatype])](#DenseMatrix+create)
- * [.subset(index, [replacement], [defaultValue])](#DenseMatrix+subset)
- * [.get(index)](#DenseMatrix+get) ⇒ \*
- * [.set(index, value, [defaultValue])](#DenseMatrix+set) ⇒ DenseMatrix
- * [.resize(size, [defaultValue], [copy])](#DenseMatrix+resize) ⇒ Matrix
- * [.clone()](#DenseMatrix+clone) ⇒ DenseMatrix
- * [.size()](#DenseMatrix+size) ⇒ Array.<number>
- * [.map(callback)](#DenseMatrix+map) ⇒ DenseMatrix
- * [.forEach(callback)](#DenseMatrix+forEach)
- * [.toArray()](#DenseMatrix+toArray) ⇒ Array
- * [.valueOf()](#DenseMatrix+valueOf) ⇒ Array
- * [.format([options])](#DenseMatrix+format) ⇒ string
- * [.toString()](#DenseMatrix+toString) ⇒ string
- * [.toJSON()](#DenseMatrix+toJSON) ⇒ Object
- * [.diagonal([k])](#DenseMatrix+diagonal) ⇒ Array
- * [.swapRows(i, j)](#DenseMatrix+swapRows) ⇒ Matrix
-* _static_
- * [.diagonal(size, value, [k], [defaultValue])](#DenseMatrix.diagonal) ⇒ DenseMatrix
- * [.fromJSON(json)](#DenseMatrix.fromJSON) ⇒ DenseMatrix
- * [.preprocess(data)](#DenseMatrix.preprocess) ⇒ Array
-
-
-### denseMatrix.storage() ⇒ string
-Get the storage format used by the matrix.
-
-Usage:
-
-```js
-const format = matrix.storage() // retrieve storage format
-```
-
-**Kind**: instance method of DenseMatrix
-**Returns**: string - The storage format.
-
-### denseMatrix.datatype() ⇒ string
-Get the datatype of the data stored in the matrix.
-
-Usage:
-
-```js
-const format = matrix.datatype() // retrieve matrix datatype
-```
-
-**Kind**: instance method of DenseMatrix
-**Returns**: string - The datatype.
-
-### denseMatrix.create(data, [datatype])
-Create a new DenseMatrix
-
-**Kind**: instance method of DenseMatrix
-
-| Param | Type |
-| --- | --- |
-| data | Array |
-| [datatype] | string |
-
-
-### denseMatrix.subset(index, [replacement], [defaultValue])
-Get a subset of the matrix, or replace a subset of the matrix.
-
-Usage:
-
-```js
-const subset = matrix.subset(index) // retrieve subset
-const value = matrix.subset(index, replacement) // replace subset
-```
-
-**Kind**: instance method of DenseMatrix
-
-| Param | Type | Default | Description |
-| --- | --- | --- | --- |
-| index | Index | | |
-| [replacement] | Array | DenseMatrix| \* | | |
-| [defaultValue] | \* | 0 | Default value, filled in on new entries when the matrix is resized. If not provided, new matrix elements will be filled with zeros. |
-
-
-### denseMatrix.get(index) ⇒ \*
-Get a single element from the matrix.
-
-**Kind**: instance method of DenseMatrix
-**Returns**: \* - value
-
-| Param | Type | Description |
-| --- | --- | --- |
-| index | Array.<number> | Zero-based index |
-
-
-### denseMatrix.set(index, value, [defaultValue]) ⇒ DenseMatrix
-Replace a single element in the matrix.
-
-**Kind**: instance method of DenseMatrix
-**Returns**: DenseMatrix- self
-
-| Param | Type | Description |
-| --- | --- | --- |
-| index | Array.<number> | Zero-based index |
-| value | \* | |
-| [defaultValue] | \* | Default value, filled in on new entries when the matrix is resized. If not provided, new matrix elements will be left undefined. |
-
-
-### denseMatrix.resize(size, [defaultValue], [copy]) ⇒ Matrix
-Resize the matrix to the given size. Returns a copy of the matrix when
-`copy=true`, otherwise return the matrix itself (resize in place).
-
-**Kind**: instance method of DenseMatrix
-**Returns**: Matrix - The resized matrix
-
-| Param | Type | Default | Description |
-| --- | --- | --- | --- |
-| size | Array.<number> | | The new size the matrix should have. |
-| [defaultValue] | \* | 0 | Default value, filled in on new entries. If not provided, the matrix elements will be filled with zeros. |
-| [copy] | boolean | | Return a resized copy of the matrix |
-
-
-### denseMatrix.clone() ⇒ DenseMatrix
-Create a clone of the matrix
-
-**Kind**: instance method of DenseMatrix
-**Returns**: DenseMatrix- clone
-
-### denseMatrix.size() ⇒ Array.<number>
-Retrieve the size of the matrix.
-
-**Kind**: instance method of DenseMatrix
-**Returns**: Array.<number> - size
-
-### denseMatrix.map(callback) ⇒ DenseMatrix
-Create a new matrix with the results of the callback function executed on
-each entry of the matrix.
-
-**Kind**: instance method of DenseMatrix
-**Returns**: DenseMatrix- matrix
-
-| Param | Type | Description |
-| --- | --- | --- |
-| callback | function | The callback function is invoked with three parameters: the value of the element, the index of the element, and the Matrix being traversed. |
-
-
-### denseMatrix.forEach(callback)
-Execute a callback function on each entry of the matrix.
-
-**Kind**: instance method of DenseMatrix
-
-| Param | Type | Description |
-| --- | --- | --- |
-| callback | function | The callback function is invoked with three parameters: the value of the element, the index of the element, and the Matrix being traversed. |
-
-
-### denseMatrix.toArray() ⇒ Array
-Create an Array with a copy of the data of the DenseMatrix
-
-**Kind**: instance method of DenseMatrix
-**Returns**: Array - array
-
-### denseMatrix.valueOf() ⇒ Array
-Get the primitive value of the DenseMatrix: a multidimensional array
-
-**Kind**: instance method of DenseMatrix
-**Returns**: Array - array
-
-### denseMatrix.format([options]) ⇒ string
-Get a string representation of the matrix, with optional formatting options.
-
-**Kind**: instance method of DenseMatrix
-**Returns**: string - str
-
-| Param | Type | Description |
-| --- | --- | --- |
-| [options] | Object | number | function | Formatting options. See lib/utils/number:format for a description of the available options. |
-
-
-### denseMatrix.toString() ⇒ string
-Get a string representation of the matrix
-
-**Kind**: instance method of DenseMatrix
-**Returns**: string - str
-
-### denseMatrix.toJSON() ⇒ Object
-Get a JSON representation of the matrix
-
-**Kind**: instance method of DenseMatrix
-
-### denseMatrix.diagonal([k]) ⇒ Array
-Get the kth Matrix diagonal.
-
-**Kind**: instance method of DenseMatrix
-**Returns**: Array - The array vector with the diagonal values.
-
-| Param | Type | Default | Description |
-| --- | --- | --- | --- |
-| [k] | number | BigNumber | 0 | The kth diagonal where the vector will retrieved. |
-
-
-### denseMatrix.swapRows(i, j) ⇒ Matrix
-Swap rows i and j in Matrix.
-
-**Kind**: instance method of DenseMatrix
-**Returns**: Matrix - The matrix reference
-
-| Param | Type | Description |
-| --- | --- | --- |
-| i | number | Matrix row index 1 |
-| j | number | Matrix row index 2 |
-
-
-### DenseMatrix.diagonal(size, value, [k], [defaultValue]) ⇒ DenseMatrix
-Create a diagonal matrix.
-
-**Kind**: static method of DenseMatrix
-
-| Param | Type | Default | Description |
-| --- | --- | --- | --- |
-| size | Array | | The matrix size. |
-| value | number | Array | | The values for the diagonal. |
-| [k] | number | BigNumber | 0 | The kth diagonal where the vector will be filled in. |
-| [defaultValue] | number | | The default value for non-diagonal |
-
-### DenseMatrix.fromJSON(json) ⇒ DenseMatrix
-Generate a matrix from a JSON object
+Implementation of the [Matrix](matrix.md) API as a wrapper around ordinary
+(nested) JavaScript Arrays to hold the content. This representation is
+reasonably efficient for "dense" matrices in which most entries are nonzero,
+which may contain arbitrary data in any entry.
-**Kind**: static method of DenseMatrix
+This page documents only the differences and extensions to the basic Matrix
+API. Any methods of that API not mentioned here operate exactly as documented
+under [Matrix](matrix.md)
-| Param | Type | Description |
-| --- | --- | --- |
-| json | Object | An object structured like `{"mathjs": "DenseMatrix", data: [], size: []}`, where mathjs is optional |
+### Constructing a DenseMatrix
-
-### DenseMatrix.preprocess(data) ⇒ Array
-Preprocess data, which can be an Array or DenseMatrix with nested Arrays and
-Matrices. Replaces all nested Matrices with Arrays
+#### new DenseMatrix(data? datatype?)
-**Kind**: static method of DenseMatrix
-**Returns**: Array - data
+The _data_ should be a (nested, rectangular) Array or a Matrix. Creates a
+DenseMatrix whose contents consist of a deep copy of the provided _data_.
+Any supplied _datatype_ is trusted without checking. If it is not supplied,
+the datatype is copied from the _data_ argument if it is a Matrix, and
+otherwise it is simply left as `undefined`. If _data_ is unspecified, a
+0-dimensional DenseMatrix is constructed.
-| Param | Type |
-| --- | --- |
-| data | Array |
+### Instance methods of Matrix
+#### .storage()
+
+Always returns 'dense'
+
+#### .datatype()
+
+May be undefined, even if the entries are of uniform type, if no datatype
+was specified at construction time. See `.getDataType()` for determining
+a datatype from the data of the matrix itself.
+
+### New instance methods provided by DenseMatrix
+
+#### .getDataType()
+
+Examines the entries of the matrix to see if they are of uniform type, and
+if so, returns the string mathjs name of that type. If not, returns "mixed".
+
+#### .rows()
+
+Returns a JavaScript Array of the rows of this Matrix, each as a 1-D
+DenseMatrix. Throws an error if this Matrix is not 2-dimensional.
+
+#### .columns()
+
+Returns a JavaScript Array of the columns of this Matrix, each as a 1-D
+DenseMatrix. Throws an error if this Matrix is not 2-dimensional.
+
+#### .diagonal(k?)
+
+When _k_ is not specified, returns the main diagonal of this Matrix, as
+as a DenseMatrix. When it is, returns the diagonal _k_ entries above the
+main diagonal when _k_ is positive, and the absolut value of _k_ entries
+below the main diagonal when _k_ is negative. Note that these diagonals do
+_not_ "wrap around", so in a an n×n matrix, `.diagonal(k)` will have n - |k|
+entries.
+
+#### swapRows(i, j)
+
+Alters this Matrix in place by swapping rows _i_ and _j_.
+
+#### .toJSON()
+
+Returns a JSON (plain object) representation of this Matrix. This
+representation can be restored to a DenseMatrix using the static `.fromJson`
+method.
+
+### New static methods
+
+#### DenseMatrix.diagonal(size, diagonalValue?, k?, offDiagValue?)
+
+Constructs a fresh diagonal DenseMatrix of the given _size_. The diagonal
+entries are along the main diagonal if _k_ is not specified or zero; otherwise
+they are along an offset diagonal as per the _k_ parameter of the
+`.diagonal` instance method. The diagonal entries are set to _diagonalValue_
+if it is specified, or one otherwise. The off-diagonal entries are set to
+_offDiagValue_ if it is specified, or zero otherwise. Thus
+`DenseMatrix.diagonal([3, 3])` returns the usual 3×3 identity matrix.
+
+#### DenseMatrix.fromJSON(json)
+
+Constructs a fresh DenseMatrix from _json_, which should be the result of
+a `.toJSON()` call on some DenseMatrix.
diff --git a/docs/reference/classes/matrix.md b/docs/reference/classes/matrix.md
new file mode 100644
index 0000000000..d288a73e71
--- /dev/null
+++ b/docs/reference/classes/matrix.md
@@ -0,0 +1,236 @@
+
+# Matrix API
+
+The mathjs library provides a number of classes that implement the Matrix API
+described here, so that they can be used (at least to a certain extent)
+interchangeably to represent rectangular arrays of numeric values, which may
+be 1-dimensional (a vector of numbers, for example), 2-dimensional (ordinary
+matrices), or 3-dimensional (rectangular solid blocks of numbers, in which
+each "slice" is a matrix), or even higher-dimensional.
+
+Each of the implementing classes is subclass of the Matrix class, which
+serves just to set the interface and generally speaking contains no
+implementations of its own for the methods listed below.
+
+Every Matrix implementation should define a string "storage format",
+describing the concrete aspects of that particular implementation. For example,
+the SparseMatrix implementation that stores only the nonzero entries has
+its storage format equal to "sparse". Each implementation should corresponde
+to a single unique storage format and vice-versa.
+
+A Matrix can optionally have a datatype, which is the mathjs name of the
+data type of every element stored in the Matrix. They are also allowed to
+be heterogeneous, in which case the datatype should be "mixed", undefined, or
+the empty string.
+
+## Instance methods of all Matrix objects
+
+### .storage()
+
+Returns the storage format of the Matrix, as a string.
+
+### .datatype()
+
+Returns the data type of the Matrix, as a string, or possibly undefined if
+the Matrix is heterogeneous or if the data type is not necessarily known.
+
+### .clone()
+
+Returns a fresh "deep copy" of this Matrix.
+
+### .create(data, datatype?)
+
+Generates a fresh Matrix of the same storage format, containing the given
+_data_ (which may be an ordinary JavaScript Array, possibly nested to represent
+a multidimensional matrix, or another Matrix), and having the given _datatype_,
+which may be omitted to infer the datatype from the _data_.
+
+### .size()
+
+Returns the extent of this matrix in each of its dimensions, as an ordinary
+JavaScript Array of nonnegative numbers.
+
+Some examples:
+```
+math.matrix([1, 2, 3]).size() // [3]
+math.matrix([[1, 2, 3], [4, 5, 6]]) // [3, 2]
+```
+
+### .resize(size, defaultValue?, copy?)
+
+Changes the matrix size. If _copy_ is specified and true, leaves this Matrix
+alone; otherwise alters it in place. In either case, the changed Matrix, of the
+same storage type as this Matrix, is returned. If the new size is larger in any
+dimension than the current size, new entries are filled in with the
+_defaultValue_ or zero if it is not specified. If the new size is smaller
+in any dimension, entries are dropped. (A combination is possible). Note that
+if the new size has more entries than this Matrix has dimensions, this Matrix
+is interpreted as lying along the **initial** dimensions of the resulting
+matrix. (In the 1- to 2-dimensional case, that means this Matrix becomes
+a **column**, not a row, of the result.)
+
+Some examples:
+```
+math.matrix([[1, 4], [2, 5], [3, 6]]).resize([2, 3], -1)
+ // [[1, 4, -1], [2, 5, -1]]
+math.matrix([1, 2, 3]).resize([4, 2])
+ // [[1, 0], [2, 0], [3, 0], [0, 0]]
+```
+
+### .subset(index, replacement?, defaultValue?)
+
+Returns (and possibly replaces) a subarray of a Matrix. The _index_ must be
+an instance of the mathjs [Index](matrixindex.md) type, specifying the extent
+of the subarray in question along each dimension of the matrix. If none of
+the optional arguments are specified, the corresponding subarray is simply
+returned as a Matrix of the same storage format as this one. In this form, it
+is an error if the index extends outside the bounds of this matrix.
+
+If _replacement_ and possibly also _defaultValue_ are specified, it is allowed
+(but by no means necessary) for the _index_ to extend outside the bounds of the
+matrix. If so, this matrix is first resized to the smallest rectangle that will
+accommodate the index, filling in any new entries by the _defaultValue_ or
+zero if that argument is not supplied. (So if the specified _index_ is within
+the current bounds of this matrix, the _defaultValue_ argument, if any, is
+simply ignored.) Then the subarray designated by the index is overwritten by
+the _replacement_, potentially broadcasting the entries of the _replacement_
+if necessary to fill the entire designated subarray. Finally, this **entire**
+matrix is returned (not just the replaced portion), unlike in the plain
+access case.
+
+Some examples:
+```
+const M = math.matrix([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 9, 8]])
+M.subset(math.index('1:2', '0:1')) // [[4, 5], [8, 9]]
+M.subset(math.index('1:2', '0:1'), [[1, 2], [3, 4]])
+ // [[0, 1, 2, 3], [1, 2, 6, 7], [3, 4, 9, 8]]
+M.subset(math.index('0:1', '3:4'), [[9, 8], [7, 6]], -99)
+ // [[0, 1, 2, 9, 8], [1, 2, 6, 7, 6], [3, 4, 9, 8, -99]]
+M.subset(math.index('1:2', '2:4'), 5)
+ // [[0, 1, 2, 9, 8], [1, 2, 5, 5, 5], [3, 4, 5, 5, 5]]
+M.subset(math.index('1:2', '2:4'), [0, 1, 2])
+ // [[0, 1, 2, 9, 8], [1, 2, 0, 1, 2], [3, 4, 0, 1, 2]]
+M.subset(math.index('1:2', '2:4'), [[7], [6]])
+ // [[0, 1, 2, 9, 8], [1, 2, 7, 7, 7], [3, 4, 6, 6, 6]]
+```
+
+### .layer(n)
+
+Returns the _n_ th "top-level" slice of this matrix as a Matrix of the same
+type, or as the entry itself if this matrix is 1-dimensional. The argument _n_
+must be an integer number at least 0 and less than the extent of this matrix
+in the first dimension.
+
+Some examples:
+```
+math.matrix([1, 2, 3]).layer(1) // 2
+math.matrix([[1, 2], [3, 4], [6, 7]]).layer(1) // [3, 4]
+math.matrix([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]).layer(1) // [[5, 6], [7, 8]]
+```
+
+### .get(position)
+
+Returns a single entry of this matrix at a specific _position_ given as
+an array of numbers, each one the (0-based) index along the corresponding
+dimension. An error will be thrown if the _position_ lies outside the bounds
+of this matrix.
+
+Some examples:
+```
+math.matrix([1, 2, 3]).get([2]) // 3
+math.matrix([[1, 2], [3, 4], [6, 7]]).get([1, 0]) // 3
+math.matrix([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]).get([0, 1, 0]) // 3
+```
+
+### .set(position, value, defaultValue?)
+
+Replaces a single entry of this matrix and returns the entire matrix. In this
+method, it is permissible for the position to lie outside the current bounds
+of the matrix, in which case the matrix is first resized to the smallest
+rectangular array including the position, using the _defaultValue_ or zero if
+not specified for new entrues.
+
+Some examples:
+```
+const M = math.matrix([1, 2, 3])
+M.set([2], 4) // [1, 2, 4]
+M.set([0, 1], 8, -1) // [[1, 8], [2, -1], [4, -1]]
+```
+
+### .reshape(size, copy?)
+
+The entries of this Matrix in row-major order are inserted into
+a changed Matrix having the specified _size_ in row-major order. Unlike with
+`.resize`, the resulting Matrix is required to have exactly the same number
+of entries as this Matrix. But like `.resize`, the current Matrix is altered
+in place unless the the _copy_ argument is specified and true, in which
+case this Matrix is left alone and the changed Matrix (of the same storage
+format) is returned.
+
+Note that because of the rule on the number of entries, it is allowed to
+specify (no more than) one of the extents of the new _size_ as -1, in which
+case that extent is calculated from the others so that the number of entries
+will match.
+
+Some examples:
+```
+math.matrix([[1, 4], [2, 5], [3, 6]]).reshape([2, 3])
+ // [[1, 4, 2], [5, 3, 6]]
+math.matrix([1, 2, 3, 4, 5, 6, 7, 8]).reshape([2, -1])
+ // [[1, 2, 3, 4], [5, 6, 7, 8]]
+```
+
+### .map(callback, skipZeros?)
+
+Returns a fresh matrix of the same size and storage type as this Matrix, in
+which each entry has been replaced by the result of executing the _callback_
+with three arguments: the value of the entry, the position of the element as a
+plain JavaScript Array of nonnegative numbers, and this Matrix being traversed.
+
+If the _skipZeros_ argument is specified and true, zero entries of this Matrix
+are left alone (and the _callback_ is not executed for them).
+
+### .forEach(callback)
+
+Executes the _callback_ (with the same arguments as in `.map` for each
+entry of this Matrix.
+
+### [Symbol.iterator]
+
+Implementations define a function on the special JavaScript iterator Symbol
+so that the following syntax is supported:
+```
+const M = math.matrix([[1, 2, 3], [4, 5, 6]])
+for (const {value, position} of M) {
+ console.log(`M[${position[0]}, ${position[1]}] = ${value}`)
+}
+```
+This example code will print out a listing of all of the entries of `M` in
+row-major order (so in numerical order in this case).
+
+### .toArray(), .valueOf()
+
+These synonymous methods return the content of this Matrix as a JavaScript
+Array, with nesting depth equal to the number of dimensions of this Matrix.
+
+### .format(options)
+
+Obtain a string representation of this Matrix. It accepts the same options as
+the mathjs [`format` function](../functions/format.js).
+
+### .toString()
+
+Converts this Matrix to a string in a vanilla fashion (similar to the way
+the corresponding nested Array would look).
+
+## Implementations
+
+The current concrete implementations of this interface are listed below,
+with links to their specific documentation.
+
+* [DenseMatrix](densematrix.md)
+* [SparseMatrix](sparsematrix.md)
+* [Range](range.md)
diff --git a/docs/reference/classes/range.md b/docs/reference/classes/range.md
new file mode 100644
index 0000000000..6a584dd1c8
--- /dev/null
+++ b/docs/reference/classes/range.md
@@ -0,0 +1,187 @@
+
+ ## Range
+
+This type implements Matrix objects whose entries are given by arithmetic
+sequences. Only the constants in the definition of the sequence are kept in
+memory; the actual entries are generated on the fly as needed. This design
+makes Range the most efficient in memory usage and often speed, for matrices
+that happen to have entries in this form. Among Matrix implementations, only
+Ranges can represent entities that are infinite in extent, with more
+entries always available to be generated on demand.
+
+Note that the term "arithmetic sequence" does not imply that Ranges are
+only (1-dimensional) vectors. If the a Range starts with, say [1, 2, 3],
+and its step is 3 and its length is 4, then it represents the 2-dimensional
+matrix `[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]`.
+
+Note that a Range matrix object is immutable, in the sense that its definition
+and therefore its entries cannot be changed once the Range has been constructed.
+Therefore, some methods of the [Matrix API](matrix.md) may throw errors if
+they would entail in-place changes of a Range. Such exceptions are not
+otherwise listed here.
+
+This page documents all other differeces and extensions to the basic Matrix
+interface.
+
+### Constructing a Range
+
+Ranges have the most options for construction, representing the many possible
+ways to define an arithmetic sequence.
+
+#### new Range(start?, end?, step?, options?)
+
+First, note that the options argument, which is distinguished as a plain
+object with some of the keys listed below as "attributes" of a Range, may
+appear at any point among the other constructor arguments. The other arguments
+are convenience positional arguments, corresponding to the start, end, and
+step attributes; they can be used instead of the equivalent options, but
+the same attribute may not be specified in two different ways. Thus,
+`new Range({end: 10}, 1)` and `new Range(1, {end: 10})` is OK, but
+`new Range(1, {start: 1, end: 10})` is not.
+
+Every Range has several attributes that determine its entries. Once
+constructed, these attributes cannot be changed; they are read-only.
+Moreover, to allow different terminology that may be clearer in
+different uses of this class, each attribute can be specified (at
+construction time) or read (at any later time) via either of two
+synonymous property names, separated by a vertical bar in the lists below.
+Note that it is perfectly OK to specify an attribute using one of its
+two names and read it later using the other; but the same attribute cannot
+be specified in two different ways, even if the values match.
+
+Every Range has these attributes:
+ * start|from: the first element of the Range
+ * step|by: the step or common difference of the Range
+ * length|for: the number of elements in the Range. Note that this attribute
+ may be Infinity, so that a Range can represent an unending arithmetic
+ progression.
+
+In addition, a Range may have one or both of the following attributes:
+ * last|to: the inclusive final limit of the Range. This value must be
+ of the form `start + t * step` for some number `t`, in which case the
+ Range consists of `start + s * step` for all nonnegative integers `s ≤ t`.
+ * end|til: an exclusive limit of the Range. This value must be of the
+ form `start + u * step` for some number `u`, in which case the Range
+ consists of `start + s * step` for all nonnegative integers `s < u`.
+
+There is a consistency relation, in that if the last value is the start
+value plus `t` times the step value, then the length must be the floor
+of `t`, plus one. Similarly, if the end value is the start value plus `u`
+times the step, then the length must be the ceiling of u. If the step
+of a Range is zero, then it generally does not have an end or last value
+to avoid breaking this consistency relation.
+
+A Range can be constructed from a plain object with any of the above
+attributes, presuming they are consistent. Other positional arguments, if any,
+are interprete as described above.
+
+Because of the consistency relation and defaults provided for convenience,
+some or even all of the attributes may be missing in the constructor.
+If any are missing, they are deduced for you in the following order:
+ * step|by: filled in via consistency if start, length, and at least
+ one of last and end are specified; otherwise set to the "one" value of
+ the type of last, end, or start if specified, or the number 1 if not.
+ * start|from: filled in via consistency with the step if length and at
+ least one of last and end are specified; otherwise set to the "zero"
+ value of the type of last or end if specified, or the number 0 if not.
+ * length|for: filled in via consistency with start and step if at least
+ one of last and end are specified; otherwise, set to 0.
+
+In addition, if the length value is finite and the step is nonzero, the
+following are set whether or not they were specified, to canonicalize
+the attributes of the Range (which makes it easier to use and interpret):
+ * last|to: Set to the start value plus the step times the length minus one.
+ * end|til: Set to the start value plus the step times the length.
+
+Note that the endpoints and increment may be specified with any type
+handled by mathjs, but they must support the operations needed by Range
+(addition, multiplication by an integer ordinary number, comparison).
+The data type of the range is the data type of `start + n*step`, where `n`
+is an integer number; the Range class assumes that this data type does not
+depend on the value of `n`.
+
+#### [DEPRECATED] Range.parse(str)
+
+This static function generates Range objects from strings using the syntax
+(`from:last` or `from:by:last`) of the mathjs expression language. It has
+been deprecated in favor of calling `math.evaluate()` directly.
+
+### Instance methods of Matrix
+
+#### .storage()
+
+Always returns 'range'.
+
+#### .datatype()
+
+Always deduced via `math.typeOf` on the expression `start + 1 * step`.
+
+#### .create()
+
+Note that this function can be given a data array, and will attempt
+to find attributes that will regenerate the array. It will throw an
+error if the entries of the input do not form an arithmetic sequence.
+
+#### .layer(s)
+
+Note that this is the `s`-th element of the arithmetic sequnce, and it
+has a simple formula: it is `start` plus `s * step`.
+
+### New instance methods of Range
+
+#### .getDataType()
+
+Purely for conssistency with the other Matrix implmentations, this method
+is just a synonym for `.datatype`
+
+#### .createRange()
+
+A companion to the `.create()` method that takes exactly the parameters of
+the constructor, rather than the specific parameters enforced on the
+`.create()` method by virtue of the Matrix API.
+
+#### .min()
+
+Returns the smallest element of the sequence. If there is none because
+the sequence has zero length or is infinite and decreasing, returns undefined.
+If the elements of the sequence are not comparable, throws an error.
+
+#### .max()
+
+Returns the largest element of the sequence. If there is none because
+the sequence has zero length or is infinite and increasing, returns undefined.
+If the elements of the sequence are not comparable, throws an error.
+
+#### .rows()
+
+Returns a JavaScript Array of the rows of this Matrix, each as a 1-D
+DenseMatrix. Throws an error if this Matrix is not 2-dimensional.
+
+#### .columns()
+
+Returns a JavaScript Array of the columns of this Matrix, each as a 1-D
+DenseMatrix. Throws an error if this Matrix is not 2-dimensional.
+
+#### .toNumber()
+
+Returns this Matrix itself if the entries of the matrix are JavaScriot
+`number` type. Otherwise (if possible) it returns a fresh Range consisting
+of all of the entries of this Range converted to `number`. Throws an
+error if such conversion is not possible.
+
+#### .toJSON()
+
+Returns a JSON (plain object) representation of this Range. This
+representation can be restored to a Range using the static `.fromJson`
+method.
+
+### New static methods
+
+#### DenseMatrix.fromJSON(json)
+
+Constructs a fresh Range from _json_, which should be the result of
+a `.toJSON()` call on some Range.
diff --git a/docs/reference/classes/sparsematrix.md b/docs/reference/classes/sparsematrix.md
index 3007436180..74bfa9583b 100644
--- a/docs/reference/classes/sparsematrix.md
+++ b/docs/reference/classes/sparsematrix.md
@@ -1,245 +1,90 @@
## SparseMatrix
-Sparse Matrix implementation. This type implements a Compressed Column Storage format
-for sparse matrices.
-
-* _instance_
- * [.storage()](#SparseMatrix+storage) ⇒ string
- * [.datatype()](#SparseMatrix+datatype) ⇒ string
- * [.create(data, [datatype])](#SparseMatrix+create)
- * [.density()](#SparseMatrix+density) ⇒ number
- * [.subset(index, [replacement], [defaultValue])](#SparseMatrix+subset)
- * [.get(index)](#SparseMatrix+get) ⇒ \*
- * [.set(index, value, [defaultValue])](#SparseMatrix+set) ⇒ [SparseMatrix](#SparseMatrix)
- * [.resize(size, [defaultValue], [copy])](#SparseMatrix+resize) ⇒ Matrix
- * [.clone()](#SparseMatrix+clone) ⇒ [SparseMatrix](#SparseMatrix)
- * [.size()](#SparseMatrix+size) ⇒ Array.<number>
- * [.map(callback, [skipZeros])](#SparseMatrix+map) ⇒ [SparseMatrix](#SparseMatrix)
- * [.forEach(callback, [skipZeros])](#SparseMatrix+forEach)
- * [.toArray()](#SparseMatrix+toArray) ⇒ Array
- * [.valueOf()](#SparseMatrix+valueOf) ⇒ Array
- * [.format([options])](#SparseMatrix+format) ⇒ string
- * [.toString()](#SparseMatrix+toString) ⇒ string
- * [.toJSON()](#SparseMatrix+toJSON) ⇒ Object
- * [.diagonal([k])](#SparseMatrix+diagonal) ⇒ Matrix
- * [.swapRows(i, j)](#SparseMatrix+swapRows) ⇒ Matrix
-* _static_
- * [.fromJSON(json)](#SparseMatrix.fromJSON) ⇒ [SparseMatrix](#SparseMatrix)
- * [.diagonal(size, value, [k], [datatype])](#SparseMatrix.diagonal) ⇒ [SparseMatrix](#SparseMatrix)
-
-
-### sparseMatrix.storage() ⇒ string
-Get the storage format used by the matrix.
-
-Usage:
-```js
-const format = matrix.storage() // retrieve storage format
-```
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: string - The storage format.
-
-### sparseMatrix.datatype() ⇒ string
-Get the datatype of the data stored in the matrix.
-
-Usage:
-```js
-const format = matrix.datatype() // retrieve matrix datatype
-```
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: string - The datatype.
-
-### sparseMatrix.create(data, [datatype])
-Create a new SparseMatrix
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-
-| Param | Type |
-| --- | --- |
-| data | Array |
-| [datatype] | string |
-
-
-### sparseMatrix.density() ⇒ number
-Get the matrix density.
-
-Usage:
-```js
-const density = matrix.density() // retrieve matrix density
-```
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: number - The matrix density.
-
-### sparseMatrix.subset(index, [replacement], [defaultValue])
-Get a subset of the matrix, or replace a subset of the matrix.
-
-Usage:
-```js
-const subset = matrix.subset(index) // retrieve subset
-const value = matrix.subset(index, replacement) // replace subset
-```
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-
-| Param | Type | Default | Description |
-| --- | --- | --- | --- |
-| index | Index | | |
-| [replacement] | Array | Maytrix | \* | | |
-| [defaultValue] | \* | 0 | Default value, filled in on new entries when the matrix is resized. If not provided, new matrix elements will be filled with zeros. |
-
-
-### sparseMatrix.get(index) ⇒ \*
-Get a single element from the matrix.
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: \* - value
-
-| Param | Type | Description |
-| --- | --- | --- |
-| index | Array.<number> | Zero-based index |
-
-
-### sparseMatrix.set(index, value, [defaultValue]) ⇒ [SparseMatrix](#SparseMatrix)
-Replace a single element in the matrix.
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: [SparseMatrix](#SparseMatrix) - self
-
-| Param | Type | Description |
-| --- | --- | --- |
-| index | Array.<number> | Zero-based index |
-| value | \* | |
-| [defaultValue] | \* | Default value, filled in on new entries when the matrix is resized. If not provided, new matrix elements will be set to zero. |
-
-
-### sparseMatrix.resize(size, [defaultValue], [copy]) ⇒ Matrix
-Resize the matrix to the given size. Returns a copy of the matrix when
-`copy=true`, otherwise return the matrix itself (resize in place).
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: Matrix - The resized matrix
-
-| Param | Type | Default | Description |
-| --- | --- | --- | --- |
-| size | Array.<number> | | The new size the matrix should have. |
-| [defaultValue] | \* | 0 | Default value, filled in on new entries. If not provided, the matrix elements will be filled with zeros. |
-| [copy] | boolean | | Return a resized copy of the matrix |
-
-
-### sparseMatrix.clone() ⇒ [SparseMatrix](#SparseMatrix)
-Create a clone of the matrix
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: [SparseMatrix](#SparseMatrix) - clone
-
-### sparseMatrix.size() ⇒ Array.<number>
-Retrieve the size of the matrix.
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: Array.<number> - size
-
-### sparseMatrix.map(callback, [skipZeros]) ⇒ [SparseMatrix](#SparseMatrix)
-Create a new matrix with the results of the callback function executed on
-each entry of the matrix.
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: [SparseMatrix](#SparseMatrix) - matrix
-
-| Param | Type | Description |
-| --- | --- | --- |
-| callback | function | The callback function is invoked with three parameters: the value of the element, the index of the element, and the Matrix being traversed. |
-| [skipZeros] | boolean | Invoke callback function for non-zero values only. |
-
-
-### sparseMatrix.forEach(callback, [skipZeros])
-Execute a callback function on each entry of the matrix.
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| callback | function | The callback function is invoked with three parameters: the value of the element, the index of the element, and the Matrix being traversed. |
-| [skipZeros] | boolean | Invoke callback function for non-zero values only. |
-
-
-### sparseMatrix.toArray() ⇒ Array
-Create an Array with a copy of the data of the SparseMatrix
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: Array - array
-
-### sparseMatrix.valueOf() ⇒ Array
-Get the primitive value of the SparseMatrix: a two dimensions array
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: Array - array
-
-### sparseMatrix.format([options]) ⇒ string
-Get a string representation of the matrix, with optional formatting options.
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: string - str
-
-| Param | Type | Description |
-| --- | --- | --- |
-| [options] | Object | number | function | Formatting options. See lib/utils/number:format for a description of the available options. |
-
-
-### sparseMatrix.toString() ⇒ string
-Get a string representation of the matrix
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: string - str
-
-### sparseMatrix.toJSON() ⇒ Object
-Get a JSON representation of the matrix
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-
-### sparseMatrix.diagonal([k]) ⇒ Matrix
-Get the kth Matrix diagonal.
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: Matrix - The matrix vector with the diagonal values.
-
-| Param | Type | Default | Description |
-| --- | --- | --- | --- |
-| [k] | number | BigNumber | 0 | The kth diagonal where the vector will retrieved. |
-
-
-### sparseMatrix.swapRows(i, j) ⇒ Matrix
-Swap rows i and j in Matrix.
-
-**Kind**: instance method of [SparseMatrix](#SparseMatrix)
-**Returns**: Matrix - The matrix reference
-
-| Param | Type | Description |
-| --- | --- | --- |
-| i | number | Matrix row index 1 |
-| j | number | Matrix row index 2 |
-
-
-### SparseMatrix.fromJSON(json) ⇒ [SparseMatrix](#SparseMatrix)
-Generate a matrix from a JSON object
-
-**Kind**: static method of [SparseMatrix](#SparseMatrix)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| json | Object | An object structured like `{"mathjs": "SparseMatrix", "values": [], "index": [], "ptr": [], "size": []}`, where mathjs is optional |
-
-
-### SparseMatrix.diagonal(size, value, [k], [datatype]) ⇒ [SparseMatrix](#SparseMatrix)
-Create a diagonal matrix.
-
-**Kind**: static method of [SparseMatrix](#SparseMatrix)
-
-| Param | Type | Default | Description |
-| --- | --- | --- | --- |
-| size | Array | | The matrix size. |
-| value | number | Array | Matrix | | The values for the diagonal. |
-| [k] | number | BigNumber | 0 | The kth diagonal where the vector will be filled in. |
-| [datatype] | string | | The Matrix datatype, values must be of this datatype. |
+This type implements a Compressed Column Storage format that provides greater
+memory and speed efficiency for sparse matrices, i.e., matrices for which
+a significant fraction of the entries are zero. (Note that when nearly all
+entries are nonzero, this Storage format is less efficient than
+[DenseMatrix](densematrix.md).)
+
+Note that currently the SparseMatrix implementation can only handle
+2-dimensional matrices. Therefore, some methods of the [Matrix API](matrix.md)
+may throw errors if they would entail the creation of a SparseMatrix of
+dimensionality other than two. Such exceptions are not otherwise documented
+here.
+
+This page documents all other differences and extensions to the basic
+[Matrix](matrix.md) API.
+
+### Constructing a SparseMatrix
+
+#### new SparseMatrix(data, datatype?)
+
+The _data_ should be a (nested, rectangular) Array or a Matrix. Creates a
+SparseMatrix whose contents consist of a fresh copy of the provided _data_.
+Any supplied _datatype_ is trusted without checking. If it is not supplied,
+the datatype is copied from the _data_ argument if it is a Matrix, and
+otherwise it is simply left as `undefined`. If _data_ is unspecified, a
+2-dimensional DenseMatrix with 0 extent in each dimension is constructed.
+
+### Instance methods of Matrix
+
+#### .storage()
+
+Always returns 'sparse'
+
+#### .datatype()
+
+May be undefined, even if the entries are of uniform type, if no datatype
+was specified at construction time. See `.getDataType()` for determining
+a datatype from the data of the matrix itself.
+
+### New instance methods provided by DenseMatrix
+
+#### .getDataType()
+
+Examines the entries of the matrix to see if they are of uniform type, and
+if so, returns the string mathjs name of that type. If not, returns "mixed".
+
+#### .density()
+
+Returns the fraction of entries that are nonzero, as a JavaScript number.
+
+#### .diagonal(k?)
+
+When _k_ is not specified, returns the main diagonal of this Matrix, as
+as a SparseMatrix. When it is, returns the diagonal _k_ entries above the
+main diagonal when _k_ is positive, and the absolut value of _k_ entries
+below the main diagonal when _k_ is negative. Note that these diagonals do
+_not_ "wrap around", so in a an n×n matrix, `.diagonal(k)` will have n - |k|
+entries.
+
+#### .toJSON()
+
+Returns a JSON (plain object) representation of this Matrix. This
+representation can be restored to a SparseMatrix using the static `.fromJson`
+method.
+
+#### swapRows(i, j)
+
+Alters this Matrix in place by swapping rows _i_ and _j_.
+
+
+### New static methods
+
+#### DenseMatrix.diagonal(size, diagonalValue?, k?, offDiagValue?)
+
+Constructs a fresh diagonal SparseMatrix of the given _size_. The diagonal
+entries are along the main diagonal if _k_ is not specified or zero; otherwise
+they are along an offset diagonal as per the _k_ parameter of the
+`.diagonal` instance method. The diagonal entries are set to _diagonalValue_
+if it is specified, or one otherwise. The off-diagonal entries are set to
+_offDiagValue_ if it is specified, or zero otherwise. (Note that if both
+the diagonal and off-diagonal values are nonzero, then SparseMatrix is likely
+not a good storage format for the Matrix.) Thus `SparseMatrix.diagonal([3, 3])`
+returns the usual 3×3 identity matrix.
+
+#### SparseMatrix.fromJSON(json)
+
+Constructs a fresh SparseMatrix from _json_, which should be the result of
+a `.toJSON()` call on some SparseMatrix.
diff --git a/package-lock.json b/package-lock.json
index 4ccb555ac9..506cf8afc8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,7 +17,7 @@
"javascript-natural-sort": "^0.7.1",
"seedrandom": "^3.0.5",
"tiny-emitter": "^2.1.0",
- "typed-function": "^4.2.1"
+ "typed-function": "^4.2.2"
},
"bin": {
"mathjs": "bin/cli.js"
@@ -117,6 +117,7 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -2301,6 +2302,7 @@
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
@@ -2365,6 +2367,7 @@
"integrity": "sha512-ixiWrCSRi33uqBMRuICcKECW7rtgY43TbsHDpM2XK7lXispd48opW+0IXrBVxv9NMhaz/Ue9kyj6r3NTVyXm8A==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"undici-types": "~6.20.0"
}
@@ -2422,6 +2425,7 @@
"integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.46.3",
"@typescript-eslint/types": "8.46.3",
@@ -2836,6 +2840,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -3820,6 +3825,7 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.9",
"caniuse-lite": "^1.0.30001746",
@@ -5369,6 +5375,7 @@
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -5454,6 +5461,7 @@
"integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
@@ -5569,6 +5577,7 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@@ -5684,6 +5693,7 @@
"integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"builtins": "^5.0.1",
@@ -5797,6 +5807,7 @@
"integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==",
"dev": true,
"license": "ISC",
+ "peer": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@@ -8504,6 +8515,7 @@
"integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@colors/colors": "1.5.0",
"body-parser": "^1.19.0",
@@ -10523,6 +10535,7 @@
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -11240,6 +11253,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -12595,9 +12609,9 @@
}
},
"node_modules/typed-function": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.1.tgz",
- "integrity": "sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==",
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.2.tgz",
+ "integrity": "sha512-VwaXim9Gp1bngi/q3do8hgttYn2uC3MoT/gfuMWylnj1IeZBUAyPddHZlo1K05BDoj8DYPpMdiHqH1dDYdJf2A==",
"license": "MIT",
"engines": {
"node": ">= 18"
@@ -12620,6 +12634,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -13194,6 +13209,7 @@
"integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.8",
diff --git a/package.json b/package.json
index 3d4c416e5d..383d8b7f1e 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,7 @@
"javascript-natural-sort": "^0.7.1",
"seedrandom": "^3.0.5",
"tiny-emitter": "^2.1.0",
- "typed-function": "^4.2.1"
+ "typed-function": "^4.2.2"
},
"devDependencies": {
"@babel/core": "7.28.5",
diff --git a/src/core/function/typed.js b/src/core/function/typed.js
index 8c527d29ad..895f241cca 100644
--- a/src/core/function/typed.js
+++ b/src/core/function/typed.js
@@ -132,10 +132,10 @@ export const createTyped = /* #__PURE__ */ factory('typed', dependencies, functi
{ name: 'string', test: isString },
{ name: 'Chain', test: isChain },
{ name: 'Array', test: isArray },
- { name: 'Matrix', test: isMatrix },
{ name: 'DenseMatrix', test: isDenseMatrix },
{ name: 'SparseMatrix', test: isSparseMatrix },
{ name: 'Range', test: isRange },
+ { name: 'Matrix', test: isMatrix },
{ name: 'Index', test: isIndex },
{ name: 'boolean', test: isBoolean },
{ name: 'ResultSet', test: isResultSet },
@@ -167,6 +167,11 @@ export const createTyped = /* #__PURE__ */ factory('typed', dependencies, functi
{ name: 'Object', test: isObject } // order 'Object' last, it matches on other classes too
])
+ // Note the order of conversions in this list plays an important
+ // role in selecting conversions. If there is no explicit signature for
+ // type T, but there are signatures for type A and type B and T is
+ // convertible to each of type A and type B, then the signature corresponding
+ // to the conversion that comes earlier on the below list will be preferred.
typed.addConversions([
{
from: 'number',
@@ -235,12 +240,27 @@ export const createTyped = /* #__PURE__ */ factory('typed', dependencies, functi
return new Fraction(x)
}
+ }, {
+ from: 'number',
+ to: 'Fraction',
+ convert: function (x) {
+ if (!Fraction) {
+ throwNoFraction(x)
+ }
+
+ const f = new Fraction(x)
+ if (f.valueOf() !== x) {
+ throw new TypeError('Cannot implicitly convert a number to a Fraction when there will be a loss of precision ' +
+ '(value: ' + x + '). ' +
+ 'Use function fraction(x) to convert to Fraction.')
+ }
+ return f
+ }
}, {
from: 'Fraction',
- to: 'BigNumber',
+ to: 'number',
convert: function (x) {
- throw new TypeError('Cannot implicitly convert a Fraction to BigNumber or vice versa. ' +
- 'Use function bignumber(x) to convert to BigNumber or fraction(x) to convert to Fraction.')
+ return x.valueOf()
}
}, {
from: 'Fraction',
@@ -253,29 +273,13 @@ export const createTyped = /* #__PURE__ */ factory('typed', dependencies, functi
return new Complex(x.valueOf(), 0)
}
}, {
- from: 'number',
- to: 'Fraction',
+ from: 'Fraction',
+ to: 'BigNumber',
convert: function (x) {
- if (!Fraction) {
- throwNoFraction(x)
- }
-
- const f = new Fraction(x)
- if (f.valueOf() !== x) {
- throw new TypeError('Cannot implicitly convert a number to a Fraction when there will be a loss of precision ' +
- '(value: ' + x + '). ' +
- 'Use function fraction(x) to convert to Fraction.')
- }
- return f
+ throw new TypeError('Cannot implicitly convert a Fraction to BigNumber or vice versa. ' +
+ 'Use function bignumber(x) to convert to BigNumber or fraction(x) to convert to Fraction.')
}
}, {
- // FIXME: add conversion from Fraction to number, for example for `sqrt(fraction(1,3))`
- // from: 'Fraction',
- // to: 'number',
- // convert: function (x) {
- // return x.valueOf()
- // }
- // }, {
from: 'string',
to: 'number',
convert: function (x) {
diff --git a/src/expression/embeddedDocs/embeddedDocs.js b/src/expression/embeddedDocs/embeddedDocs.js
index 2e4383beb4..c6dc694687 100644
--- a/src/expression/embeddedDocs/embeddedDocs.js
+++ b/src/expression/embeddedDocs/embeddedDocs.js
@@ -55,6 +55,7 @@ import { ceilDocs } from './function/arithmetic/ceil.js'
import { cubeDocs } from './function/arithmetic/cube.js'
import { divideDocs } from './function/arithmetic/divide.js'
import { dotDivideDocs } from './function/arithmetic/dotDivide.js'
+import { scalarDivideDocs } from './function/arithmetic/scalarDivide.js'
import { dotMultiplyDocs } from './function/arithmetic/dotMultiply.js'
import { dotPowDocs } from './function/arithmetic/dotPow.js'
import { expDocs } from './function/arithmetic/exp.js'
@@ -75,11 +76,13 @@ import { multiplyDocs } from './function/arithmetic/multiply.js'
import { normDocs } from './function/arithmetic/norm.js'
import { nthRootDocs } from './function/arithmetic/nthRoot.js'
import { nthRootsDocs } from './function/arithmetic/nthRoots.js'
+import { oneDocs } from './function/arithmetic/one.js'
import { powDocs } from './function/arithmetic/pow.js'
import { roundDocs } from './function/arithmetic/round.js'
import { signDocs } from './function/arithmetic/sign.js'
import { sqrtDocs } from './function/arithmetic/sqrt.js'
import { sqrtmDocs } from './function/arithmetic/sqrtm.js'
+import { zeroDocs } from './function/arithmetic/zero.js'
import { sylvesterDocs } from './function/algebra/sylvester.js'
import { schurDocs } from './function/algebra/schur.js'
import { lyapDocs } from './function/algebra/lyap.js'
@@ -378,6 +381,7 @@ export const embeddedDocs = {
cube: cubeDocs,
divide: divideDocs,
dotDivide: dotDivideDocs,
+ scalarDivide: scalarDivideDocs,
dotMultiply: dotMultiplyDocs,
dotPow: dotPowDocs,
exp: expDocs,
@@ -397,6 +401,7 @@ export const embeddedDocs = {
norm: normDocs,
nthRoot: nthRootDocs,
nthRoots: nthRootsDocs,
+ one: oneDocs,
pow: powDocs,
round: roundDocs,
sign: signDocs,
@@ -408,6 +413,7 @@ export const embeddedDocs = {
unaryPlus: unaryPlusDocs,
xgcd: xgcdDocs,
invmod: invmodDocs,
+ zero: zeroDocs,
// functions - bitwise
bitAnd: bitAndDocs,
diff --git a/src/expression/embeddedDocs/function/arithmetic/one.js b/src/expression/embeddedDocs/function/arithmetic/one.js
new file mode 100644
index 0000000000..9d53522198
--- /dev/null
+++ b/src/expression/embeddedDocs/function/arithmetic/one.js
@@ -0,0 +1,8 @@
+export const oneDocs = {
+ name: 'one',
+ category: 'arithmetic',
+ syntax: ['one(x)'],
+ description: 'returns the multiplicative identity of the same type as x',
+ examples: ['one(2/3)', 'one([[1, -1], [-1, 2]])'],
+ seealso: ['zero', 'typeOf', 'numeric']
+}
diff --git a/src/expression/embeddedDocs/function/arithmetic/scalarDivide.js b/src/expression/embeddedDocs/function/arithmetic/scalarDivide.js
new file mode 100644
index 0000000000..ba52ce87f1
--- /dev/null
+++ b/src/expression/embeddedDocs/function/arithmetic/scalarDivide.js
@@ -0,0 +1,17 @@
+export const scalarDivideDocs = {
+ name: 'scalarDivide',
+ category: 'arithmetic',
+ syntax: [
+ 'scalarDivide(x, y)'
+ ],
+ description: 'Determine if one entity is a scalar multiple of another',
+ examples: [
+ 'scalarDivide([3, 2, 0], [6, 4, 0])',
+ 'scalarDivide([3, 2, 1], [6, 4, 1])',
+ 'scalarDivide(3, 0)'
+ ],
+ seealso: [
+ 'divide',
+ 'dotDivide'
+ ]
+}
diff --git a/src/expression/embeddedDocs/function/arithmetic/zero.js b/src/expression/embeddedDocs/function/arithmetic/zero.js
new file mode 100644
index 0000000000..465fb4da27
--- /dev/null
+++ b/src/expression/embeddedDocs/function/arithmetic/zero.js
@@ -0,0 +1,8 @@
+export const zeroDocs = {
+ name: 'zero',
+ category: 'arithmetic',
+ syntax: ['zero(x)'],
+ description: 'returns the additive identity of the same type as x',
+ examples: ['zero(2/3)', 'zero([[1, -1, 1], [-1, 2, -1]])'],
+ seealso: ['one', 'typeOf', 'numeric']
+}
diff --git a/src/expression/node/ArrayNode.js b/src/expression/node/ArrayNode.js
index 145e96152f..36cad25cae 100644
--- a/src/expression/node/ArrayNode.js
+++ b/src/expression/node/ArrayNode.js
@@ -13,11 +13,15 @@ export const createArrayNode = /* #__PURE__ */ factory(name, dependencies, ({ No
* @constructor ArrayNode
* @extends {Node}
* Holds an 1-dimensional array with items
- * @param {Node[]} [items] 1 dimensional array with items
+ * @param {Node[]} [items] 1 dimensional array with items
+ * @param {boolean} [forceArray]
+ * Should the result always be Array regardless of config? (default
+ * is false)
*/
- constructor (items) {
+ constructor (items, forceArray = false) {
super()
this.items = items || []
+ this.forceArray = forceArray
// validate input
if (!Array.isArray(this.items) || !this.items.every(isNode)) {
@@ -47,7 +51,7 @@ export const createArrayNode = /* #__PURE__ */ factory(name, dependencies, ({ No
return item._compile(math, argNames)
})
- const asMatrix = (math.config.matrix !== 'Array')
+ const asMatrix = !this.forceArray && (math.config.matrix !== 'Array')
if (asMatrix) {
const matrix = math.matrix
return function evalArrayNode (scope, args, context) {
diff --git a/src/expression/parse.js b/src/expression/parse.js
index 7be4f883ee..e04e6d2fa4 100644
--- a/src/expression/parse.js
+++ b/src/expression/parse.js
@@ -1745,28 +1745,37 @@ export const createParse = /* #__PURE__ */ factory(name, dependencies, ({
* @private
*/
function parseParentheses (state) {
- let node
-
// check if it is a parenthesized expression
- if (state.token === '(') {
- // parentheses (...)
- openParams(state)
- getToken(state)
-
- node = parseAssignment(state) // start again
+ if (state.token !== '(') return parseEnd(state)
+ // Yes, we have parentheses (...)
+ openParams(state)
+ getToken(state)
- if (state.token !== ')') {
- throw createSyntaxError(state, 'Parenthesis ) expected')
- }
+ if (state.token === ')') { // `()` is empty array
closeParams(state)
getToken(state)
+ return parseAccessors(state, new ArrayNode([], true))
+ }
- node = new ParenthesisNode(node)
- node = parseAccessors(state, node)
- return node
+ let node = parseAssignment(state) // start again
+
+ if (state.token === ',') { // Array notation
+ const items = [node]
+ do {
+ getToken(state)
+ if (state.token === ')') break
+ items.push(parseAssignment(state))
+ } while (state.token === ',')
+ node = new ArrayNode(items, true)
+ } else node = new ParenthesisNode(node)
+
+ if (state.token !== ')') {
+ throw createSyntaxError(state, 'Parenthesis ) expected')
}
+ closeParams(state)
+ getToken(state)
- return parseEnd(state)
+ return parseAccessors(state, node)
}
/**
diff --git a/src/expression/transform/and.transform.js b/src/expression/transform/and.transform.js
index 4212eecefe..6440b3f390 100644
--- a/src/expression/transform/and.transform.js
+++ b/src/expression/transform/and.transform.js
@@ -3,10 +3,10 @@ import { factory } from '../../utils/factory.js'
import { isCollection } from '../../utils/is.js'
const name = 'and'
-const dependencies = ['typed', 'matrix', 'zeros', 'add', 'equalScalar', 'not', 'concat']
+const dependencies = ['typed', 'DenseMatrix', 'zeros', 'add', 'equalScalar', 'not']
-export const createAndTransform = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, zeros, not, concat }) => {
- const and = createAnd({ typed, matrix, equalScalar, zeros, not, concat })
+export const createAndTransform = /* #__PURE__ */ factory(name, dependencies, provided => {
+ const and = createAnd(provided)
function andTransform (args, math, scope) {
const condition1 = args[0].compile().evaluate(scope)
diff --git a/src/expression/transform/bitAnd.transform.js b/src/expression/transform/bitAnd.transform.js
index ff9e709261..b3b80d8627 100644
--- a/src/expression/transform/bitAnd.transform.js
+++ b/src/expression/transform/bitAnd.transform.js
@@ -3,10 +3,10 @@ import { factory } from '../../utils/factory.js'
import { isCollection } from '../../utils/is.js'
const name = 'bitAnd'
-const dependencies = ['typed', 'matrix', 'zeros', 'add', 'equalScalar', 'not', 'concat']
+const dependencies = ['typed', 'DenseMatrix', 'equalScalar']
-export const createBitAndTransform = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, zeros, not, concat }) => {
- const bitAnd = createBitAnd({ typed, matrix, equalScalar, zeros, not, concat })
+export const createBitAndTransform = /* #__PURE__ */ factory(name, dependencies, provided => {
+ const bitAnd = createBitAnd(provided)
function bitAndTransform (args, math, scope) {
const condition1 = args[0].compile().evaluate(scope)
diff --git a/src/expression/transform/bitOr.transform.js b/src/expression/transform/bitOr.transform.js
index ae04f7de04..d337422cc7 100644
--- a/src/expression/transform/bitOr.transform.js
+++ b/src/expression/transform/bitOr.transform.js
@@ -3,10 +3,10 @@ import { factory } from '../../utils/factory.js'
import { isCollection } from '../../utils/is.js'
const name = 'bitOr'
-const dependencies = ['typed', 'matrix', 'equalScalar', 'DenseMatrix', 'concat']
+const dependencies = ['typed', 'equalScalar', 'DenseMatrix']
-export const createBitOrTransform = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, DenseMatrix, concat }) => {
- const bitOr = createBitOr({ typed, matrix, equalScalar, DenseMatrix, concat })
+export const createBitOrTransform = /* #__PURE__ */ factory(name, dependencies, provided => {
+ const bitOr = createBitOr(provided)
function bitOrTransform (args, math, scope) {
const condition1 = args[0].compile().evaluate(scope)
diff --git a/src/expression/transform/index.transform.js b/src/expression/transform/index.transform.js
index 40ffe3694a..dda06ca918 100644
--- a/src/expression/transform/index.transform.js
+++ b/src/expression/transform/index.transform.js
@@ -4,9 +4,9 @@ import {
import { factory } from '../../utils/factory.js'
const name = 'index'
-const dependencies = ['Index', 'getMatrixDataType']
+export const dependencies = ['Index', 'Range', 'number', 'getMatrixDataType']
-export const createIndexTransform = /* #__PURE__ */ factory(name, dependencies, ({ Index, getMatrixDataType }) => {
+export const createIndexTransform = /* #__PURE__ */ factory(name, dependencies, ({ Index, Range, number, getMatrixDataType }) => {
/**
* Attach a transform function to math.index
* Adds a property transform containing the transform function.
@@ -20,8 +20,11 @@ export const createIndexTransform = /* #__PURE__ */ factory(name, dependencies,
// change from one-based to zero based, convert BigNumber to number and leave Array of Booleans as is
if (isRange(arg)) {
- arg.start--
- arg.end -= (arg.step > 0 ? 0 : 2)
+ arg = new Range({
+ start: number(arg.start) - 1,
+ length: arg.length,
+ step: number(arg.step)
+ })
} else if (arg && arg.isSet === true) {
arg = arg.map(function (v) { return v - 1 })
} else if (isArray(arg) || isMatrix(arg)) {
@@ -33,7 +36,7 @@ export const createIndexTransform = /* #__PURE__ */ factory(name, dependencies,
} else if (isBigNumber(arg)) {
arg = arg.toNumber() - 1
} else if (typeof arg === 'string') {
- // leave as is
+ // leave as is, will be interpreted later
} else {
throw new TypeError('Dimension must be an Array, Matrix, number, bigint, string, or Range')
}
@@ -43,6 +46,8 @@ export const createIndexTransform = /* #__PURE__ */ factory(name, dependencies,
const res = new Index()
Index.apply(res, args)
+ res.includeEnd = true
+ res.shiftPosition = 1
return res
}
}, { isTransformFunction: true })
diff --git a/src/expression/transform/nullish.transform.js b/src/expression/transform/nullish.transform.js
index 7c6e733be6..26e7823726 100644
--- a/src/expression/transform/nullish.transform.js
+++ b/src/expression/transform/nullish.transform.js
@@ -3,10 +3,10 @@ import { factory } from '../../utils/factory.js'
import { isCollection } from '../../utils/is.js'
const name = 'nullish'
-const dependencies = ['typed', 'matrix', 'size', 'flatten', 'deepEqual']
+const dependencies = ['typed', 'DenseMatrix', 'size', 'flatten', 'deepEqual']
-export const createNullishTransform = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, size, flatten, deepEqual }) => {
- const nullish = createNullish({ typed, matrix, size, flatten, deepEqual })
+export const createNullishTransform = /* #__PURE__ */ factory(name, dependencies, provided => {
+ const nullish = createNullish(provided)
function nullishTransform (args, math, scope) {
const left = args[0].compile().evaluate(scope)
diff --git a/src/expression/transform/range.transform.js b/src/expression/transform/range.transform.js
index 67211d4b94..1b6d876a2e 100644
--- a/src/expression/transform/range.transform.js
+++ b/src/expression/transform/range.transform.js
@@ -1,11 +1,10 @@
import { factory } from '../../utils/factory.js'
-import { createRange } from '../../function/matrix/range.js'
+import { createRange, dependencies } from '../../function/matrix/range.js'
const name = 'range'
-const dependencies = ['typed', 'config', '?matrix', '?bignumber', 'equal', 'smaller', 'smallerEq', 'larger', 'largerEq', 'add', 'isZero', 'isPositive']
-export const createRangeTransform = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, matrix, bignumber, equal, smaller, smallerEq, larger, largerEq, add, isZero, isPositive }) => {
- const range = createRange({ typed, config, matrix, bignumber, equal, smaller, smallerEq, larger, largerEq, add, isZero, isPositive })
+export const createRangeTransform = /* #__PURE__ */ factory(name, dependencies, provided => {
+ const range = createRange(provided)
/**
* Attach a transform function to math.range
@@ -13,15 +12,22 @@ export const createRangeTransform = /* #__PURE__ */ factory(name, dependencies,
*
* This transform creates a range which includes the end value
*/
- return typed('range', {
+ return provided.typed('range', {
'...any': function (args) {
const lastIndex = args.length - 1
+ if (lastIndex < 0) {
+ throw new SyntaxError('range() requires at least one argument')
+ }
const last = args[lastIndex]
if (typeof last !== 'boolean') {
// append a parameter includeEnd=true
args.push(true)
}
-
+ const first = args[0]
+ if (typeof first === 'string' && first.charAt(0) === ':') {
+ // default start in expressions is 1
+ args[0] = '1' + first
+ }
return range.apply(null, args)
}
})
diff --git a/src/expression/transform/subset.transform.js b/src/expression/transform/subset.transform.js
index 50e1f41755..6b6393a6b9 100644
--- a/src/expression/transform/subset.transform.js
+++ b/src/expression/transform/subset.transform.js
@@ -1,26 +1,44 @@
import { factory } from '../../utils/factory.js'
import { errorTransform } from './utils/errorTransform.js'
-import { createSubset } from '../../function/matrix/subset.js'
+import {
+ createSubset, dependencies as subsetDependencies
+} from '../../function/matrix/subset.js'
+import {
+ createIndexTransform, dependencies as indexTransformDependencies
+} from './index.transform.js'
const name = 'subset'
-const dependencies = ['typed', 'matrix', 'zeros', 'add']
+const dependencies = subsetDependencies.slice()
+for (const indexDep of indexTransformDependencies) {
+ if (!dependencies.includes(indexDep)) dependencies.push(indexDep)
+}
-export const createSubsetTransform = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, zeros, add }) => {
- const subset = createSubset({ typed, matrix, zeros, add })
+export const createSubsetTransform = /* #__PURE__ */ factory(
+ name,
+ dependencies,
+ provided => {
+ const subset = createSubset(provided)
+ const indexTransform = createIndexTransform(provided)
- /**
- * Attach a transform function to math.subset
- * Adds a property transform containing the transform function.
- *
- * This transform creates a range which includes the end value
- */
- return typed('subset', {
- '...any': function (args) {
- try {
- return subset.apply(null, args)
- } catch (err) {
- throw errorTransform(err)
+ /**
+ * Attach a transform function to math.subset
+ * Adds a property transform containing the transform function.
+ *
+ * This transform creates a range which includes the end value
+ */
+ return provided.typed('subset', {
+ '...any': function (args) {
+ try {
+ if (args[1] && Array.isArray(args[1])) {
+ // supplied an array instead of an index for the 2nd argument, so
+ // have to turn that into an Index with indexTransform rather than
+ // just plain index, as subset will if we just let it have at it.
+ args[1] = indexTransform.apply(null, args[1])
+ }
+ return subset.apply(null, args)
+ } catch (err) {
+ throw errorTransform(err)
+ }
}
- }
- })
-}, { isTransformFunction: true })
+ })
+ }, { isTransformFunction: true })
diff --git a/src/factoriesAny.js b/src/factoriesAny.js
index 15ea5de0d0..921412b30d 100644
--- a/src/factoriesAny.js
+++ b/src/factoriesAny.js
@@ -114,6 +114,7 @@ export { createToBest } from './function/unit/toBest.js'
export { createIsPrime } from './function/utils/isPrime.js'
export { createNumeric } from './function/utils/numeric.js'
export { createDivideScalar } from './function/arithmetic/divideScalar.js'
+export { createOneUnitless, createOne } from './function/arithmetic/one.js'
export { createPow } from './function/arithmetic/pow.js'
export { createRound } from './function/arithmetic/round.js'
export { createLog } from './function/arithmetic/log.js'
@@ -121,6 +122,8 @@ export { createLog1p } from './function/arithmetic/log1p.js'
export { createNthRoots } from './function/arithmetic/nthRoots.js'
export { createDotPow } from './function/arithmetic/dotPow.js'
export { createDotDivide } from './function/arithmetic/dotDivide.js'
+export { createScalarDivide } from './function/arithmetic/scalarDivide.js'
+export { createZero } from './function/arithmetic/zero.js'
export { createLsolve } from './function/algebra/solver/lsolve.js'
export { createUsolve } from './function/algebra/solver/usolve.js'
export { createLsolveAll } from './function/algebra/solver/lsolveAll.js'
diff --git a/src/factoriesNumber.js b/src/factoriesNumber.js
index 73a0eaafb5..4ead512191 100644
--- a/src/factoriesNumber.js
+++ b/src/factoriesNumber.js
@@ -83,6 +83,7 @@ export { createTyped } from './core/function/typed.js'
// classes
export { createResultSet } from './type/resultset/ResultSet.js'
export { createRangeClass } from './type/matrix/Range.js'
+export { createMatrixClass } from './type/matrix/Matrix.js'
export { createHelpClass } from './expression/Help.js'
export { createChainClass } from './type/chain/Chain.js'
export { createHelp } from './expression/function/help.js'
@@ -105,6 +106,7 @@ export const createSubtractScalar = /* #__PURE__ */ createNumberFactory('subtrac
export const createCbrt = /* #__PURE__ */ createNumberFactory('cbrt', cbrtNumber)
export { createCeilNumber as createCeil } from './function/arithmetic/ceil.js'
export const createCube = /* #__PURE__ */ createNumberFactory('cube', cubeNumber)
+export { createDotMultiplyNumber } from './function/arithmetic/dotMultiply.js'
export const createExp = /* #__PURE__ */ createNumberFactory('exp', expNumber)
export const createExpm1 = /* #__PURE__ */ createNumberFactory('expm1', expm1Number)
export { createFixNumber as createFix } from './function/arithmetic/fix.js'
@@ -115,7 +117,7 @@ export const createLog10 = /* #__PURE__ */ createNumberFactory('log10', log10Num
export const createLog2 = /* #__PURE__ */ createNumberFactory('log2', log2Number)
export const createMod = /* #__PURE__ */ createNumberFactory('mod', modNumber)
export const createMultiplyScalar = /* #__PURE__ */ createNumberFactory('multiplyScalar', multiplyNumber)
-export const createMultiply = /* #__PURE__ */ createNumberFactory('multiply', multiplyNumber)
+export { createMultiplyNumber } from './function/arithmetic/multiply.js'
export const createNthRoot = /* #__PURE__ */
createNumberOptionalSecondArgFactory('nthRoot', nthRootNumber)
export const createSign = /* #__PURE__ */ createNumberFactory('sign', signNumber)
@@ -125,8 +127,10 @@ export const createSubtract = /* #__PURE__ */ createNumberFactory('subtract', su
export const createXgcd = /* #__PURE__ */ createNumberFactory('xgcd', xgcdNumber)
export const createDivideScalar = /* #__PURE__ */ createNumberFactory('divideScalar', divideNumber)
export const createPow = /* #__PURE__ */ createNumberFactory('pow', powNumber)
+export { createOneNumber } from './function/arithmetic/one.js'
export const createRound = /* #__PURE__ */
createNumberOptionalSecondArgFactory('round', roundNumber)
+export { createScalarDivide } from './function/arithmetic/scalarDivide.js'
export const createLog = /* #__PURE__ */
createNumberOptionalSecondArgFactory('log', logNumber)
export const createLog1p = /* #__PURE__ */ createNumberFactory('log1p', log1pNumber)
@@ -134,6 +138,7 @@ export const createAdd = /* #__PURE__ */ createNumberFactory('add', addNumber)
export { createHypot } from './function/arithmetic/hypot.js'
export const createNorm = /* #__PURE__ */ createNumberFactory('norm', normNumber)
export const createDivide = /* #__PURE__ */ createNumberFactory('divide', divideNumber)
+export { createZeroNumber } from './function/arithmetic/zero.js'
// bitwise
export const createBitAnd = /* #__PURE__ */ createNumberFactory('bitAnd', bitAndNumber)
@@ -209,6 +214,7 @@ export const createOr = /* #__PURE__ */ createNumberFactory('or', orNumber)
export const createXor = /* #__PURE__ */ createNumberFactory('xor', xorNumber)
// matrix
+export { createGetMatrixDataType } from './function/matrix/getMatrixDataType.js'
export { createMapSlices } from './function/matrix/mapSlices.js'
export { createFilter } from './function/matrix/filter.js'
export { createForEach } from './function/matrix/forEach.js'
@@ -219,6 +225,8 @@ export { createSize } from './function/matrix/size.js'
export const createIndex = /* #__PURE__ */ factory('index', [], () => noIndex)
export const createMatrix = /* #__PURE__ */ factory('matrix', [], () => noMatrix) // FIXME: needed now because subset transform needs it. Remove the need for it in subset
export const createSubset = /* #__PURE__ */ factory('subset', [], () => noSubset)
+export { createSqueeze } from './function/matrix/squeeze.js'
+
// TODO: provide number+array implementations for map, filter, forEach, zeros, ...?
// TODO: create range implementation for range?
export { createPartitionSelect } from './function/matrix/partitionSelect.js'
diff --git a/src/function/arithmetic/add.js b/src/function/arithmetic/add.js
index b9bc0ae9af..0402aebe64 100644
--- a/src/function/arithmetic/add.js
+++ b/src/function/arithmetic/add.js
@@ -7,22 +7,20 @@ import { createMatrixAlgorithmSuite } from '../../type/matrix/utils/matrixAlgori
const name = 'add'
const dependencies = [
'typed',
- 'matrix',
'addScalar',
'equalScalar',
'DenseMatrix',
- 'SparseMatrix',
- 'concat'
+ 'SparseMatrix'
]
export const createAdd = /* #__PURE__ */ factory(
name,
dependencies,
- ({ typed, matrix, addScalar, equalScalar, DenseMatrix, SparseMatrix, concat }) => {
+ ({ typed, addScalar, equalScalar, DenseMatrix, SparseMatrix, math, concat }) => {
const matAlgo01xDSid = createMatAlgo01xDSid({ typed })
const matAlgo04xSidSid = createMatAlgo04xSidSid({ typed, equalScalar })
const matAlgo10xSids = createMatAlgo10xSids({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Add two or more values, `x + y`.
* For matrices, the function is evaluated element wise.
@@ -70,7 +68,29 @@ export const createAdd = /* #__PURE__ */ factory(
}
return result
- })
+ }),
+ 'Range, Range': typed.referToSelf(self => (r, p) => {
+ if (r.for !== p.for) throw new Error('Range length mismatch')
+ return r.createRange({
+ start: self(r.start, p.start),
+ length: r.length,
+ step: self(r.step, p.step)
+ })
+ }),
+ 'Range, Matrix': typed.referToSelf(
+ self => (r, m) => self(r.valueOf(), m)),
+ 'Range, any': typed.referToSelf(self => (r, s) => r.createRange({
+ start: self(r.start, s),
+ length: r.length,
+ step: r.step
+ })),
+ 'Matrix, Range': typed.referToSelf(
+ self => (m, r) => self(m, r.valueOf())),
+ 'any, Range': typed.referToSelf(self => (s, r) => r.createRange({
+ start: self(s, r.start),
+ length: r.length,
+ step: r.step
+ }))
},
matrixAlgorithmSuite({
elop: addScalar,
diff --git a/src/function/arithmetic/ceil.js b/src/function/arithmetic/ceil.js
index a3f40bbbed..d1480769c3 100644
--- a/src/function/arithmetic/ceil.js
+++ b/src/function/arithmetic/ceil.js
@@ -8,7 +8,9 @@ import { createMatAlgo12xSfs } from '../../type/matrix/utils/matAlgo12xSfs.js'
import { createMatAlgo14xDs } from '../../type/matrix/utils/matAlgo14xDs.js'
const name = 'ceil'
-const dependencies = ['typed', 'config', 'round', 'matrix', 'equalScalar', 'zeros', 'DenseMatrix']
+const dependencies = [
+ 'typed', 'config', 'round', 'equalScalar', 'isZero', 'DenseMatrix', 'sparse'
+]
const bigTen = new Decimal(10)
@@ -46,7 +48,9 @@ export const createCeilNumber = /* #__PURE__ */ factory(
}
)
-export const createCeil = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, round, matrix, equalScalar, zeros, DenseMatrix }) => {
+export const createCeil = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, config, round, equalScalar, isZero, DenseMatrix, sparse
+}) => {
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
@@ -181,16 +185,18 @@ export const createCeil = /* #__PURE__ */ factory(name, dependencies, ({ typed,
'number | Complex | Fraction | BigNumber, Array':
typed.referToSelf(self => (x, y) => {
// use matrix implementation
- return matAlgo14xDs(matrix(y), x, self, true).valueOf()
+ return matAlgo14xDs(new DenseMatrix(y), x, self, true).valueOf()
}),
- 'number | Complex | Fraction | BigNumber, Matrix':
- typed.referToSelf(self => (x, y) => {
- if (equalScalar(x, 0)) return zeros(y.size(), y.storage())
- if (y.storage() === 'dense') {
- return matAlgo14xDs(y, x, self, true)
- }
- return matAlgo12xSfs(y, x, self, true)
- })
+ 'number | Complex | BigNumber | Fraction, SparseMatrix': typed.referToSelf(
+ self => (x, n) => {
+ const dense = matAlgo12xSfs(n, x, self, true)
+ if (isZero(x)) return sparse(dense)
+ return dense
+ }
+ ),
+
+ 'number | Complex | BigNumber | Fraction, DenseMatrix': typed.referToSelf(
+ self => (x, n) => matAlgo14xDs(n, x, self, true))
})
})
diff --git a/src/function/arithmetic/divide.js b/src/function/arithmetic/divide.js
index 84840b7f0f..db8ebe9308 100644
--- a/src/function/arithmetic/divide.js
+++ b/src/function/arithmetic/divide.js
@@ -6,14 +6,14 @@ import { createMatAlgo14xDs } from '../../type/matrix/utils/matAlgo14xDs.js'
const name = 'divide'
const dependencies = [
'typed',
- 'matrix',
+ 'DenseMatrix',
'multiply',
'equalScalar',
'divideScalar',
'inv'
]
-export const createDivide = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, multiply, equalScalar, divideScalar, inv }) => {
+export const createDivide = /* #__PURE__ */ factory(name, dependencies, ({ typed, DenseMatrix, multiply, equalScalar, divideScalar, inv }) => {
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
@@ -69,9 +69,15 @@ export const createDivide = /* #__PURE__ */ factory(name, dependencies, ({ typed
'Array, any': function (x, y) {
// use matrix implementation
- return matAlgo14xDs(matrix(x), y, divideScalar, false).valueOf()
+ return matAlgo14xDs(new DenseMatrix(x), y, divideScalar, false).valueOf()
},
+ 'Range, any': typed.referToSelf(self => (r, s) => r.createRange({
+ start: self(r.start, s),
+ length: r.length,
+ step: self(r.step, s)
+ })),
+
'any, Array | Matrix': function (x, y) {
return multiply(x, inv(y))
}
diff --git a/src/function/arithmetic/dotDivide.js b/src/function/arithmetic/dotDivide.js
index 3587faf762..db68306fa8 100644
--- a/src/function/arithmetic/dotDivide.js
+++ b/src/function/arithmetic/dotDivide.js
@@ -9,11 +9,9 @@ import { createMatrixAlgorithmSuite } from '../../type/matrix/utils/matrixAlgori
const name = 'dotDivide'
const dependencies = [
'typed',
- 'matrix',
'equalScalar',
'divideScalar',
'DenseMatrix',
- 'concat',
'SparseMatrix'
]
@@ -23,7 +21,7 @@ export const createDotDivide = /* #__PURE__ */ factory(name, dependencies, ({ ty
const matAlgo07xSSf = createMatAlgo07xSSf({ typed, SparseMatrix })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Divide two matrices element wise. The function accepts both matrices and
diff --git a/src/function/arithmetic/dotMultiply.js b/src/function/arithmetic/dotMultiply.js
index d49287bc6b..6cb87cff28 100644
--- a/src/function/arithmetic/dotMultiply.js
+++ b/src/function/arithmetic/dotMultiply.js
@@ -1,4 +1,6 @@
import { factory } from '../../utils/factory.js'
+import { isArray } from '../../utils/is.js'
+import { deepMultiply } from '../../plain/number/arithmetic.js'
import { createMatAlgo02xDS0 } from '../../type/matrix/utils/matAlgo02xDS0.js'
import { createMatAlgo09xS0Sf } from '../../type/matrix/utils/matAlgo09xS0Sf.js'
import { createMatAlgo11xS0s } from '../../type/matrix/utils/matAlgo11xS0s.js'
@@ -7,17 +9,16 @@ import { createMatrixAlgorithmSuite } from '../../type/matrix/utils/matrixAlgori
const name = 'dotMultiply'
const dependencies = [
'typed',
- 'matrix',
+ 'DenseMatrix',
'equalScalar',
- 'multiplyScalar',
- 'concat'
+ 'multiplyScalar'
]
-export const createDotMultiply = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, multiplyScalar, concat }) => {
+export const createDotMultiply = /* #__PURE__ */ factory(name, dependencies, ({ typed, DenseMatrix, equalScalar, multiplyScalar }) => {
const matAlgo02xDS0 = createMatAlgo02xDS0({ typed, equalScalar })
const matAlgo09xS0Sf = createMatAlgo09xS0Sf({ typed, equalScalar })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Multiply two matrices element wise. The function accepts both matrices and
@@ -52,3 +53,30 @@ export const createDotMultiply = /* #__PURE__ */ factory(name, dependencies, ({
Ss: matAlgo11xS0s
}))
})
+
+export const createDotMultiplyNumber = /* #__PURE__ */ factory(
+ name, ['typed'], ({ typed }) => {
+ return typed(name, {
+ 'number, number': (m, n) => m * n,
+ 'number, Array': deepMultiply,
+ 'Array, number': (A, n) => deepMultiply(n, A),
+ 'Array, Array': _dotMult
+ })
+ }
+)
+
+/* Multiply corresponding entries of A and B */
+function _dotMult (A, B) {
+ if (A.length !== B.length) {
+ throw new Error('Cannot dot-multiply arrays of differing length.')
+ }
+ return A.map((a, ix) => {
+ const b = B[ix]
+ if (isArray(a)) {
+ if (isArray(b)) return _dotMult(a, b)
+ } else {
+ if (!isArray(b)) return a * b
+ }
+ throw new Error('Cannot dot-multiply arrays of different shape.')
+ })
+}
diff --git a/src/function/arithmetic/dotPow.js b/src/function/arithmetic/dotPow.js
index a1c25f1522..b4188ed436 100644
--- a/src/function/arithmetic/dotPow.js
+++ b/src/function/arithmetic/dotPow.js
@@ -9,19 +9,17 @@ const name = 'dotPow'
const dependencies = [
'typed',
'equalScalar',
- 'matrix',
'pow',
'DenseMatrix',
- 'concat',
'SparseMatrix'
]
-export const createDotPow = /* #__PURE__ */ factory(name, dependencies, ({ typed, equalScalar, matrix, pow, DenseMatrix, concat, SparseMatrix }) => {
+export const createDotPow = /* #__PURE__ */ factory(name, dependencies, ({ typed, equalScalar, pow, DenseMatrix, SparseMatrix }) => {
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo07xSSf = createMatAlgo07xSSf({ typed, SparseMatrix })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
const powScalarSignatures = {}
for (const signature in pow.signatures) {
diff --git a/src/function/arithmetic/fix.js b/src/function/arithmetic/fix.js
index 0cc47ffc0f..8dc6dce1f6 100644
--- a/src/function/arithmetic/fix.js
+++ b/src/function/arithmetic/fix.js
@@ -4,7 +4,9 @@ import { createMatAlgo12xSfs } from '../../type/matrix/utils/matAlgo12xSfs.js'
import { createMatAlgo14xDs } from '../../type/matrix/utils/matAlgo14xDs.js'
const name = 'fix'
-const dependencies = ['typed', 'Complex', 'matrix', 'ceil', 'floor', 'equalScalar', 'zeros', 'DenseMatrix']
+const dependencies = [
+ 'typed', 'Complex', 'ceil', 'floor', 'isZero', 'DenseMatrix', 'sparse'
+]
export const createFixNumber = /* #__PURE__ */ factory(
name, ['typed', 'ceil', 'floor'], ({ typed, ceil, floor }) => {
@@ -20,7 +22,9 @@ export const createFixNumber = /* #__PURE__ */ factory(
}
)
-export const createFix = /* #__PURE__ */ factory(name, dependencies, ({ typed, Complex, matrix, ceil, floor, equalScalar, zeros, DenseMatrix }) => {
+export const createFix = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, Complex, matrix, ceil, floor, isZero, DenseMatrix, sparse
+}) => {
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
@@ -143,16 +147,18 @@ export const createFix = /* #__PURE__ */ factory(name, dependencies, ({ typed, C
'number | Complex | Fraction | BigNumber, Array':
typed.referToSelf(self => (x, y) => {
// use matrix implementation
- return matAlgo14xDs(matrix(y), x, self, true).valueOf()
+ return matAlgo14xDs(new DenseMatrix(y), x, self, true).valueOf()
}),
- 'number | Complex | Fraction | BigNumber, Matrix':
- typed.referToSelf(self => (x, y) => {
- if (equalScalar(x, 0)) return zeros(y.size(), y.storage())
- if (y.storage() === 'dense') {
- return matAlgo14xDs(y, x, self, true)
- }
- return matAlgo12xSfs(y, x, self, true)
- })
+ 'number | Complex | BigNumber | Fraction, SparseMatrix': typed.referToSelf(
+ self => (x, n) => {
+ const dense = matAlgo12xSfs(n, x, self, true)
+ if (isZero(x)) return sparse(dense)
+ return dense
+ }
+ ),
+
+ 'number | Complex | BigNumber | Fraction, DenseMatrix': typed.referToSelf(
+ self => (x, n) => matAlgo14xDs(n, x, self, true))
})
})
diff --git a/src/function/arithmetic/floor.js b/src/function/arithmetic/floor.js
index 752d06864d..172ec9bed3 100644
--- a/src/function/arithmetic/floor.js
+++ b/src/function/arithmetic/floor.js
@@ -8,7 +8,9 @@ import { createMatAlgo12xSfs } from '../../type/matrix/utils/matAlgo12xSfs.js'
import { createMatAlgo14xDs } from '../../type/matrix/utils/matAlgo14xDs.js'
const name = 'floor'
-const dependencies = ['typed', 'config', 'round', 'matrix', 'equalScalar', 'zeros', 'DenseMatrix']
+const dependencies = [
+ 'typed', 'config', 'round', 'equalScalar', 'isZero', 'DenseMatrix', 'sparse'
+]
const bigTen = new Decimal(10)
@@ -53,7 +55,9 @@ export const createFloorNumber = /* #__PURE__ */ factory(
}
)
-export const createFloor = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, round, matrix, equalScalar, zeros, DenseMatrix }) => {
+export const createFloor = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, config, round, equalScalar, isZero, DenseMatrix, sparse
+}) => {
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
@@ -191,16 +195,18 @@ export const createFloor = /* #__PURE__ */ factory(name, dependencies, ({ typed,
'number | Complex | Fraction | BigNumber, Array':
typed.referToSelf(self => (x, y) => {
// use matrix implementation
- return matAlgo14xDs(matrix(y), x, self, true).valueOf()
+ return matAlgo14xDs(new DenseMatrix(y), x, self, true).valueOf()
}),
- 'number | Complex | Fraction | BigNumber, Matrix':
- typed.referToSelf(self => (x, y) => {
- if (equalScalar(x, 0)) return zeros(y.size(), y.storage())
- if (y.storage() === 'dense') {
- return matAlgo14xDs(y, x, self, true)
- }
- return matAlgo12xSfs(y, x, self, true)
- })
+ 'number | Complex | BigNumber | Fraction, SparseMatrix': typed.referToSelf(
+ self => (x, n) => {
+ const dense = matAlgo12xSfs(n, x, self, true)
+ if (isZero(x)) return sparse(dense)
+ return dense
+ }
+ ),
+
+ 'number | Complex | BigNumber | Fraction, DenseMatrix': typed.referToSelf(
+ self => (x, n) => matAlgo14xDs(n, x, self, true))
})
})
diff --git a/src/function/arithmetic/gcd.js b/src/function/arithmetic/gcd.js
index d370c498d6..e0f94c0f3f 100644
--- a/src/function/arithmetic/gcd.js
+++ b/src/function/arithmetic/gcd.js
@@ -12,12 +12,11 @@ const dependencies = [
'typed',
'config',
'round',
- 'matrix',
'equalScalar',
- 'zeros',
+ 'isZero',
'BigNumber',
'DenseMatrix',
- 'concat'
+ 'sparse'
]
const gcdTypes = 'number | BigNumber | Fraction | Matrix | Array'
@@ -27,12 +26,16 @@ function is1d (array) {
return !array.some(element => Array.isArray(element))
}
-export const createGcd = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, config, round, equalScalar, zeros, BigNumber, DenseMatrix, concat }) => {
- const mod = createMod({ typed, config, round, matrix, equalScalar, zeros, DenseMatrix, concat })
+export const createGcd = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, config, round, equalScalar, isZero, BigNumber, DenseMatrix, sparse
+}) => {
+ const mod = createMod({
+ typed, config, round, equalScalar, isZero, DenseMatrix, sparse
+ })
const matAlgo01xDSid = createMatAlgo01xDSid({ typed })
const matAlgo04xSidSid = createMatAlgo04xSidSid({ typed, equalScalar })
const matAlgo10xSids = createMatAlgo10xSids({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Calculate the greatest common divisor for two or more values or arrays.
diff --git a/src/function/arithmetic/lcm.js b/src/function/arithmetic/lcm.js
index d53dd3e799..0e66503180 100644
--- a/src/function/arithmetic/lcm.js
+++ b/src/function/arithmetic/lcm.js
@@ -8,16 +8,15 @@ import { lcmNumber } from '../../plain/number/index.js'
const name = 'lcm'
const dependencies = [
'typed',
- 'matrix',
- 'equalScalar',
- 'concat'
+ 'DenseMatrix',
+ 'equalScalar'
]
-export const createLcm = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, concat }) => {
+export const createLcm = /* #__PURE__ */ factory(name, dependencies, ({ typed, DenseMatrix, equalScalar, concat }) => {
const matAlgo02xDS0 = createMatAlgo02xDS0({ typed, equalScalar })
const matAlgo06xS0S0 = createMatAlgo06xS0S0({ typed, equalScalar })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
const lcmTypes = 'number | BigNumber | Fraction | Matrix | Array'
const lcmManySignature = {}
diff --git a/src/function/arithmetic/mod.js b/src/function/arithmetic/mod.js
index dee0b97e4a..1192a53049 100644
--- a/src/function/arithmetic/mod.js
+++ b/src/function/arithmetic/mod.js
@@ -12,21 +12,24 @@ const dependencies = [
'typed',
'config',
'round',
- 'matrix',
'equalScalar',
- 'zeros',
+ 'isZero',
'DenseMatrix',
- 'concat'
+ 'sparse'
]
-export const createMod = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, round, matrix, equalScalar, zeros, DenseMatrix, concat }) => {
- const floor = createFloor({ typed, config, round, matrix, equalScalar, zeros, DenseMatrix })
+export const createMod = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, config, round, equalScalar, isZero, DenseMatrix, sparse
+}) => {
+ const floor = createFloor({
+ typed, config, round, equalScalar, isZero, DenseMatrix, sparse
+ })
const matAlgo02xDS0 = createMatAlgo02xDS0({ typed, equalScalar })
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo05xSfSf = createMatAlgo05xSfSf({ typed, equalScalar })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Calculates the modulus, the remainder of an integer division.
diff --git a/src/function/arithmetic/multiply.js b/src/function/arithmetic/multiply.js
index 07e1553b83..e8c8845c9c 100644
--- a/src/function/arithmetic/multiply.js
+++ b/src/function/arithmetic/multiply.js
@@ -1,20 +1,21 @@
import { factory } from '../../utils/factory.js'
import { isMatrix } from '../../utils/is.js'
import { arraySize } from '../../utils/array.js'
+import { deepMultiply } from '../../plain/number/arithmetic.js'
import { createMatAlgo11xS0s } from '../../type/matrix/utils/matAlgo11xS0s.js'
import { createMatAlgo14xDs } from '../../type/matrix/utils/matAlgo14xDs.js'
const name = 'multiply'
const dependencies = [
'typed',
- 'matrix',
+ 'DenseMatrix',
'addScalar',
'multiplyScalar',
'equalScalar',
'dot'
]
-export const createMultiply = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, addScalar, multiplyScalar, equalScalar, dot }) => {
+export const createMultiply = /* #__PURE__ */ factory(name, dependencies, ({ typed, DenseMatrix, addScalar, multiplyScalar, equalScalar, dot }) => {
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
@@ -801,7 +802,7 @@ export const createMultiply = /* #__PURE__ */ factory(name, dependencies, ({ typ
_validateMatrixDimensions(arraySize(x), arraySize(y))
// use dense matrix implementation
- const m = selfMM(matrix(x), matrix(y))
+ const m = selfMM(new DenseMatrix(x), new DenseMatrix(y))
// return array or scalar
return isMatrix(m) ? m.valueOf() : m
}),
@@ -834,11 +835,11 @@ export const createMultiply = /* #__PURE__ */ factory(name, dependencies, ({ typ
},
'Matrix, Array': typed.referTo('Matrix,Matrix', selfMM =>
- (x, y) => selfMM(x, matrix(y))),
+ (x, y) => selfMM(x, x.create(y))),
'Array, Matrix': typed.referToSelf(self => (x, y) => {
// use Matrix * Matrix implementation
- return self(matrix(x, y.storage()), y)
+ return self(y.create(x), y)
}),
'SparseMatrix, any': function (x, y) {
@@ -859,14 +860,26 @@ export const createMultiply = /* #__PURE__ */ factory(name, dependencies, ({ typ
'Array, any': function (x, y) {
// use matrix implementation
- return matAlgo14xDs(matrix(x), y, multiplyScalar, false).valueOf()
+ return matAlgo14xDs(new DenseMatrix(x), y, multiplyScalar, false).valueOf()
},
'any, Array': function (x, y) {
// use matrix implementation
- return matAlgo14xDs(matrix(y), x, multiplyScalar, true).valueOf()
+ return matAlgo14xDs(new DenseMatrix(y), x, multiplyScalar, true).valueOf()
},
+ 'Range, any': typed.referToSelf(self => (r, s) => r.createRange({
+ start: self(r.start, s),
+ length: r.length,
+ step: self(r.step, s)
+ })),
+
+ 'any, Range': typed.referToSelf(self => (s, r) => r.createRange({
+ start: self(s, r.start),
+ length: r.length,
+ step: self(s, r.step)
+ })),
+
'any, any': multiplyScalar,
'any, any, ...any': typed.referToSelf(self => (x, y, rest) => {
@@ -880,3 +893,14 @@ export const createMultiply = /* #__PURE__ */ factory(name, dependencies, ({ typ
})
})
})
+
+export const createMultiplyNumber = /* #__PURE__ */ factory(
+ name, ['typed'], ({ typed }) => {
+ return typed(name, {
+ 'number, number': (m, n) => m * n,
+ 'bigint, bigint': (m, n) => m * n,
+ 'number | bigint, Array': deepMultiply,
+ 'Array, number | bigint': (A, n) => deepMultiply(n, A)
+ })
+ }
+)
diff --git a/src/function/arithmetic/nthRoot.js b/src/function/arithmetic/nthRoot.js
index abcbaeaf3f..8ec27ce3e0 100644
--- a/src/function/arithmetic/nthRoot.js
+++ b/src/function/arithmetic/nthRoot.js
@@ -9,18 +9,17 @@ import { nthRootNumber } from '../../plain/number/index.js'
const name = 'nthRoot'
const dependencies = [
'typed',
- 'matrix',
+ 'DenseMatrix',
'equalScalar',
- 'BigNumber',
- 'concat'
+ 'BigNumber'
]
-export const createNthRoot = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, BigNumber, concat }) => {
+export const createNthRoot = /* #__PURE__ */ factory(name, dependencies, ({ typed, DenseMatrix, equalScalar, BigNumber }) => {
const matAlgo01xDSid = createMatAlgo01xDSid({ typed })
const matAlgo02xDS0 = createMatAlgo02xDS0({ typed, equalScalar })
const matAlgo06xS0S0 = createMatAlgo06xS0S0({ typed, equalScalar })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Calculate the nth root of a value.
@@ -69,7 +68,7 @@ export const createNthRoot = /* #__PURE__ */ factory(name, dependencies, ({ type
'Complex, number': complexErr,
Array: typed.referTo('DenseMatrix,number', selfDn =>
- x => selfDn(matrix(x), 2).valueOf()),
+ x => selfDn(new DenseMatrix(x), 2).valueOf()),
DenseMatrix: typed.referTo('DenseMatrix,number', selfDn =>
x => selfDn(x, 2)),
SparseMatrix: typed.referTo('SparseMatrix,number', selfSn =>
@@ -98,7 +97,7 @@ export const createNthRoot = /* #__PURE__ */ factory(name, dependencies, ({ type
}),
'Array, SparseMatrix': typed.referTo('DenseMatrix,SparseMatrix', selfDS =>
- (x, y) => selfDS(matrix(x), y)),
+ (x, y) => selfDS(new DenseMatrix(x), y)),
'number | BigNumber, SparseMatrix': typed.referToSelf(self => (x, y) => {
// density must be one (no zeros in matrix)
diff --git a/src/function/arithmetic/one.js b/src/function/arithmetic/one.js
new file mode 100644
index 0000000000..0ee073d956
--- /dev/null
+++ b/src/function/arithmetic/one.js
@@ -0,0 +1,75 @@
+import { factory } from '../../utils/factory.js'
+import { extend } from '../../utils/object.js'
+
+const name = 'one'
+const dependencies = [
+ 'typed', '?BigNumber', '?Complex', '?Fraction', 'size', 'identity'
+]
+export const createOneNumber = /* #__PURE__ */ factory(
+ name, ['typed'], ({ typed }) => {
+ return typed(name, {
+ number: () => 1
+ })
+ })
+
+export const createOneUnitless = /* #__PURE__ */ factory(
+ 'oneUnitless', dependencies, ({
+ typed, BigNumber, Complex, Fraction, size, identity
+ }) => {
+ /**
+ * Like one() but doesn't handle units, to break circularity
+ */
+ return typed('oneUnitless', {
+ number: () => 1,
+ bigint: () => 1n,
+ BigNumber: () => new BigNumber(1),
+ Complex: () => new Complex(1),
+ Fraction: () => new Fraction(1),
+ boolean: () => true,
+ Array: A => {
+ const sz = size(A)
+ if (sz.length === 2 && sz[0] === sz[1]) {
+ return identity(sz[0]).valueOf()
+ }
+ throw new Error(`No identity of size ${sz}`)
+ },
+ Matrix: M => {
+ const sz = size(M)
+ if (sz.length === 2 && sz[0] === sz[1]) return identity(sz[0])
+ throw new Error(`No identity matrix of size ${sz}`)
+ }
+ })
+ })
+
+export const createOne = /* #__PURE__ */ factory(
+ name, ['typed', 'oneUnitless', '?unit'], ({
+ typed, oneUnitless, unit
+ }) => {
+ /**
+ * Return the multiplicative identity of the same type as the argument.
+ *
+ * Syntax:
+ *
+ * math.one(x)
+ *
+ * Examples:
+ *
+ * math.one(1.618) // returns 1
+ * math.one(math.bignumber(222)) // BigNumber 1
+ * math.one(math.fraction(1, 3)) // Fraction 1
+ * math.one(math.evaluate('0 + 2i')) // Complex 1+0i
+ * math.one([[2, 3], [4, 5]]) // [[1, 0], [0,1]]
+ *
+ * See also:
+ * typeOf, numeric, zero
+ *
+ * @param {MathType} x Any entity mathjs understands
+ * @return {MathType} Multiplicative identity of same type as x
+ */
+ return typed(name, extend({
+ Unit: u => {
+ if (u.value === undefined || u.value === null) return unit(1)
+ return oneUnitless(u.value)
+ }
+ }, oneUnitless.signatures))
+ })
diff --git a/src/function/arithmetic/pow.js b/src/function/arithmetic/pow.js
index f7dfba37d5..2ec83bd871 100644
--- a/src/function/arithmetic/pow.js
+++ b/src/function/arithmetic/pow.js
@@ -9,14 +9,13 @@ const dependencies = [
'config',
'identity',
'multiply',
- 'matrix',
'inv',
'fraction',
'number',
'Complex'
]
-export const createPow = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, identity, multiply, matrix, inv, number, fraction, Complex }) => {
+export const createPow = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, identity, multiply, inv, number, fraction, Complex }) => {
/**
* Calculates the power of x to y, `x ^ y`.
*
@@ -206,6 +205,6 @@ export const createPow = /* #__PURE__ */ factory(name, dependencies, ({ typed, c
* @private
*/
function _powMatrix (x, y) {
- return matrix(_powArray(x.valueOf(), y))
+ return x.create(_powArray(x.valueOf(), y))
}
})
diff --git a/src/function/arithmetic/round.js b/src/function/arithmetic/round.js
index 738559ce80..a3a827e94f 100644
--- a/src/function/arithmetic/round.js
+++ b/src/function/arithmetic/round.js
@@ -13,14 +13,16 @@ const name = 'round'
const dependencies = [
'typed',
'config',
- 'matrix',
'equalScalar',
- 'zeros',
+ 'isZero',
'BigNumber',
- 'DenseMatrix'
+ 'DenseMatrix',
+ 'sparse'
]
-export const createRound = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, matrix, equalScalar, zeros, BigNumber, DenseMatrix }) => {
+export const createRound = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, config, matrix, equalScalar, isZero, BigNumber, DenseMatrix, sparse
+}) => {
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
@@ -175,36 +177,29 @@ export const createRound = /* #__PURE__ */ factory(name, dependencies, ({ typed,
return matAlgo11xS0s(x, n, self, false)
}),
- 'DenseMatrix, number | BigNumber': typed.referToSelf(self => (x, n) => {
+ 'Matrix, number | BigNumber': typed.referToSelf(self => (x, n) => {
return matAlgo14xDs(x, n, self, false)
}),
'Array, number | BigNumber': typed.referToSelf(self => (x, n) => {
// use matrix implementation
- return matAlgo14xDs(matrix(x), n, self, false).valueOf()
+ return matAlgo14xDs(new DenseMatrix(x), n, self, false).valueOf()
}),
- 'number | Complex | BigNumber | Fraction, SparseMatrix': typed.referToSelf(self => (x, n) => {
- // check scalar is zero
- if (equalScalar(x, 0)) {
- // do not execute algorithm, result will be a zero matrix
- return zeros(n.size(), n.storage())
+ 'number | Complex | BigNumber | Fraction, SparseMatrix': typed.referToSelf(
+ self => (x, n) => {
+ const dense = matAlgo12xSfs(n, x, self, true)
+ if (isZero(x)) return sparse(dense)
+ return dense
}
- return matAlgo12xSfs(n, x, self, true)
- }),
+ ),
- 'number | Complex | BigNumber | Fraction, DenseMatrix': typed.referToSelf(self => (x, n) => {
- // check scalar is zero
- if (equalScalar(x, 0)) {
- // do not execute algorithm, result will be a zero matrix
- return zeros(n.size(), n.storage())
- }
- return matAlgo14xDs(n, x, self, true)
- }),
+ 'number | Complex | BigNumber | Fraction, DenseMatrix': typed.referToSelf(
+ self => (x, n) => matAlgo14xDs(n, x, self, true)),
'number | Complex | BigNumber | Fraction, Array': typed.referToSelf(self => (x, n) => {
// use matrix implementation
- return matAlgo14xDs(matrix(n), x, self, true).valueOf()
+ return matAlgo14xDs(new DenseMatrix(n), x, self, true).valueOf()
})
})
})
diff --git a/src/function/arithmetic/scalarDivide.js b/src/function/arithmetic/scalarDivide.js
new file mode 100644
index 0000000000..15ecfd62c2
--- /dev/null
+++ b/src/function/arithmetic/scalarDivide.js
@@ -0,0 +1,111 @@
+import { factory } from '../../utils/factory.js'
+import { isComplex, isMatrix, isUnit } from '../../utils/is.js'
+
+const name = 'scalarDivide'
+const dependencies = [
+ 'typed', '?Unit', 'map', 'multiply', 'equal', 'deepEqual',
+ 'isInteger', 'isNumeric', 'isZero',
+ 'abs', 'add', 'divide', '?fraction'
+]
+
+export const createScalarDivide = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, map, multiply, equal, deepEqual,
+ isInteger, isNumeric, isZero,
+ abs, add, divide, fraction
+}) => {
+ const isScalar = x => isNumeric(x) || isComplex(x) || isUnit(x)
+
+ /**
+ * Determine what scalar multiple one entity is of another, if it is.
+ * For scalar arguments, this function is essentially the same as `divide`,
+ * except that it will always look for a result `r` of a more inclusive
+ * type to solve `x = r*y` if there is no such result of the same type
+ * as `y`.
+ *
+ * For collection arguments, this function first does a scalar divide
+ * on the first nonzero entries of the collections, producing `r`,
+ * and then checks whether the first argument is `r` times the second. If
+ * so, it returns `r`, otherwise it returns undefined.
+ *
+ * Syntax:
+ *
+ * math.scalarDivide(x, y)
+ *
+ * Examples:
+ *
+ * math.scalarDivide(8, 2) // returns 4
+ * math.scalarDivide(12n, 3n) // returns 4n
+ * math.scalarDivide(11n, 3n) // Fraction 11,3
+ * math.scalarDivide(0, math.bignumber(7)) // returns 0
+ * math.scalarDivide(3, math.fraction(0)) // undefined
+ * math.scalarDivide([6, 9], [4, 6]) // returns 1.5
+ * math.scalarDivide([6, 10], [4, 6]) // undefined
+ *
+ * See also:
+ *
+ * divide, dotDivide
+ *
+ * @param {MathType} x Numerator
+ * @param {MathType} y Denominator
+ * @return {MathScalarType | undefined} scalar coefficient or undefined if not a scalar multiple
+ */
+ return typed(name, {
+ 'Array | Matrix, Array | Matrix': typed.referToSelf(self => (x, y) => {
+ if (isMatrix(x)) x = x.valueOf()
+ if (isMatrix(y)) y = y.valueOf()
+ if (x.length === 0) {
+ if (y.length === 0) return 0
+ else return undefined
+ }
+ if (y.length === 0) return undefined
+ let initialx = 0
+ let initialy = 0
+ let foundInit = false
+ map(x, y, (eltx, elty) => {
+ if (foundInit) return 0
+ if (isZero(eltx) && isZero(elty)) return 0
+ initialx = eltx
+ initialy = elty
+ foundInit = true
+ return 1
+ })
+ const initialr = self(initialx, initialy)
+ if (initialr === undefined) return undefined
+ if (deepEqual(multiply(initialr, y), x)) return initialr
+ return undefined
+ }),
+ 'Array | Matrix, any': () => undefined,
+ 'any, Array | Matrix': () => undefined,
+ 'any, any': (x, y) => {
+ if (!isScalar(x) || !isScalar(y)) return undefined
+ if (isZero(y)) {
+ if (isZero(x)) {
+ if (isUnit(x)) {
+ if (isUnit(y)) {
+ const y1 = y.clone()
+ y1.value = 1
+ return divide(x, y)
+ } else return divide(x, add(y, 1))
+ } return x
+ } else return undefined
+ }
+ if (isZero(x)) y = abs(y) // avoid `-0`
+ if (typeof y === 'bigint' && fraction) {
+ const quotient = fraction(x, y)
+ if (isInteger(quotient)) return quotient.n
+ return quotient
+ }
+ const quotient = divide(x, y)
+ if (isNumeric(quotient)) return quotient
+ if (isComplex(quotient)) {
+ if (equal(quotient.re + quotient.im, quotient.re)) return quotient.re
+ return quotient
+ }
+ if (isUnit(quotient)) {
+ if (quotient.unitless()) return quotient.value
+ return quotient
+ }
+ return undefined
+ }
+ })
+})
diff --git a/src/function/arithmetic/subtract.js b/src/function/arithmetic/subtract.js
index aa6732484b..5c546604b0 100644
--- a/src/function/arithmetic/subtract.js
+++ b/src/function/arithmetic/subtract.js
@@ -9,7 +9,6 @@ import { createMatrixAlgorithmSuite } from '../../type/matrix/utils/matrixAlgori
const name = 'subtract'
const dependencies = [
'typed',
- 'matrix',
'equalScalar',
'subtractScalar',
'unaryMinus',
@@ -17,7 +16,7 @@ const dependencies = [
'concat'
]
-export const createSubtract = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, subtractScalar, unaryMinus, DenseMatrix, concat }) => {
+export const createSubtract = /* #__PURE__ */ factory(name, dependencies, ({ typed, equalScalar, subtractScalar, unaryMinus, DenseMatrix, concat }) => {
// TODO: split function subtract in two: subtract and subtractScalar
const matAlgo01xDSid = createMatAlgo01xDSid({ typed })
@@ -25,7 +24,7 @@ export const createSubtract = /* #__PURE__ */ factory(name, dependencies, ({ typ
const matAlgo05xSfSf = createMatAlgo05xSfSf({ typed, equalScalar })
const matAlgo10xSids = createMatAlgo10xSids({ typed, DenseMatrix })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Subtract two values, `x - y`.
@@ -60,7 +59,30 @@ export const createSubtract = /* #__PURE__ */ factory(name, dependencies, ({ typ
return typed(
name,
{
- 'any, any': subtractScalar
+ 'any, any': subtractScalar,
+
+ 'Range, Range': typed.referToSelf(self => (r, p) => {
+ if (r.for !== p.for) throw new Error('Range length mismatch')
+ return r.createRange({
+ start: self(r.start, p.start),
+ length: r.length,
+ step: self(r.step, p.step)
+ })
+ }),
+ 'Range, Matrix': typed.referToSelf(
+ self => (r, m) => self(r.valueOf(), m)),
+ 'Range, any': typed.referToSelf(self => (r, s) => r.createRange({
+ start: self(r.start, s),
+ length: r.length,
+ step: r.step
+ })),
+ 'Matrix, Range': typed.referToSelf(
+ self => (m, r) => self(m, r.valueOf())),
+ 'any, Range': typed.referToSelf(self => (s, r) => r.createRange({
+ start: self(s, r.start),
+ length: r.length,
+ step: unaryMinus(r.step)
+ }))
},
matrixAlgorithmSuite({
elop: subtractScalar,
diff --git a/src/function/arithmetic/zero.js b/src/function/arithmetic/zero.js
new file mode 100644
index 0000000000..8bc6037c8f
--- /dev/null
+++ b/src/function/arithmetic/zero.js
@@ -0,0 +1,64 @@
+import { factory } from '../../utils/factory.js'
+
+const name = 'zero'
+const dependencies = [
+ 'typed', '?BigNumber', '?Complex', '?Fraction', '?unit'
+]
+
+export const createZeroNumber = /* #__PURE__ */ factory(
+ name, ['typed'], ({ typed }) => {
+ return typed(name, { number: () => 0 })
+ })
+
+export const createZero = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, BigNumber, Complex, Fraction, unit
+}) => {
+ /**
+ * Return the additive identity of the same type as the argument.
+ *
+ * Syntax:
+ *
+ * math.zero(x)
+ *
+ * Examples:
+ *
+ * math.zero(1.618) // returns 0
+ * math.zero(math.bignumber(222)) // BigNumber 0
+ * math.zero(math.fraction(1, 3)) // Fraction 0
+ * math.zero(math.evaluate('0 + 2i')) // Complex 0+0i
+ * math.zero([[2, 3, 4], [4, 5, 6]]) // [[0, 0, 0], [0, 0, 0]]
+ *
+ * See also:
+ * typeOf, numeric, one
+ *
+ * @param {MathType} x Any entity mathjs understands
+ * @return {MathType} Additive identity of same type as x
+ */
+ return typed(name, {
+ number: () => 0,
+ bigint: () => 0n,
+ BigNumber: () => new BigNumber(0),
+ Complex: () => new Complex(0),
+ Fraction: () => new Fraction(0),
+ boolean: () => false,
+ Unit: typed.referToSelf(self => u => {
+ // want 0 of the same units and value type as u
+ const result = u.clone()
+ result.value = self(u.value)
+ return result
+ }),
+ Array: typed.referToSelf(self => A => _zeroArray(A, self)),
+ Range: typed.referToSelf(self => R => {
+ const z = self(R.start)
+ return R.createRange({ start: z, step: z, length: R.length })
+ }),
+ // TODO: there should be a way to create the all-zero sparse matrix that
+ // does not involve constructing an array of all zeros
+ Matrix: typed.referToSelf(self => M =>
+ M.create(_zeroArray(M.valueOf(), self)))
+ })
+
+ function _zeroArray (A, zeroer) {
+ return A.map(elt => zeroer(elt))
+ }
+})
diff --git a/src/function/bitwise/bitAnd.js b/src/function/bitwise/bitAnd.js
index d6e169b09e..080a176aae 100644
--- a/src/function/bitwise/bitAnd.js
+++ b/src/function/bitwise/bitAnd.js
@@ -9,16 +9,15 @@ import { bitAndNumber } from '../../plain/number/index.js'
const name = 'bitAnd'
const dependencies = [
'typed',
- 'matrix',
- 'equalScalar',
- 'concat'
+ 'DenseMatrix',
+ 'equalScalar'
]
-export const createBitAnd = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, concat }) => {
+export const createBitAnd = /* #__PURE__ */ factory(name, dependencies, ({ typed, DenseMatrix, equalScalar }) => {
const matAlgo02xDS0 = createMatAlgo02xDS0({ typed, equalScalar })
const matAlgo06xS0S0 = createMatAlgo06xS0S0({ typed, equalScalar })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Bitwise AND two values, `x & y`.
diff --git a/src/function/bitwise/bitOr.js b/src/function/bitwise/bitOr.js
index c0f7dea215..45f98376f6 100644
--- a/src/function/bitwise/bitOr.js
+++ b/src/function/bitwise/bitOr.js
@@ -9,17 +9,15 @@ import { bitOrNumber } from '../../plain/number/index.js'
const name = 'bitOr'
const dependencies = [
'typed',
- 'matrix',
'equalScalar',
- 'DenseMatrix',
- 'concat'
+ 'DenseMatrix'
]
-export const createBitOr = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, DenseMatrix, concat }) => {
+export const createBitOr = /* #__PURE__ */ factory(name, dependencies, ({ typed, equalScalar, DenseMatrix }) => {
const matAlgo01xDSid = createMatAlgo01xDSid({ typed })
const matAlgo04xSidSid = createMatAlgo04xSidSid({ typed, equalScalar })
const matAlgo10xSids = createMatAlgo10xSids({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Bitwise OR two values, `x | y`.
diff --git a/src/function/bitwise/bitXor.js b/src/function/bitwise/bitXor.js
index a46acacc8a..75e1130a37 100644
--- a/src/function/bitwise/bitXor.js
+++ b/src/function/bitwise/bitXor.js
@@ -9,17 +9,15 @@ import { bitXorNumber } from '../../plain/number/index.js'
const name = 'bitXor'
const dependencies = [
'typed',
- 'matrix',
'DenseMatrix',
- 'concat',
'SparseMatrix'
]
-export const createBitXor = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, DenseMatrix, concat, SparseMatrix }) => {
+export const createBitXor = /* #__PURE__ */ factory(name, dependencies, ({ typed, DenseMatrix, SparseMatrix }) => {
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo07xSSf = createMatAlgo07xSSf({ typed, SparseMatrix })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Bitwise XOR two values, `x ^ y`.
diff --git a/src/function/bitwise/leftShift.js b/src/function/bitwise/leftShift.js
index 18b406fc61..a84ab91796 100644
--- a/src/function/bitwise/leftShift.js
+++ b/src/function/bitwise/leftShift.js
@@ -13,22 +13,20 @@ import { leftShiftBigNumber } from '../../utils/bignumber/bitwise.js'
const name = 'leftShift'
const dependencies = [
'typed',
- 'matrix',
'equalScalar',
'zeros',
- 'DenseMatrix',
- 'concat'
+ 'DenseMatrix'
]
-export const createLeftShift = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, zeros, DenseMatrix, concat }) => {
+export const createLeftShift = /* #__PURE__ */ factory(name, dependencies, ({ typed, equalScalar, zeros, DenseMatrix }) => {
const matAlgo01xDSid = createMatAlgo01xDSid({ typed })
const matAlgo02xDS0 = createMatAlgo02xDS0({ typed, equalScalar })
const matAlgo08xS0Sid = createMatAlgo08xS0Sid({ typed, equalScalar })
const matAlgo10xSids = createMatAlgo10xSids({ typed, DenseMatrix })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
- const useMatrixForArrayScalar = createUseMatrixForArrayScalar({ typed, matrix })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
+ const useMatrixForArrayScalar = createUseMatrixForArrayScalar({ typed, DenseMatrix })
/**
* Bitwise left logical shift of a value x by y number of bits, `x << y`.
diff --git a/src/function/bitwise/rightArithShift.js b/src/function/bitwise/rightArithShift.js
index c84a411d06..2747b973c4 100644
--- a/src/function/bitwise/rightArithShift.js
+++ b/src/function/bitwise/rightArithShift.js
@@ -13,22 +13,20 @@ import { rightArithShiftNumber } from '../../plain/number/index.js'
const name = 'rightArithShift'
const dependencies = [
'typed',
- 'matrix',
'equalScalar',
'zeros',
- 'DenseMatrix',
- 'concat'
+ 'DenseMatrix'
]
-export const createRightArithShift = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, zeros, DenseMatrix, concat }) => {
+export const createRightArithShift = /* #__PURE__ */ factory(name, dependencies, ({ typed, equalScalar, zeros, DenseMatrix }) => {
const matAlgo01xDSid = createMatAlgo01xDSid({ typed })
const matAlgo02xDS0 = createMatAlgo02xDS0({ typed, equalScalar })
const matAlgo08xS0Sid = createMatAlgo08xS0Sid({ typed, equalScalar })
const matAlgo10xSids = createMatAlgo10xSids({ typed, DenseMatrix })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
- const useMatrixForArrayScalar = createUseMatrixForArrayScalar({ typed, matrix })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
+ const useMatrixForArrayScalar = createUseMatrixForArrayScalar({ typed, DenseMatrix })
/**
* Bitwise right arithmetic shift of a value x by y number of bits, `x >> y`.
diff --git a/src/function/bitwise/rightLogShift.js b/src/function/bitwise/rightLogShift.js
index 55cef7d355..286e201343 100644
--- a/src/function/bitwise/rightLogShift.js
+++ b/src/function/bitwise/rightLogShift.js
@@ -12,22 +12,20 @@ import { createUseMatrixForArrayScalar } from './useMatrixForArrayScalar.js'
const name = 'rightLogShift'
const dependencies = [
'typed',
- 'matrix',
'equalScalar',
'zeros',
- 'DenseMatrix',
- 'concat'
+ 'DenseMatrix'
]
-export const createRightLogShift = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, zeros, DenseMatrix, concat }) => {
+export const createRightLogShift = /* #__PURE__ */ factory(name, dependencies, ({ typed, equalScalar, zeros, DenseMatrix }) => {
const matAlgo01xDSid = createMatAlgo01xDSid({ typed })
const matAlgo02xDS0 = createMatAlgo02xDS0({ typed, equalScalar })
const matAlgo08xS0Sid = createMatAlgo08xS0Sid({ typed, equalScalar })
const matAlgo10xSids = createMatAlgo10xSids({ typed, DenseMatrix })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
- const useMatrixForArrayScalar = createUseMatrixForArrayScalar({ typed, matrix })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
+ const useMatrixForArrayScalar = createUseMatrixForArrayScalar({ typed, DenseMatrix })
/**
* Bitwise right logical shift of value x by y number of bits, `x >>> y`.
diff --git a/src/function/bitwise/useMatrixForArrayScalar.js b/src/function/bitwise/useMatrixForArrayScalar.js
index ff7f231e2f..a039ab828c 100644
--- a/src/function/bitwise/useMatrixForArrayScalar.js
+++ b/src/function/bitwise/useMatrixForArrayScalar.js
@@ -1,15 +1,15 @@
import { factory } from '../../utils/factory.js'
-export const createUseMatrixForArrayScalar = /* #__PURE__ */ factory('useMatrixForArrayScalar', ['typed', 'matrix'], ({ typed, matrix }) => ({
+export const createUseMatrixForArrayScalar = /* #__PURE__ */ factory('useMatrixForArrayScalar', ['typed', 'DenseMatrix'], ({ typed, DenseMatrix }) => ({
'Array, number': typed.referTo('DenseMatrix, number',
- selfDn => (x, y) => selfDn(matrix(x), y).valueOf()),
+ selfDn => (x, y) => selfDn(new DenseMatrix(x), y).valueOf()),
'Array, BigNumber': typed.referTo('DenseMatrix, BigNumber',
- selfDB => (x, y) => selfDB(matrix(x), y).valueOf()),
+ selfDB => (x, y) => selfDB(new DenseMatrix(x), y).valueOf()),
'number, Array': typed.referTo('number, DenseMatrix',
- selfnD => (x, y) => selfnD(x, matrix(y)).valueOf()),
+ selfnD => (x, y) => selfnD(x, new DenseMatrix(y)).valueOf()),
'BigNumber, Array': typed.referTo('BigNumber, DenseMatrix',
- selfBD => (x, y) => selfBD(x, matrix(y)).valueOf())
+ selfBD => (x, y) => selfBD(x, new DenseMatrix(y)).valueOf())
}))
diff --git a/src/function/logical/and.js b/src/function/logical/and.js
index 827751d042..dc7ef692cd 100644
--- a/src/function/logical/and.js
+++ b/src/function/logical/and.js
@@ -9,19 +9,18 @@ import { andNumber } from '../../plain/number/index.js'
const name = 'and'
const dependencies = [
'typed',
- 'matrix',
+ 'DenseMatrix',
'equalScalar',
'zeros',
- 'not',
- 'concat'
+ 'not'
]
-export const createAnd = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, zeros, not, concat }) => {
+export const createAnd = /* #__PURE__ */ factory(name, dependencies, ({ typed, DenseMatrix, equalScalar, zeros, not }) => {
const matAlgo02xDS0 = createMatAlgo02xDS0({ typed, equalScalar })
const matAlgo06xS0S0 = createMatAlgo06xS0S0({ typed, equalScalar })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Logical `and`. Test whether two values are both defined with a nonzero/nonempty value.
@@ -107,12 +106,12 @@ export const createAnd = /* #__PURE__ */ factory(name, dependencies, ({ typed, m
'Array, any': typed.referToSelf(self => (x, y) => {
// use matrix implementation
- return self(matrix(x), y).valueOf()
+ return self(new DenseMatrix(x), y).valueOf()
}),
'any, Array': typed.referToSelf(self => (x, y) => {
// use matrix implementation
- return self(x, matrix(y)).valueOf()
+ return self(x, new DenseMatrix(y)).valueOf()
})
},
matrixAlgorithmSuite({
diff --git a/src/function/logical/nullish.js b/src/function/logical/nullish.js
index cd3a7af52d..1005097cce 100644
--- a/src/function/logical/nullish.js
+++ b/src/function/logical/nullish.js
@@ -5,15 +5,15 @@ import { createMatAlgo13xDD } from '../../type/matrix/utils/matAlgo13xDD.js'
import { DimensionError } from '../../error/DimensionError.js'
const name = 'nullish'
-const dependencies = ['typed', 'matrix', 'size', 'flatten', 'deepEqual']
+const dependencies = ['typed', 'DenseMatrix', 'size', 'flatten', 'deepEqual']
export const createNullish = /* #__PURE__ */ factory(
name,
dependencies,
- ({ typed, matrix, size, flatten, deepEqual }) => {
+ ({ typed, DenseMatrix, size, flatten, deepEqual }) => {
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
- const matAlgo14xDs = createMatAlgo14xDs({ typed })
const matAlgo13xDD = createMatAlgo13xDD({ typed })
+ const matAlgo14xDs = createMatAlgo14xDs({ typed })
/**
* Nullish coalescing operator (??). Returns the right-hand side operand
@@ -66,14 +66,14 @@ export const createNullish = /* #__PURE__ */ factory(
// DenseMatrix-first handlers (no broadcasting between collections)
'DenseMatrix, DenseMatrix': typed.referToSelf(self => (x, y) => matAlgo13xDD(x, y, self)),
'DenseMatrix, SparseMatrix': typed.referToSelf(self => (x, y) => matAlgo03xDSf(x, y, self, false)),
- 'DenseMatrix, Array': typed.referToSelf(self => (x, y) => matAlgo13xDD(x, matrix(y), self)),
+ 'DenseMatrix, Array': typed.referToSelf(self => (x, y) => matAlgo13xDD(x, new DenseMatrix(y), self)),
'DenseMatrix, any': typed.referToSelf(self => (x, y) => matAlgo14xDs(x, y, self, false)),
// Array-first handlers (bridge via matrix() where needed)
- 'Array, Array': typed.referToSelf(self => (x, y) => matAlgo13xDD(matrix(x), matrix(y), self).valueOf()),
- 'Array, DenseMatrix': typed.referToSelf(self => (x, y) => matAlgo13xDD(matrix(x), y, self)),
- 'Array, SparseMatrix': typed.referToSelf(self => (x, y) => matAlgo03xDSf(matrix(x), y, self, false)),
- 'Array, any': typed.referToSelf(self => (x, y) => matAlgo14xDs(matrix(x), y, self, false).valueOf())
+ 'Array, Array': typed.referToSelf(self => (x, y) => matAlgo13xDD(new DenseMatrix(x), new DenseMatrix(y), self).valueOf()),
+ 'Array, DenseMatrix': typed.referToSelf(self => (x, y) => matAlgo13xDD(new DenseMatrix(x), y, self)),
+ 'Array, SparseMatrix': typed.referToSelf(self => (x, y) => matAlgo03xDSf(new DenseMatrix(x), y, self, false)),
+ 'Array, any': typed.referToSelf(self => (x, y) => matAlgo14xDs(new DenseMatrix(x), y, self, false).valueOf())
}
)
}
diff --git a/src/function/logical/or.js b/src/function/logical/or.js
index 12d6c5b48d..694e1f8768 100644
--- a/src/function/logical/or.js
+++ b/src/function/logical/or.js
@@ -8,17 +8,15 @@ import { orNumber } from '../../plain/number/index.js'
const name = 'or'
const dependencies = [
'typed',
- 'matrix',
'equalScalar',
- 'DenseMatrix',
- 'concat'
+ 'DenseMatrix'
]
-export const createOr = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, DenseMatrix, concat }) => {
+export const createOr = /* #__PURE__ */ factory(name, dependencies, ({ typed, equalScalar, DenseMatrix }) => {
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo05xSfSf = createMatAlgo05xSfSf({ typed, equalScalar })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Logical `or`. Test if at least one value is defined with a nonzero/nonempty value.
diff --git a/src/function/logical/xor.js b/src/function/logical/xor.js
index 1ae3a05f5b..078c6716c7 100644
--- a/src/function/logical/xor.js
+++ b/src/function/logical/xor.js
@@ -8,9 +8,7 @@ import { xorNumber } from '../../plain/number/index.js'
const name = 'xor'
const dependencies = [
'typed',
- 'matrix',
'DenseMatrix',
- 'concat',
'SparseMatrix'
]
@@ -18,7 +16,7 @@ export const createXor = /* #__PURE__ */ factory(name, dependencies, ({ typed, m
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo07xSSf = createMatAlgo07xSSf({ typed, SparseMatrix })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Logical `xor`. Test whether one and only one value is defined with a nonzero/nonempty value.
diff --git a/src/function/matrix/concat.js b/src/function/matrix/concat.js
index 6d8fe0f44b..fbd3d7f537 100644
--- a/src/function/matrix/concat.js
+++ b/src/function/matrix/concat.js
@@ -6,9 +6,9 @@ import { DimensionError } from '../../error/DimensionError.js'
import { factory } from '../../utils/factory.js'
const name = 'concat'
-const dependencies = ['typed', 'matrix', 'isInteger']
+const dependencies = ['typed', 'isInteger']
-export const createConcat = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, isInteger }) => {
+export const createConcat = /* #__PURE__ */ factory(name, dependencies, ({ typed, isInteger }) => {
/**
* Concatenate two or more matrices.
*
@@ -44,6 +44,7 @@ export const createConcat = /* #__PURE__ */ factory(name, dependencies, ({ typed
let i
const len = args.length
let dim = -1 // zero-based dimension
+ let matrixMaker
let prevDim
let asMatrix = false
const matrices = [] // contains multi dimensional arrays
@@ -54,6 +55,7 @@ export const createConcat = /* #__PURE__ */ factory(name, dependencies, ({ typed
// test whether we need to return a Matrix (if not we return an Array)
if (isMatrix(arg)) {
asMatrix = true
+ matrixMaker = arg
}
if (isNumber(arg) || isBigNumber(arg)) {
@@ -97,7 +99,7 @@ export const createConcat = /* #__PURE__ */ factory(name, dependencies, ({ typed
res = _concat(res, matrices.shift(), dim)
}
- return asMatrix ? matrix(res) : res
+ return asMatrix ? matrixMaker.create(res) : res
},
'...string': function (args) {
diff --git a/src/function/matrix/det.js b/src/function/matrix/det.js
index 8c2123fa6e..f3bb8f7c50 100644
--- a/src/function/matrix/det.js
+++ b/src/function/matrix/det.js
@@ -4,9 +4,9 @@ import { format } from '../../utils/string.js'
import { factory } from '../../utils/factory.js'
const name = 'det'
-const dependencies = ['typed', 'matrix', 'subtractScalar', 'multiply', 'divideScalar', 'isZero', 'unaryMinus']
+const dependencies = ['typed', 'DenseMatrix', 'subtractScalar', 'multiply', 'divideScalar', 'isZero', 'unaryMinus']
-export const createDet = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, subtractScalar, multiply, divideScalar, isZero, unaryMinus }) => {
+export const createDet = /* #__PURE__ */ factory(name, dependencies, ({ typed, DenseMatrix, subtractScalar, multiply, divideScalar, isZero, unaryMinus }) => {
/**
* Calculate the determinant of a matrix.
*
@@ -42,7 +42,7 @@ export const createDet = /* #__PURE__ */ factory(name, dependencies, ({ typed, m
if (isMatrix(x)) {
size = x.size()
} else if (Array.isArray(x)) {
- x = matrix(x)
+ x = new DenseMatrix(x)
size = x.size()
} else {
// a scalar
diff --git a/src/function/matrix/diag.js b/src/function/matrix/diag.js
index a0f9bc4d47..b6e849a0c1 100644
--- a/src/function/matrix/diag.js
+++ b/src/function/matrix/diag.js
@@ -127,6 +127,7 @@ export const createDiag = /* #__PURE__ */ factory(name, dependencies, ({ typed,
// matrix size
const ms = [l + kSub, l + kSuper]
+ if (format === 'range') format = 'dense'
if (format && format !== 'sparse' && format !== 'dense') {
throw new TypeError(`Unknown matrix type ${format}"`)
}
diff --git a/src/function/matrix/identity.js b/src/function/matrix/identity.js
index 5ee1cf0bc6..0642e4c5c7 100644
--- a/src/function/matrix/identity.js
+++ b/src/function/matrix/identity.js
@@ -7,13 +7,12 @@ const name = 'identity'
const dependencies = [
'typed',
'config',
- 'matrix',
'BigNumber',
'DenseMatrix',
'SparseMatrix'
]
-export const createIdentity = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, matrix, BigNumber, DenseMatrix, SparseMatrix }) => {
+export const createIdentity = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, BigNumber, DenseMatrix, SparseMatrix }) => {
/**
* Create a 2-dimensional identity matrix with size m x n or n x n.
* The matrix has ones on the diagonal and zeros elsewhere.
@@ -46,11 +45,11 @@ export const createIdentity = /* #__PURE__ */ factory(name, dependencies, ({ typ
*/
return typed(name, {
'': function () {
- return (config.matrix === 'Matrix') ? matrix([]) : []
+ return (config.matrix === 'Matrix') ? new DenseMatrix([]) : []
},
string: function (format) {
- return matrix(format)
+ return _identity(0, 0, format)
},
'number | BigNumber': function (rows) {
@@ -88,7 +87,7 @@ export const createIdentity = /* #__PURE__ */ factory(name, dependencies, ({ typ
function _identityVector (size, format) {
switch (size.length) {
- case 0: return format ? matrix(format) : []
+ case 0: return _identity(0, 0, format)
case 1: return _identity(size[0], size[0], format)
case 2: return _identity(size[0], size[1], format)
default: throw new Error('Vector containing two values expected')
@@ -112,11 +111,16 @@ export const createIdentity = /* #__PURE__ */ factory(name, dependencies, ({ typ
if (isBigNumber(rows)) rows = rows.toNumber()
if (isBigNumber(cols)) cols = cols.toNumber()
- if (!isInteger(rows) || rows < 1) {
- throw new Error('Parameters in function identity must be positive integers')
+ if (!isInteger(rows) || rows < 0) {
+ throw new Error('Parameters in function identity must be nonnegative integers')
}
- if (!isInteger(cols) || cols < 1) {
- throw new Error('Parameters in function identity must be positive integers')
+ if (!isInteger(cols) || cols < 0) {
+ throw new Error('Parameters in function identity must be nonnegative integers')
+ }
+
+ if (rows === 0 || cols === 0) {
+ if (!format) return []
+ return format === 'sparse' ? new SparseMatrix() : new DenseMatrix()
}
const one = Big ? new BigNumber(1) : 1
diff --git a/src/function/matrix/inv.js b/src/function/matrix/inv.js
index 5065fce27e..060e4b1ca9 100644
--- a/src/function/matrix/inv.js
+++ b/src/function/matrix/inv.js
@@ -6,7 +6,6 @@ import { format } from '../../utils/string.js'
const name = 'inv'
const dependencies = [
'typed',
- 'matrix',
'divideScalar',
'addScalar',
'multiply',
@@ -45,7 +44,7 @@ export const createInv = /* #__PURE__ */ factory(name, dependencies, ({ typed, m
// vector
if (size[0] === 1) {
if (isMatrix(x)) {
- return matrix([
+ return x.create([
divideScalar(1, x.valueOf()[0])
])
} else {
@@ -65,10 +64,7 @@ export const createInv = /* #__PURE__ */ factory(name, dependencies, ({ typed, m
const cols = size[1]
if (rows === cols) {
if (isMatrix(x)) {
- return matrix(
- _inv(x.valueOf(), rows, cols),
- x.storage()
- )
+ return x.create(_inv(x.valueOf(), rows, cols))
} else {
// return an Array
return _inv(x, rows, cols)
diff --git a/src/function/matrix/matrixFromRows.js b/src/function/matrix/matrixFromRows.js
index e8eaf8178c..2aad9b2e0e 100644
--- a/src/function/matrix/matrixFromRows.js
+++ b/src/function/matrix/matrixFromRows.js
@@ -24,7 +24,7 @@ export const createMatrixFromRows = /* #__PURE__ */ factory(name, dependencies,
* matrix, matrixFromColumns, matrixFromFunction, zeros
*
* @param {... Array | Matrix} rows Multiple rows
- * @return { number[][] | Matrix } if at least one of the arguments is an array, an array will be returned
+ * @return { number[][] | Matrix } if at least one of the arguments is an Matrix, a Matrix will be returned
*/
return typed(name, {
'...Array': function (arr) {
diff --git a/src/function/matrix/ones.js b/src/function/matrix/ones.js
index 5bd55b7719..c000b88085 100644
--- a/src/function/matrix/ones.js
+++ b/src/function/matrix/ones.js
@@ -4,9 +4,13 @@ import { resize } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'ones'
-const dependencies = ['typed', 'config', 'matrix', 'BigNumber']
+const dependencies = [
+ 'typed', 'config', 'matrix', 'BigNumber', 'Range', 'zero'
+]
-export const createOnes = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, matrix, BigNumber }) => {
+export const createOnes = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, config, matrix, BigNumber, Range, zero
+}) => {
/**
* Create a matrix filled with ones. The created matrix can have one or
* multiple dimensions.
@@ -88,6 +92,23 @@ export const createOnes = /* #__PURE__ */ factory(name, dependencies, ({ typed,
if (format) {
// return a matrix
+ if (format === 'range') {
+ if (size.length === 0) {
+ return new Range({ start: defaultValue, length: 0 })
+ }
+ if (size.length === 1) {
+ return new Range({
+ start: defaultValue,
+ length: size[0],
+ step: zero(defaultValue)
+ })
+ }
+ return new Range({
+ start: _ones(size.slice(1), format),
+ length: size[0],
+ step: zero(defaultValue)
+ })
+ }
const m = matrix(format)
if (size.length > 0) {
return m.resize(size, defaultValue)
diff --git a/src/function/matrix/range.js b/src/function/matrix/range.js
index d919eeb76a..a27ec26e77 100644
--- a/src/function/matrix/range.js
+++ b/src/function/matrix/range.js
@@ -1,14 +1,21 @@
import { factory } from '../../utils/factory.js'
-import { noBignumber, noMatrix } from '../../utils/noop.js'
+import { parseRange } from '../../utils/collection.js'
const name = 'range'
-const dependencies = ['typed', 'config', '?matrix', '?bignumber', 'equal', 'smaller', 'smallerEq', 'larger', 'largerEq', 'add', 'isZero', 'isPositive']
-
-export const createRange = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, matrix, bignumber, smaller, smallerEq, larger, largerEq, add, isZero, isPositive }) => {
+export const dependencies = [
+ 'typed', 'config', '?Range', '?matrix', '?bignumber',
+ 'smaller', 'smallerEq', 'larger', 'largerEq', 'add', 'isZero', 'isPositive'
+]
+
+export const createRange = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, config, Range, matrix, bignumber,
+ smaller, smallerEq, larger, largerEq, add, isZero, isPositive
+}) => {
/**
* Create a matrix or array containing a range of values.
* By default, the range end is excluded. This can be customized by providing
- * an extra parameter `includeEnd`.
+ * an extra boolean parameter `includeEnd`, or by using the attribute-object
+ * argument form instead.
*
* Syntax:
*
@@ -20,6 +27,7 @@ export const createRange = /* #__PURE__ */ factory(name, dependencies, ({ typed,
* // end and a step size of 1.
* math.range(start, end, step [, includeEnd]) // Create a range with start, step,
* // and end.
+ * math.range({start?, end?, last?, step?, length?}) // Create a range with given attributes
*
* Where:
*
@@ -33,12 +41,28 @@ export const createRange = /* #__PURE__ */ factory(name, dependencies, ({ typed,
* Step size. Default value is 1.
* - `includeEnd: boolean`
* Option to specify whether to include the end or not. False by default.
+ * Note this parameter is not allowed when the arguments are supplied as
+ * a plain object of attributes.
+ * - `{start?, end?, last?, step?, length?}`
+ * A plain object of attributes with any of the indicated keys, each of
+ * which is optional. Any unspecified keys will be filled in per the
+ * [Range documentation](../../../docs/reference/classes/range.md). Note
+ * in particular in this form, the `end` property always specifies an
+ * exclusive upper bound and the `last` property specifies an inclusive
+ * upper bound.
+ *
+ * The function returns a `Range` matrix object when the library is
+ * configured with `config = { matrix: 'Matrix' }, and returns an Array
+ * otherwise.
*
- * The function returns a `DenseMatrix` when the library is configured with
- * `config = { matrix: 'Matrix' }, and returns an Array otherwise.
- * Note that the type of the returned values is taken from the type of the
- * provided start/end value. If only one of these is a built-in `number` type,
- * it will be promoted to the type of the other endpoint. However, in the case
+ * Note that the type of the returned values is determined primarily by the
+ * type(s) of the provided start and step values. Generally speaking, it will
+ * be the type of the start value if the step is not specified, and the
+ * type that mathjs returns for `start + step` if both are specified. For
+ * example, `range(3, bignumber(10), bignumber(1))` will produce a range of
+ * BigNumbers since `add(3, bignumber(1))` produces a BigNumber, whereas
+ * `range(3, 10n, 1n)` will produce a range of `number` values because
+ * `add(3, 1n)` produces a number, by mathjs conversion rules. In the case
* of Unit values, both endpoints must have compatible units, and the return
* value will have compatible units as well.
*
@@ -55,140 +79,77 @@ export const createRange = /* #__PURE__ */ factory(name, dependencies, ({ typed,
*
* ones, zeros, size, subset
*
- * @param {*} args Parameters describing the range's `start`, `end`, and optional `step`.
+ * @param {*} args Parameters describing the range's `start`, `end`, and
+ * optional `step`.
* @return {Array | Matrix} range
*/
+ const MathType = 'number|bigint|BigNumber|Fraction|Unit|Array|Matrix'
+ // Is anything else needed on that list?
return typed(name, {
- // TODO: simplify signatures when typed-function supports default values and optional arguments
+ // TODO: simplify signatures a bit futher when typed-function supports
+ // default values and optional arguments
- string: _strRange,
- 'string, boolean': _strRange,
+ // string arguments just get broken up and passed back to range:
+ string: typed.referToSelf(
+ self => str => _redispatchStrings(self, str, false)),
+ 'string, boolean': typed.referToSelf(
+ self => (str, includeEnd) => _redispatchStrings(self, str, includeEnd)),
number: function (oops) {
throw new TypeError(`Too few arguments to function range(): ${oops}`)
},
boolean: function (oops) {
- throw new TypeError(`Unexpected type of argument 1 to function range(): ${oops}, number|bigint|BigNumber|Fraction`)
- },
-
- 'number, number': function (start, end) {
- return _out(_range(start, end, 1, false))
- },
- 'number, number, number': function (start, end, step) {
- return _out(_range(start, end, step, false))
- },
- 'number, number, boolean': function (start, end, includeEnd) {
- return _out(_range(start, end, 1, includeEnd))
- },
- 'number, number, number, boolean': function (start, end, step, includeEnd) {
- return _out(_range(start, end, step, includeEnd))
- },
-
- // Handle bigints; if either limit is bigint, range should be too
- 'bigint, bigint|number': function (start, end) {
- return _out(_range(start, end, 1n, false))
- },
- 'number, bigint': function (start, end) {
- return _out(_range(BigInt(start), end, 1n, false))
- },
- 'bigint, bigint|number, bigint|number': function (start, end, step) {
- return _out(_range(start, end, BigInt(step), false))
- },
- 'number, bigint, bigint|number': function (start, end, step) {
- return _out(_range(BigInt(start), end, BigInt(step), false))
- },
- 'bigint, bigint|number, boolean': function (start, end, includeEnd) {
- return _out(_range(start, end, 1n, includeEnd))
+ throw new TypeError(
+ 'Unexpected type of argument 1 to function range(): ' +
+ `${oops}, number|bigint|BigNumber|Fraction`)
},
- 'number, bigint, boolean': function (start, end, includeEnd) {
- return _out(_range(BigInt(start), end, 1n, includeEnd))
- },
- 'bigint, bigint|number, bigint|number, boolean': function (start, end, step, includeEnd) {
- return _out(_range(start, end, BigInt(step), includeEnd))
- },
- 'number, bigint, bigint|number, boolean': function (start, end, step, includeEnd) {
- return _out(_range(BigInt(start), end, BigInt(step), includeEnd))
- },
-
- 'BigNumber, BigNumber': function (start, end) {
- const BigNumber = start.constructor
-
- return _out(_range(start, end, new BigNumber(1), false))
- },
- 'BigNumber, BigNumber, BigNumber': function (start, end, step) {
- return _out(_range(start, end, step, false))
- },
- 'BigNumber, BigNumber, boolean': function (start, end, includeEnd) {
- const BigNumber = start.constructor
- return _out(_range(start, end, new BigNumber(1), includeEnd))
- },
- 'BigNumber, BigNumber, BigNumber, boolean': function (start, end, step, includeEnd) {
- return _out(_range(start, end, step, includeEnd))
- },
-
- 'Fraction, Fraction': function (start, end) {
- return _out(_range(start, end, 1, false))
- },
- 'Fraction, Fraction, Fraction': function (start, end, step) {
- return _out(_range(start, end, step, false))
- },
- 'Fraction, Fraction, boolean': function (start, end, includeEnd) {
- return _out(_range(start, end, 1, includeEnd))
- },
- 'Fraction, Fraction, Fraction, boolean': function (start, end, step, includeEnd) {
- return _out(_range(start, end, step, includeEnd))
- },
-
- 'Unit, Unit, Unit': function (start, end, step) {
- return _out(_range(start, end, step, false))
+ Object: obj => {
+ if (!Range) {
+ if ('last' in obj) return _range(obj.start, obj.last, obj.step, true)
+ return _range(obj.start, obj.end, obj.step, false)
+ }
+ const rng = new Range(obj)
+ return config.matrix === 'Array' ? rng.toArray() : rng
},
- 'Unit, Unit, Unit, boolean': function (start, end, step, includeEnd) {
- return _out(_range(start, end, step, includeEnd))
- }
+ [`${MathType}, ${MathType}`]: _range,
+ [`${MathType}, ${MathType}, ${MathType}`]: _range,
+ [`${MathType}, ${MathType}, boolean`]:
+ (start, end, includeEnd) => _range(start, end, undefined, includeEnd),
+ [`${MathType}, ${MathType}, ${MathType}, boolean`]: _range
})
- function _out (arr) {
- if (config.matrix === 'Matrix') {
- return matrix ? matrix(arr) : noMatrix()
- }
-
- return arr
- }
-
- function _strRange (str, includeEnd) {
- const r = _parse(str)
- if (!r) {
- throw new SyntaxError('String "' + str + '" is no valid range')
- }
-
- if (config.number === 'BigNumber') {
- if (bignumber === undefined) {
- noBignumber()
- }
-
- return _out(_range(
- bignumber(r.start),
- bignumber(r.end),
- bignumber(r.step)),
- includeEnd)
- } else {
- return _out(_range(r.start, r.end, r.step, includeEnd))
+ function _redispatchStrings (fullRange, str, includeEnd) {
+ const fields = parseRange(str)
+ if (fields === null) {
+ throw new SyntaxError(`String '${str}' does not represent a range`)
}
+ const step = fields.step || '1'
+ const start = fields.start || '0'
+ const end = fields.end || 'Infinity'
+ // let typed-function handle the strings
+ const result = fullRange(start, end, step, includeEnd)
+ return result
}
/**
- * Create a range with numbers or BigNumbers
- * @param {number | BigNumber | Unit} start
- * @param {number | BigNumber | Unit} end
- * @param {number | BigNumber | Unit} step
+ * Create a range from start, step, end
+ * @param {MathType} start
+ * @param {MathType} end
+ * @param {MathType} step
* @param {boolean} includeEnd
- * @returns {Array} range
+ * @returns {Range | Array} range
* @private
*/
- function _range (start, end, step, includeEnd) {
+ function _range (start, end, step, includeEnd = false) {
+ if (Range) {
+ const range = new Range(
+ includeEnd ? { start, step, last: end } : { start, step, end })
+ return config.matrix === 'Array' ? range.toArray() : range
+ }
+ // Otherwise we have to make up an Array ourselves:
const array = []
if (isZero(step)) throw new Error('Step must be non-zero')
const ongoing = isPositive(step)
@@ -201,49 +162,4 @@ export const createRange = /* #__PURE__ */ factory(name, dependencies, ({ typed,
}
return array
}
-
- /**
- * Parse a string into a range,
- * The string contains the start, optional step, and end, separated by a colon.
- * If the string does not contain a valid range, null is returned.
- * For example str='0:2:11'.
- * @param {string} str
- * @return {{start: number, end: number, step: number} | null} range Object containing properties start, end, step
- * @private
- */
- function _parse (str) {
- const args = str.split(':')
-
- // number
- const nums = args.map(function (arg) {
- // use Number and not parseFloat as Number returns NaN on invalid garbage in the string
- return Number(arg)
- })
-
- const invalid = nums.some(function (num) {
- return isNaN(num)
- })
- if (invalid) {
- return null
- }
-
- switch (nums.length) {
- case 2:
- return {
- start: nums[0],
- end: nums[1],
- step: 1
- }
-
- case 3:
- return {
- start: nums[0],
- end: nums[2],
- step: nums[1]
- }
-
- default:
- return null
- }
- }
})
diff --git a/src/function/matrix/squeeze.js b/src/function/matrix/squeeze.js
index 9361b24ca4..e6a0220c29 100644
--- a/src/function/matrix/squeeze.js
+++ b/src/function/matrix/squeeze.js
@@ -18,15 +18,20 @@ export const createSqueeze = /* #__PURE__ */ factory(name, dependencies, ({ type
* math.squeeze([3]) // returns 3
* math.squeeze([[3]]) // returns 3
*
- * const A = math.zeros(3, 1) // returns [[0], [0], [0]] (size 3x1)
- * math.squeeze(A) // returns [0, 0, 0] (size 3)
- *
- * const B = math.zeros(1, 3) // returns [[0, 0, 0]] (size 1x3)
- * math.squeeze(B) // returns [0, 0, 0] (size 3)
- *
- * // only inner and outer dimensions are removed
- * const C = math.zeros(2, 1, 3) // returns [[[0, 0, 0]], [[0, 0, 0]]] (size 2x1x3)
- * math.squeeze(C) // returns [[[0, 0, 0]], [[0, 0, 0]]] (size 2x1x3)
+ * // Squeezes size 3x1 to size 3:
+ * const A = math.zeros(3, 1)
+ * A // Matrix [[0], [0], [0]] ...
+ * math.squeeze(A) // Matrix [0, 0, 0]
+ *
+ * // Also squeezes size 1x3 to 3:
+ * const B = math.zeros(1, 3)
+ * B // Matrix [[0, 0, 0]] ...
+ * math.squeeze(B) // Matrix [0, 0, 0]
+ *
+ * // only inner and outer dimensions are removed:
+ * const C = math.zeros(2, 1, 3)
+ * C // Matrix [[[0, 0, 0]], [[0, 0, 0]]] ...
+ * math.squeeze(C) // Matrix [[[0, 0, 0]], [[0, 0, 0]]]
*
* See also:
*
diff --git a/src/function/matrix/subset.js b/src/function/matrix/subset.js
index 8ca59e0523..3de8e3410a 100644
--- a/src/function/matrix/subset.js
+++ b/src/function/matrix/subset.js
@@ -6,12 +6,35 @@ import { DimensionError } from '../../error/DimensionError.js'
import { factory } from '../../utils/factory.js'
const name = 'subset'
-const dependencies = ['typed', 'matrix', 'zeros', 'add']
+export const dependencies = ['typed', 'matrix', 'zeros', 'add', 'index', 'size']
-export const createSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, zeros, add }) => {
+export const createSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, zeros, add, index, size }) => {
/**
* Get or set a subset of a matrix or string.
*
+ * The second argument should be a specification of the desired subset of
+ * the first argument. Therefore, the second argument is typically an Index
+ * object produced by the `index` function, which see (in short, for each
+ * dimension of the matrix, the Index specifies one position or a list or
+ * range of positions to include in the subset).
+ *
+ * For convenience, the second argument may be simply a number n, in which
+ * case the subset is the entire section of one dimension lower than the
+ * given matrix, at position n. In other words, it corresponds to the
+ * entry of a vector at position n, or the row of a 2D matrix at position
+ * n, etc.
+ *
+ * Furthermore, it can also be an array of appropriate arguments to the
+ * `index` function, in which case it will be passed to the `index` function
+ * for you. Beware, though: in the case of a 1d vector v,
+ * `math.subset(v, [2, 3])` will not therefore return the elements at
+ * positions 2 and 3, because passing those arguments to `index` would
+ * attempt to index the first dimension of v by 2 and its nonexistent second
+ * dimension by 3. You can call `math.subset(v, [[2, 3]])` to obtain the
+ * elements at positions 2 and 3, because now the inner `[2, 3]` will be
+ * interpreted as the list of positions with which to index into the first
+ * dimension.
+ *
* Syntax:
* math.subset(value, index) // retrieve a subset
* math.subset(value, index, replacement [, defaultValue]) // replace a subset
@@ -20,15 +43,21 @@ export const createSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed
*
* // get a subset
* const d = [[1, 2], [3, 4]]
- * math.subset(d, math.index(1, 0)) // returns 3
- * math.subset(d, math.index([0, 1], [1])) // returns [[2], [4]]
- * math.subset(d, math.index([false, true], [0])) // returns [[3]]
+ * math.subset(d, math.index(1, 0)) // returns 3 ...
+ * math.subset(d, [1, 0]) // returns 3 ...
+ * math.subset(d, math.index([0, 1], [1])) // Array [[2], [4]] ...
+ * math.subset(d, [[0, 1], [1]]) // Array [[2], [4]] ...
+ * math.subset(d, math.index([false, true], [0])) // Array [[3]] ...
+ * math.subset(d, [[false, true], 0]) // Array [3] ...
+ * math.subset(d, 1) // Array [3, 4]
*
* // replace a subset
* const e = []
- * const f = math.subset(e, math.index(0, [0, 2]), [5, 6]) // f = [[5, 0, 6]]
- * const g = math.subset(f, math.index(1, 1), 7, 0) // g = [[5, 0, 6], [0, 7, 0]]
- * math.subset(g, math.index([false, true], 1), 8) // returns [[5, 0, 6], [0, 8, 0]]
+ * const f = math.subset(e, math.index(0, [0, 2]), [5, 6])
+ * f // Array [[5, 0, 6]] ...
+ * const g = math.subset(f, math.index(1, 1), 7, 0)
+ * g // Array [[5, 0, 6], [0, 7, 0]] ...
+ * math.subset(g, math.index([false, true], 1), 8) // Array [[5, 0, 6], [0, 8, 0]]
*
* // get submatrix using ranges
* const M = [
@@ -36,7 +65,7 @@ export const createSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed
* [4, 5, 6],
* [7, 8, 9]
* ]
- * math.subset(M, math.index(math.range(0,2), math.range(0,3))) // [[1, 2, 3], [4, 5, 6]]
+ * math.subset(M, math.index(math.range(0,2), math.range(0,3))) // Array [[1, 2, 3], [4, 5, 6]]
*
* See also:
*
@@ -75,6 +104,23 @@ export const createSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed
'string, Index': _getSubstring,
+ // Allow single number index to get layer:
+ 'Matrix, number': function (M, position) {
+ return M.layer(position)
+ },
+
+ 'Array, number': function (A, position) {
+ return A[position]
+ },
+
+ 'string, number': function (s, position) {
+ return s.charAt(position)
+ },
+
+ // Otherwise pass second array argument to index function for convenience
+ 'Matrix | Array | Object | string, Array': typed.referToSelf(
+ self => (v, i) => self(v, index(...i))),
+
// set subset
'Matrix, Index, any, any': function (value, index, replacement, defaultValue) {
if (isEmptyIndex(index)) { return value }
@@ -89,19 +135,31 @@ export const createSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed
}
}),
- 'Array, Index, any': typed.referTo('Matrix, Index, any, any', function (subsetRef) {
- return function (value, index, replacement) {
- return subsetRef(matrix(value), index, replacement, undefined).valueOf()
- }
- }),
-
- 'Matrix, Index, any': typed.referTo('Matrix, Index, any, any', function (subsetRef) {
- return function (value, index, replacement) { return subsetRef(value, index, replacement, undefined) }
- }),
-
'string, Index, string': _setSubstring,
'string, Index, string, string': _setSubstring,
- 'Object, Index, any': _setObjectProperty
+ 'Object, Index, any': _setObjectProperty,
+
+ // fourth argument defaults to undefined:
+ 'Matrix | Array, Index | Array | number, any': typed.referToSelf(
+ self => (v, pos, rep) => self(v, pos, rep, undefined)
+ ),
+
+ // Allow 2nd index to be a number:
+ 'Matrix | Array | Object | string, number, any, any': typed.referToSelf(
+ self => (v, pos, rep, def) => {
+ const ix = [pos]
+ let wildcards = size(v).length
+ while (--wildcards > 0) ix.push(':')
+ return self(v, index(...ix), rep, def)
+ }),
+
+ 'string, number, string': typed.referTo(
+ 'string, Index, string', sis => (s, n, rep) => sis(s, index(n), rep)),
+
+ // Or allow 2nd argument to be an array of arguments to index
+ 'Matrix | Array | Object | string, Array, any, any': typed.referToSelf(
+ self => (v, ixes, rep, def) => self(v, index(...ixes), rep, def)
+ )
})
/**
diff --git a/src/function/matrix/zeros.js b/src/function/matrix/zeros.js
index 95f5d79129..11e78bc92f 100644
--- a/src/function/matrix/zeros.js
+++ b/src/function/matrix/zeros.js
@@ -4,9 +4,13 @@ import { resize } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'zeros'
-const dependencies = ['typed', 'config', 'matrix', 'BigNumber']
+const dependencies = [
+ 'typed', 'config', 'DenseMatrix', 'SparseMatrix', 'Range', 'BigNumber'
+]
-export const createZeros = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, matrix, BigNumber }) => {
+export const createZeros = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, config, DenseMatrix, SparseMatrix, Range, BigNumber
+}) => {
/**
* Create a matrix filled with zeros. The created matrix can have one or
* multiple dimensions.
@@ -26,6 +30,7 @@ export const createZeros = /* #__PURE__ */ factory(name, dependencies, ({ typed,
* math.zeros(3) // returns [0, 0, 0]
* math.zeros(3, 2) // returns [[0, 0], [0, 0], [0, 0]]
* math.zeros(3, 'dense') // returns [0, 0, 0]
+ * math.zeros(3, 'range') // returns new math.Range({start: 0, step: 0, length: 3})
*
* const A = [[1, 2, 3], [4, 5, 6]]
* math.zeros(math.size(A)) // returns [[0, 0, 0], [0, 0, 0]]
@@ -86,7 +91,24 @@ export const createZeros = /* #__PURE__ */ factory(name, dependencies, ({ typed,
if (format) {
// return a matrix
- const m = matrix(format)
+ if (format === 'range') {
+ if (size.length === 0) {
+ return new Range({ start: defaultValue, length: 0 })
+ }
+ if (size.length === 1) {
+ return new Range({
+ start: defaultValue,
+ length: size[0],
+ step: defaultValue
+ })
+ }
+ return new Range({
+ start: _zeros(size.slice(1), format),
+ length: size[0],
+ step: defaultValue
+ })
+ }
+ const m = format === 'sparse' ? new SparseMatrix() : new DenseMatrix()
if (size.length > 0) {
return m.resize(size, defaultValue)
}
diff --git a/src/function/probability/factorial.js b/src/function/probability/factorial.js
index 081f5e702d..8cd425f9a4 100644
--- a/src/function/probability/factorial.js
+++ b/src/function/probability/factorial.js
@@ -1,49 +1,128 @@
import { deepMap } from '../../utils/collection.js'
import { factory } from '../../utils/factory.js'
+import { product } from '../../utils/product.js'
+import { factorialNumber } from '../../plain/number/index.js'
const name = 'factorial'
-const dependencies = ['typed', 'gamma']
+const dependencies = ['typed', 'isInteger', '?BigNumber', 'equalScalar']
+
+export const createFactorial = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, isInteger, BigNumber, equalScalar
+}) => {
+ const bigMemo = BigNumber
+ ? [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
+ .map(n => new BigNumber(n))
+ : null
+ // Fortunately, the largest argument the factorial of which
+ // fits in a BigNumber is less than MAX_SAFE_INTEGER
+ // This value was derived, with gratitude, via
+ // [Hypercalc](https://mrob.com/pub/comp/hypercalc/hypercalc-javascript.html)
+ const MAX_BIGNUMBER_FACTORIAL = 626622622402120
-export const createFactorial = /* #__PURE__ */ factory(name, dependencies, ({ typed, gamma }) => {
/**
- * Compute the factorial of a value
+ * Compute the factorial of a value.
+ *
+ * The factorial of _n_ (which you can write as `n!` in the expression
+ * parser) is the product of all of the positive integers less than
+ * or equal to _n_, with a special case that `0!` is 1. As such, factorial
+ * only supports a nonnegative integer value (of any datatype) as its
+ * argument, and returns a result of the same datatype, except that the
+ * factorial of an (integer) Fraction is returned as a bigint and of a
+ * (real, integer) Complex number is returned as a plain number.
*
- * Factorial only supports an integer value as argument.
* For matrices, the function is evaluated element wise.
*
+ * **Variant factorials**
+ *
+ * For computing variations on the factorial, such as the "_n_ th rising
+ * factorial of _x_" equal to _x·(x+1)·...·(x+n-1)_, or the "_n_ th falling
+ * factorial of _x_" equal to _x·(x-1)·...·(x-(n-1))_, or the "double
+ * factorial of _n_" equal to _n·(n-2)·..._ (ending at 2 if _n_ is even or
+ * 1 if _n_ is odd), mathjs recommends you use the `prod` function on the
+ * arithmetic sequence of factors, generated with the `range` function.
+ * Explicitly, we have that
+ *
+ * - nth rising factorial of x is `math.prod(math.range(x, x + n))`, or in
+ * the expression parser (which has different conventions for specifying
+ * ranges) `prod(x:x+n-1)`
+ * - nth falling factorial of x is `math.prod(math.range(x, x - n, -1))` or
+ * in the expression parser either `prod(x:-1:x-n+1)` or `prod(x-n+1:x)`.
+ * - double factorial of n is `math.prod(math.range(n, 1, -2))` or in the
+ * expression parser `prod(n:-2:1)`.
+ *
+ * Of course, you can for example use `prod(1:n)` in the expression parser
+ * for `n!` but the `factorial` function has a specialized implementation
+ * that is approximately 33% faster for large, high-precision values.
+ *
* Syntax:
*
* math.factorial(n)
*
* Examples:
*
- * math.factorial(5) // returns 120
- * math.factorial(3) // returns 6
+ * math.factorial(5) // returns 120
+ * const big21 = math.bignumber(21)
+ * math.factorial(big21) // returns BigNumber 51090942171709440000
*
* See also:
*
- * combinations, combinationsWithRep, gamma, permutations
+ * combinations, combinationsWithRep, gamma, permutations, prod, range
*
- * @param {number | BigNumber | Array | Matrix} n An integer number
- * @return {number | BigNumber | Array | Matrix} The factorial of `n`
+ * @param {number | bigint | Fraction | Complex | BigNumber | Array | Matrix} n An integer number or matrix thereof
+ * @return {number | bigint | BigNumber | Array | Matrix}
+ * The (possibly elementwise) factorial of `n`
*/
return typed(name, {
- number: function (n) {
- if (n < 0) {
- throw new Error('Value must be non-negative')
+ number: factorialNumber,
+ bigint: function (b) {
+ if (b < 0n) {
+ throw new RangeError('factorial requires a nonnegative argument.')
}
-
- return gamma(n + 1)
+ return product(1n, b)
+ },
+ Fraction: function (f) {
+ if (f.s < 0 || !isInteger(f)) {
+ throw new RangeError('factorial requires nonnegative integer argument.')
+ }
+ return product(1n, f.n)
+ },
+ Complex: function (z) {
+ if (!equalScalar(z.re, z.re + z.im)) {
+ throw new RangeError('factorial requires nonnegative integer argument.')
+ }
+ return factorialNumber(z.re)
},
-
BigNumber: function (n) {
- if (n.isNegative()) {
- throw new Error('Value must be non-negative')
+ // When n overflows `number`, n! will overflow BigNumber
+ if (n.toNumber() === Infinity) return new BigNumber(Infinity)
+ if (n.isNegative() || !isInteger(n)) {
+ throw new RangeError('factorial requires nonnegative integer argument.')
}
-
- return gamma(n.plus(1))
+ return bigFactorial(n)
},
'Array | Matrix': typed.referToSelf(self => n => deepMap(n, self))
})
+
+ /* NB: only call this with a nonnegative integer */
+ function bigFactorial (n) {
+ if (n.lessThan(11)) return bigMemo[n.toNumber()]
+ if (n.greaterThan(MAX_BIGNUMBER_FACTORIAL)) return new BigNumber(Infinity)
+ if (n.modulo(2).isPositive()) return n.times(bigFactorial(n.minus(1)))
+
+ const Big = BigNumber.clone({
+ precision: BigNumber.precision + (Math.log(n.toNumber()) | 0)
+ })
+
+ let p = n.toNumber()
+ let prod = new Big(n)
+ let sum = n // can overflow MAX_SAFE_INTEGER for p < MAX_BIGNUMBER_FACTORIAL
+ while (p > 2) {
+ p -= 2
+ sum = sum.plus(p)
+ prod = prod.times(sum)
+ }
+
+ return new BigNumber(prod.toPrecision(BigNumber.precision))
+ }
})
diff --git a/src/function/probability/gamma.js b/src/function/probability/gamma.js
index 2fb37729b6..54445fbd02 100644
--- a/src/function/probability/gamma.js
+++ b/src/function/probability/gamma.js
@@ -2,9 +2,15 @@ import { factory } from '../../utils/factory.js'
import { gammaG, gammaNumber, gammaP } from '../../plain/number/index.js'
const name = 'gamma'
-const dependencies = ['typed', 'config', 'multiplyScalar', 'pow', 'BigNumber', 'Complex']
-
-export const createGamma = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, multiplyScalar, pow, BigNumber, Complex }) => {
+const dependencies = [
+ 'typed', 'config', 'BigNumber', 'Complex',
+ 'equalScalar', 'multiplyScalar', 'pow', 'factorial'
+]
+
+export const createGamma = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, config, BigNumber, Complex,
+ equalScalar, multiplyScalar, pow, factorial
+}) => {
/**
* Compute the gamma function of a value using Lanczos approximation for
* small values, and an extended Stirling approximation for large values.
@@ -31,9 +37,8 @@ export const createGamma = /* #__PURE__ */ factory(name, dependencies, ({ typed,
*/
function gammaComplex (n) {
- if (n.im === 0) {
- return gammaNumber(n.re)
- }
+ // Handle the "essentially real" case
+ if (equalScalar(n.re, n.re + n.im)) return gammaNumber(n.re)
// Lanczos approximation doesn't work well with real part lower than 0.5
// So reflection formula is required
@@ -77,9 +82,8 @@ export const createGamma = /* #__PURE__ */ factory(name, dependencies, ({ typed,
Complex: gammaComplex,
BigNumber: function (n) {
if (n.isInteger()) {
- return (n.isNegative() || n.isZero())
- ? new BigNumber(Infinity)
- : bigFactorial(n.minus(1))
+ if (n.isNegative() || n.isZero()) return new BigNumber(Infinity)
+ return factorial(n.minus(1))
}
if (!n.isFinite()) {
@@ -89,34 +93,4 @@ export const createGamma = /* #__PURE__ */ factory(name, dependencies, ({ typed,
throw new Error('Integer BigNumber expected')
}
})
-
- /**
- * Calculate factorial for a BigNumber
- * @param {BigNumber} n
- * @returns {BigNumber} Returns the factorial of n
- */
- function bigFactorial (n) {
- if (n < 8) {
- return new BigNumber([1, 1, 2, 6, 24, 120, 720, 5040][n])
- }
-
- const precision = config.precision + (Math.log(n.toNumber()) | 0)
- const Big = BigNumber.clone({ precision })
-
- if (n % 2 === 1) {
- return n.times(bigFactorial(new BigNumber(n - 1)))
- }
-
- let p = n
- let prod = new Big(n)
- let sum = n.toNumber()
-
- while (p > 2) {
- p -= 2
- sum += p
- prod = prod.times(sum)
- }
-
- return new BigNumber(prod.toPrecision(BigNumber.precision))
- }
})
diff --git a/src/function/relational/compare.js b/src/function/relational/compare.js
index 39886c3920..968df6f395 100644
--- a/src/function/relational/compare.js
+++ b/src/function/relational/compare.js
@@ -11,19 +11,17 @@ const name = 'compare'
const dependencies = [
'typed',
'config',
- 'matrix',
'equalScalar',
'BigNumber',
'Fraction',
- 'DenseMatrix',
- 'concat'
+ 'DenseMatrix'
]
-export const createCompare = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, equalScalar, matrix, BigNumber, Fraction, DenseMatrix, concat }) => {
+export const createCompare = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, equalScalar, BigNumber, Fraction, DenseMatrix }) => {
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo05xSfSf = createMatAlgo05xSfSf({ typed, equalScalar })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
const compareUnits = createCompareUnits({ typed })
/**
diff --git a/src/function/relational/compareText.js b/src/function/relational/compareText.js
index 310b55cd0e..c38d82e14a 100644
--- a/src/function/relational/compareText.js
+++ b/src/function/relational/compareText.js
@@ -3,16 +3,11 @@ import { factory } from '../../utils/factory.js'
import { createMatrixAlgorithmSuite } from '../../type/matrix/utils/matrixAlgorithmSuite.js'
const name = 'compareText'
-const dependencies = [
- 'typed',
- 'matrix',
- 'concat'
-]
-
+const dependencies = ['typed', 'DenseMatrix']
_compareText.signature = 'any, any'
-export const createCompareText = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, concat }) => {
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+export const createCompareText = /* #__PURE__ */ factory(name, dependencies, ({ typed, DenseMatrix }) => {
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Compare two strings lexically. Comparison is case sensitive.
diff --git a/src/function/relational/compareUnits.js b/src/function/relational/compareUnits.js
index 5b4937d26b..8830428c2b 100644
--- a/src/function/relational/compareUnits.js
+++ b/src/function/relational/compareUnits.js
@@ -1,12 +1,26 @@
import { factory } from '../../utils/factory.js'
export const createCompareUnits = /* #__PURE__ */ factory(
- 'compareUnits', ['typed'], ({ typed }) => ({
+ 'compareUnits', ['typed'], ({ typed, Unit }) => ({
'Unit, Unit': typed.referToSelf(self => (x, y) => {
if (!x.equalBase(y)) {
throw new Error('Cannot compare units with different base')
}
return typed.find(self, [x.valueType(), y.valueType()])(x.value, y.value)
- })
+ }),
+ 'Unit, number | bigint | BigNumber | Fraction | Complex': typed.referToSelf(
+ self => (x, y) => {
+ if (!x.unitless()) {
+ throw new Error('To compare Unit with pure numeric, must be unitless')
+ }
+ return self(x.value, y)
+ }),
+ 'number | bigint | BigNumber | Fraction | Complex, Unit': typed.referToSelf(
+ self => (x, y) => {
+ if (!y.unitless()) {
+ throw new Error('To compare Unit with pure numeric, must be unitless')
+ }
+ return self(x, y.value)
+ })
})
)
diff --git a/src/function/relational/deepEqual.js b/src/function/relational/deepEqual.js
index f693b8b6e9..ea6540bccb 100644
--- a/src/function/relational/deepEqual.js
+++ b/src/function/relational/deepEqual.js
@@ -1,5 +1,4 @@
import { factory } from '../../utils/factory.js'
-
const name = 'deepEqual'
const dependencies = [
'typed',
@@ -37,9 +36,11 @@ export const createDeepEqual = /* #__PURE__ */ factory(name, dependencies, ({ ty
* Returns true when the input matrices have the same size and each of their elements is equal.
*/
return typed(name, {
- 'any, any': function (x, y) {
- return _deepEqual(x.valueOf(), y.valueOf())
- }
+ // Don't want to execute .valueOf() on a unit, as it returns a string,
+ // messing up the equality test
+ 'Unit, any': (u, x) => equal(u, x),
+ 'any, Unit': (x, u) => equal(x, u),
+ 'any, any': (x, y) => _deepEqual(x.valueOf(), y.valueOf())
})
/**
diff --git a/src/function/relational/equal.js b/src/function/relational/equal.js
index a4e1031b68..66bc660a4d 100644
--- a/src/function/relational/equal.js
+++ b/src/function/relational/equal.js
@@ -7,18 +7,16 @@ import { createMatrixAlgorithmSuite } from '../../type/matrix/utils/matrixAlgori
const name = 'equal'
const dependencies = [
'typed',
- 'matrix',
'equalScalar',
'DenseMatrix',
'SparseMatrix'
]
-export const createEqual = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, DenseMatrix, concat, SparseMatrix }) => {
+export const createEqual = /* #__PURE__ */ factory(name, dependencies, ({ typed, equalScalar, DenseMatrix, concat, SparseMatrix }) => {
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo07xSSf = createMatAlgo07xSSf({ typed, SparseMatrix })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix })
-
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Test whether two values are equal.
*
diff --git a/src/function/relational/equalScalar.js b/src/function/relational/equalScalar.js
index eea6881e4d..835c223cdb 100644
--- a/src/function/relational/equalScalar.js
+++ b/src/function/relational/equalScalar.js
@@ -7,14 +7,14 @@ import { createCompareUnits } from './compareUnits.js'
const name = 'equalScalar'
const dependencies = ['typed', 'config']
-export const createEqualScalar = /* #__PURE__ */ factory(name, dependencies, ({ typed, config }) => {
+export const createEqualScalar = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, Unit }) => {
const compareUnits = createCompareUnits({ typed })
/**
* Test whether two scalar values are nearly equal.
*
* @param {number | BigNumber | bigint | Fraction | boolean | Complex | Unit} x First value to compare
- * @param {number | BigNumber | bigint | Fraction | boolean | Complex} y Second value to compare
+ * @param {number | BigNumber | bigint | Fraction | boolean | Complex | Unit} y Second value to compare
* @return {boolean} Returns true when the compared values are equal, else returns false
* @private
*/
diff --git a/src/function/relational/larger.js b/src/function/relational/larger.js
index 8507d21511..131d054e25 100644
--- a/src/function/relational/larger.js
+++ b/src/function/relational/larger.js
@@ -12,9 +12,7 @@ const dependencies = [
'typed',
'config',
'bignumber',
- 'matrix',
'DenseMatrix',
- 'concat',
'SparseMatrix'
]
@@ -22,7 +20,7 @@ export const createLarger = /* #__PURE__ */ factory(name, dependencies, ({ typed
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo07xSSf = createMatAlgo07xSSf({ typed, SparseMatrix })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
const compareUnits = createCompareUnits({ typed })
/**
diff --git a/src/function/relational/largerEq.js b/src/function/relational/largerEq.js
index 6760df7f6c..78b353dfd7 100644
--- a/src/function/relational/largerEq.js
+++ b/src/function/relational/largerEq.js
@@ -11,9 +11,7 @@ const name = 'largerEq'
const dependencies = [
'typed',
'config',
- 'matrix',
'DenseMatrix',
- 'concat',
'SparseMatrix'
]
@@ -21,7 +19,7 @@ export const createLargerEq = /* #__PURE__ */ factory(name, dependencies, ({ typ
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo07xSSf = createMatAlgo07xSSf({ typed, SparseMatrix })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
const compareUnits = createCompareUnits({ typed })
/**
diff --git a/src/function/relational/smaller.js b/src/function/relational/smaller.js
index 87807f0240..912dd95db3 100644
--- a/src/function/relational/smaller.js
+++ b/src/function/relational/smaller.js
@@ -12,17 +12,16 @@ const dependencies = [
'typed',
'config',
'bignumber',
- 'matrix',
'DenseMatrix',
'concat',
'SparseMatrix'
]
-export const createSmaller = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, bignumber, matrix, DenseMatrix, concat, SparseMatrix }) => {
+export const createSmaller = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, bignumber, DenseMatrix, concat, SparseMatrix }) => {
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo07xSSf = createMatAlgo07xSSf({ typed, SparseMatrix })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
const compareUnits = createCompareUnits({ typed })
/**
diff --git a/src/function/relational/smallerEq.js b/src/function/relational/smallerEq.js
index b314418a4c..272ed22303 100644
--- a/src/function/relational/smallerEq.js
+++ b/src/function/relational/smallerEq.js
@@ -11,7 +11,6 @@ const name = 'smallerEq'
const dependencies = [
'typed',
'config',
- 'matrix',
'DenseMatrix',
'concat',
'SparseMatrix'
@@ -21,7 +20,7 @@ export const createSmallerEq = /* #__PURE__ */ factory(name, dependencies, ({ ty
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo07xSSf = createMatAlgo07xSSf({ typed, SparseMatrix })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
const compareUnits = createCompareUnits({ typed })
/**
diff --git a/src/function/relational/unequal.js b/src/function/relational/unequal.js
index 1accaf846b..b89345d3b2 100644
--- a/src/function/relational/unequal.js
+++ b/src/function/relational/unequal.js
@@ -9,9 +9,7 @@ const dependencies = [
'typed',
'config',
'equalScalar',
- 'matrix',
'DenseMatrix',
- 'concat',
'SparseMatrix'
]
@@ -19,7 +17,7 @@ export const createUnequal = /* #__PURE__ */ factory(name, dependencies, ({ type
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo07xSSf = createMatAlgo07xSSf({ typed, SparseMatrix })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Test whether two values are unequal.
diff --git a/src/function/set/setCartesian.js b/src/function/set/setCartesian.js
index 1f259e7a5b..b901284370 100644
--- a/src/function/set/setCartesian.js
+++ b/src/function/set/setCartesian.js
@@ -2,9 +2,9 @@ import { flatten } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'setCartesian'
-const dependencies = ['typed', 'size', 'subset', 'compareNatural', 'Index', 'DenseMatrix']
+const dependencies = ['typed', 'size', 'compareNatural']
-export const createSetCartesian = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, subset, compareNatural, Index, DenseMatrix }) => {
+export const createSetCartesian = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, compareNatural }) => {
/**
* Create the cartesian product of two (multi)sets.
* Multi-dimension arrays will be converted to single-dimension arrays
@@ -30,10 +30,10 @@ export const createSetCartesian = /* #__PURE__ */ factory(name, dependencies, ({
return typed(name, {
'Array | Matrix, Array | Matrix': function (a1, a2) {
let result = []
-
- if (subset(size(a1), new Index(0)) !== 0 && subset(size(a2), new Index(0)) !== 0) { // if any of them is empty, return empty
- const b1 = flatten(Array.isArray(a1) ? a1 : a1.toArray()).sort(compareNatural)
- const b2 = flatten(Array.isArray(a2) ? a2 : a2.toArray()).sort(compareNatural)
+ // if either is empty, return empty
+ if (size(a1)[0] !== 0 && size(a2)[0] !== 0) {
+ const b1 = flatten(a1.valueOf()).sort(compareNatural)
+ const b2 = flatten(a2.valueOf()).sort(compareNatural)
result = []
for (let i = 0; i < b1.length; i++) {
for (let j = 0; j < b2.length; j++) {
@@ -42,11 +42,11 @@ export const createSetCartesian = /* #__PURE__ */ factory(name, dependencies, ({
}
}
// return an array, if both inputs were arrays
- if (Array.isArray(a1) && Array.isArray(a2)) {
- return result
+ if (Array.isArray(a1)) {
+ if (Array.isArray(a2)) return result
+ return a2.create(result)
}
- // return a matrix otherwise
- return new DenseMatrix(result)
+ return a1.create(result)
}
})
})
diff --git a/src/function/set/setDifference.js b/src/function/set/setDifference.js
index 872c369e95..9dbde4cc50 100644
--- a/src/function/set/setDifference.js
+++ b/src/function/set/setDifference.js
@@ -2,9 +2,9 @@ import { flatten, generalize, identify } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'setDifference'
-const dependencies = ['typed', 'size', 'subset', 'compareNatural', 'Index', 'DenseMatrix']
+const dependencies = ['typed', 'size', 'compareNatural']
-export const createSetDifference = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, subset, compareNatural, Index, DenseMatrix }) => {
+export const createSetDifference = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, compareNatural }) => {
/**
* Create the difference of two (multi)sets: every element of set1, that is not the element of set2.
* Multi-dimension arrays will be converted to single-dimension arrays before the operation.
@@ -28,15 +28,14 @@ export const createSetDifference = /* #__PURE__ */ factory(name, dependencies, (
*/
return typed(name, {
'Array | Matrix, Array | Matrix': function (a1, a2) {
- let result
- if (subset(size(a1), new Index(0)) === 0) { // empty-anything=empty
- result = []
- } else if (subset(size(a2), new Index(0)) === 0) { // anything-empty=anything
- return flatten(a1.toArray())
- } else {
- const b1 = identify(flatten(Array.isArray(a1) ? a1 : a1.toArray()).sort(compareNatural))
- const b2 = identify(flatten(Array.isArray(a2) ? a2 : a2.toArray()).sort(compareNatural))
- result = []
+ let result = []
+ // empty - anything = empty
+ if (size(a1)[0] !== 0) {
+ if (size(a2)[0] === 0) { // anything - empty = anything
+ return flatten(a1.valueOf())
+ }
+ const b1 = identify(flatten(a1.valueOf()).sort(compareNatural))
+ const b2 = identify(flatten(a2.valueOf()).sort(compareNatural))
let inb2
for (let i = 0; i < b1.length; i++) {
inb2 = false
@@ -51,12 +50,13 @@ export const createSetDifference = /* #__PURE__ */ factory(name, dependencies, (
}
}
}
+ result = generalize(result) // remove the identifiers
// return an array, if both inputs were arrays
- if (Array.isArray(a1) && Array.isArray(a2)) {
- return generalize(result)
+ if (Array.isArray(a1)) {
+ if (Array.isArray(a2)) return result
+ return a2.create(result)
}
- // return a matrix otherwise
- return new DenseMatrix(generalize(result))
+ return a1.create(result)
}
})
})
diff --git a/src/function/set/setDistinct.js b/src/function/set/setDistinct.js
index 231ff76554..f9cbb6b9a5 100644
--- a/src/function/set/setDistinct.js
+++ b/src/function/set/setDistinct.js
@@ -2,9 +2,9 @@ import { flatten } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'setDistinct'
-const dependencies = ['typed', 'size', 'subset', 'compareNatural', 'Index', 'DenseMatrix']
+const dependencies = ['typed', 'size', 'compareNatural', 'Index', 'DenseMatrix']
-export const createSetDistinct = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, subset, compareNatural, Index, DenseMatrix }) => {
+export const createSetDistinct = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, compareNatural }) => {
/**
* Collect the distinct elements of a multiset.
* A multi-dimension array will be converted to a single-dimension array before the operation.
@@ -27,12 +27,10 @@ export const createSetDistinct = /* #__PURE__ */ factory(name, dependencies, ({
*/
return typed(name, {
'Array | Matrix': function (a) {
- let result
- if (subset(size(a), new Index(0)) === 0) { // if empty, return empty
- result = []
- } else {
- const b = flatten(Array.isArray(a) ? a : a.toArray())
- result = []
+ const result = []
+ // if empty, return empty
+ if (size(a)[0] !== 0) {
+ const b = flatten(a.valueOf())
result.push(b[0])
for (let i = 1; i < b.length; i++) {
if (!result.some(item => compareNatural(b[i], item) === 0)) {
@@ -45,7 +43,7 @@ export const createSetDistinct = /* #__PURE__ */ factory(name, dependencies, ({
return result
}
// return a matrix otherwise
- return new DenseMatrix(result)
+ return a.create(result)
}
})
})
diff --git a/src/function/set/setIntersect.js b/src/function/set/setIntersect.js
index 4f2845e507..1db76000dd 100644
--- a/src/function/set/setIntersect.js
+++ b/src/function/set/setIntersect.js
@@ -2,9 +2,9 @@ import { flatten, generalize, identify } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'setIntersect'
-const dependencies = ['typed', 'size', 'subset', 'compareNatural', 'Index', 'DenseMatrix']
+const dependencies = ['typed', 'size', 'compareNatural']
-export const createSetIntersect = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, subset, compareNatural, Index, DenseMatrix }) => {
+export const createSetIntersect = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, compareNatural }) => {
/**
* Create the intersection of two (multi)sets.
* Multi-dimension arrays will be converted to single-dimension arrays before the operation.
@@ -28,13 +28,11 @@ export const createSetIntersect = /* #__PURE__ */ factory(name, dependencies, ({
*/
return typed(name, {
'Array | Matrix, Array | Matrix': function (a1, a2) {
- let result
- if (subset(size(a1), new Index(0)) === 0 || subset(size(a2), new Index(0)) === 0) { // of any of them is empty, return empty
- result = []
- } else {
- const b1 = identify(flatten(Array.isArray(a1) ? a1 : a1.toArray()).sort(compareNatural))
- const b2 = identify(flatten(Array.isArray(a2) ? a2 : a2.toArray()).sort(compareNatural))
- result = []
+ let result = []
+ // if both are nonempty, we must compute the intersection
+ if (size(a1)[0] !== 0 && size(a2)[0] !== 0) {
+ const b1 = identify(flatten(a1.valueOf()).sort(compareNatural))
+ const b2 = identify(flatten(a2.valueOf()).sort(compareNatural))
for (let i = 0; i < b1.length; i++) {
for (let j = 0; j < b2.length; j++) {
if (compareNatural(b1[i].value, b2[j].value) === 0 && b1[i].identifier === b2[j].identifier) { // the identifier is always a decimal int
@@ -44,12 +42,13 @@ export const createSetIntersect = /* #__PURE__ */ factory(name, dependencies, ({
}
}
}
+ result = generalize(result) // remove the identifiers
// return an array, if both inputs were arrays
- if (Array.isArray(a1) && Array.isArray(a2)) {
- return generalize(result)
+ if (Array.isArray(a1)) {
+ if (Array.isArray(a2)) return result
+ return a2.create(result)
}
- // return a matrix otherwise
- return new DenseMatrix(generalize(result))
+ return a1.create(result)
}
})
})
diff --git a/src/function/set/setIsSubset.js b/src/function/set/setIsSubset.js
index d2a8c72217..20e5fa912c 100644
--- a/src/function/set/setIsSubset.js
+++ b/src/function/set/setIsSubset.js
@@ -2,9 +2,9 @@ import { flatten, identify } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'setIsSubset'
-const dependencies = ['typed', 'size', 'subset', 'compareNatural', 'Index']
+const dependencies = ['typed', 'size', 'compareNatural']
-export const createSetIsSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, subset, compareNatural, Index }) => {
+export const createSetIsSubset = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, compareNatural }) => {
/**
* Check whether a (multi)set is a subset of another (multi)set. (Every element of set1 is the element of set2.)
* Multi-dimension arrays will be converted to single-dimension arrays before the operation.
@@ -28,9 +28,10 @@ export const createSetIsSubset = /* #__PURE__ */ factory(name, dependencies, ({
*/
return typed(name, {
'Array | Matrix, Array | Matrix': function (a1, a2) {
- if (subset(size(a1), new Index(0)) === 0) { // empty is a subset of anything
+ if (size(a1)[0] === 0) { // empty is a subset of anything
return true
- } else if (subset(size(a2), new Index(0)) === 0) { // anything is not a subset of empty
+ }
+ if (size(a2)[0] === 0) { // anything nonempty is not a subset of empty
return false
}
const b1 = identify(flatten(Array.isArray(a1) ? a1 : a1.toArray()).sort(compareNatural))
diff --git a/src/function/set/setMultiplicity.js b/src/function/set/setMultiplicity.js
index 7935ac210e..af012c1600 100644
--- a/src/function/set/setMultiplicity.js
+++ b/src/function/set/setMultiplicity.js
@@ -2,9 +2,9 @@ import { flatten } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'setMultiplicity'
-const dependencies = ['typed', 'size', 'subset', 'compareNatural', 'Index']
+const dependencies = ['typed', 'size', 'compareNatural']
-export const createSetMultiplicity = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, subset, compareNatural, Index }) => {
+export const createSetMultiplicity = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, compareNatural }) => {
/**
* Count the multiplicity of an element in a multiset.
* A multi-dimension array will be converted to a single-dimension array before the operation.
@@ -28,10 +28,10 @@ export const createSetMultiplicity = /* #__PURE__ */ factory(name, dependencies,
*/
return typed(name, {
'number | BigNumber | Fraction | Complex, Array | Matrix': function (e, a) {
- if (subset(size(a), new Index(0)) === 0) { // if empty, return 0
+ if (size(a)[0] === 0) { // if empty, return 0
return 0
}
- const b = flatten(Array.isArray(a) ? a : a.toArray())
+ const b = flatten(a.valueOf())
let count = 0
for (let i = 0; i < b.length; i++) {
if (compareNatural(b[i], e) === 0) {
diff --git a/src/function/set/setPowerset.js b/src/function/set/setPowerset.js
index 9737854e0a..5201ac54e9 100644
--- a/src/function/set/setPowerset.js
+++ b/src/function/set/setPowerset.js
@@ -2,12 +2,14 @@ import { flatten } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'setPowerset'
-const dependencies = ['typed', 'size', 'subset', 'compareNatural', 'Index']
+const dependencies = ['typed', 'size', 'compareNatural']
-export const createSetPowerset = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, subset, compareNatural, Index }) => {
+export const createSetPowerset = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, compareNatural }) => {
/**
* Create the powerset of a (multi)set. (The powerset contains very possible subsets of a (multi)set.)
* A multi-dimension array will be converted to a single-dimension array before the operation.
+ * Note this function always returns an Array, regardless of the type
+ * of the input collection representing the set.
*
* Syntax:
*
@@ -26,10 +28,10 @@ export const createSetPowerset = /* #__PURE__ */ factory(name, dependencies, ({
*/
return typed(name, {
'Array | Matrix': function (a) {
- if (subset(size(a), new Index(0)) === 0) { // if empty, return empty
+ if (size(a)[0] === 0) { // if empty, return empty
return []
}
- const b = flatten(Array.isArray(a) ? a : a.toArray()).sort(compareNatural)
+ const b = flatten(a.valueOf()).sort(compareNatural)
const result = []
let number = 0
while (number.toString(2).length <= b.length) {
diff --git a/src/function/set/setSymDifference.js b/src/function/set/setSymDifference.js
index 812cfbc586..1eb88d2ebd 100644
--- a/src/function/set/setSymDifference.js
+++ b/src/function/set/setSymDifference.js
@@ -2,9 +2,9 @@ import { flatten } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'setSymDifference'
-const dependencies = ['typed', 'size', 'concat', 'subset', 'setDifference', 'Index']
+const dependencies = ['typed', 'size', 'concat', 'setDifference']
-export const createSetSymDifference = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, concat, subset, setDifference, Index }) => {
+export const createSetSymDifference = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, concat, setDifference }) => {
/**
* Create the symmetric difference of two (multi)sets.
* Multi-dimension arrays will be converted to single-dimension arrays before the operation.
@@ -28,9 +28,10 @@ export const createSetSymDifference = /* #__PURE__ */ factory(name, dependencies
*/
return typed(name, {
'Array | Matrix, Array | Matrix': function (a1, a2) {
- if (subset(size(a1), new Index(0)) === 0) { // if any of them is empty, return the other one
+ if (size(a1)[0] === 0) { // if either is empty, return the other
return flatten(a2)
- } else if (subset(size(a2), new Index(0)) === 0) {
+ }
+ if (size(a2)[0] === 0) {
return flatten(a1)
}
const b1 = flatten(a1)
diff --git a/src/function/set/setUnion.js b/src/function/set/setUnion.js
index ce53bea081..db9a2caabf 100644
--- a/src/function/set/setUnion.js
+++ b/src/function/set/setUnion.js
@@ -2,9 +2,9 @@ import { flatten } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
const name = 'setUnion'
-const dependencies = ['typed', 'size', 'concat', 'subset', 'setIntersect', 'setSymDifference', 'Index']
+const dependencies = ['typed', 'size', 'concat', 'setIntersect', 'setSymDifference']
-export const createSetUnion = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, concat, subset, setIntersect, setSymDifference, Index }) => {
+export const createSetUnion = /* #__PURE__ */ factory(name, dependencies, ({ typed, size, concat, setIntersect, setSymDifference }) => {
/**
* Create the union of two (multi)sets.
* Multi-dimension arrays will be converted to single-dimension arrays before the operation.
@@ -15,8 +15,8 @@ export const createSetUnion = /* #__PURE__ */ factory(name, dependencies, ({ typ
*
* Examples:
*
- * math.setUnion([1, 2, 3, 4], [3, 4, 5, 6]) // returns [1, 2, 3, 4, 5, 6]
- * math.setUnion([[1, 2], [3, 4]], [[3, 4], [5, 6]]) // returns [1, 2, 3, 4, 5, 6]
+ * math.sort(math.setUnion([1, 2, 3, 4], [3, 4, 5, 6])) // returns [1, 2, 3, 4, 5, 6]
+ * math.sort(math.setUnion([[1, 2], [3, 4]], [[3, 4], [5, 6]])) // returns [1, 2, 3, 4, 5, 6]
*
* See also:
*
@@ -28,9 +28,10 @@ export const createSetUnion = /* #__PURE__ */ factory(name, dependencies, ({ typ
*/
return typed(name, {
'Array | Matrix, Array | Matrix': function (a1, a2) {
- if (subset(size(a1), new Index(0)) === 0) { // if any of them is empty, return the other one
+ if (size(a1)[0] === 0) { // if either is empty, return the other
return flatten(a2)
- } else if (subset(size(a2), new Index(0)) === 0) {
+ }
+ if (size(a2)[0] === 0) {
return flatten(a1)
}
const b1 = flatten(a1)
diff --git a/src/function/statistics/prod.js b/src/function/statistics/prod.js
index 1a5ba84072..b83ee928e0 100644
--- a/src/function/statistics/prod.js
+++ b/src/function/statistics/prod.js
@@ -1,16 +1,29 @@
-import { deepForEach } from '../../utils/collection.js'
import { factory } from '../../utils/factory.js'
+import { isArray, isNumber, isRange } from '../../utils/is.js'
import { safeNumberType } from '../../utils/number.js'
import { improveErrorMessage } from './utils/improveErrorMessage.js'
const name = 'prod'
-const dependencies = ['typed', 'config', 'multiplyScalar', 'numeric']
+const dependencies = [
+ 'typed', 'config', 'multiplyScalar', 'number', 'numeric',
+ 'squeeze', 'size', 'subset', 'dotMultiply'
+]
-export const createProd = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, multiplyScalar, numeric }) => {
+// Two tuning constants. For products of at least THRESHOLD terms, we split
+// rather than directly multiply. And for products of Ranges of at least
+// RANGE_THRESHOLD + 1 terms, we multiply in pairs rather than singly.
+const THRESHOLD = 16
+const RANGE_THRESHOLD = 5
+
+export const createProd = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, config, multiplyScalar, number, numeric,
+ squeeze, size, subset, dotMultiply
+}) => {
/**
* Compute the product of a matrix or a list with values.
- * In case of a multidimensional array or matrix, the sum of all
- * elements will be calculated.
+ * In case of a multidimensional array or matrix, product of all
+ * elements will be calculated, unless a second integer argument is given
+ * that specifies the dimension along which to multiply.
*
* Syntax:
*
@@ -37,11 +50,7 @@ export const createProd = /* #__PURE__ */ factory(name, dependencies, ({ typed,
'Array | Matrix': _prod,
// prod([a, b, c, d, ...], dim)
- 'Array | Matrix, number | BigNumber': function (array, dim) {
- // TODO: implement prod(A, dim)
- throw new Error('prod(A, dim) is not yet supported')
- // return reduce(arguments[0], arguments[1], math.prod)
- },
+ 'Array | Matrix, number | BigNumber': _prodAlongDim,
// prod(a, b, c, d, ...)
'...': function (args) {
@@ -51,30 +60,125 @@ export const createProd = /* #__PURE__ */ factory(name, dependencies, ({ typed,
/**
* Recursively calculate the product of an n-dimensional array
- * @param {Array} array
- * @return {number} prod
+ * @param {Array | Matrix} collection
+ * @return {scalar} prod
* @private
*/
- function _prod (array) {
+ function _prod (collection) {
+ let sz = size(collection)
+ if (sz.length === 0 || sz.some(dim => dim === 0)) return 1
let prod
+ try {
+ if (sz.every(dim => dim === 1)) prod = squeeze(collection)
+ else {
+ if (sz.length > 1) { // reduce to 1d
+ const newColl = []
+ for (let pos = 0; pos < sz[0]; ++pos) {
+ newColl.push(_prod(subset(collection, pos)))
+ }
+ collection = newColl
+ sz = [sz[0]]
+ }
+ if (Array.isArray(collection)) {
+ prod = _prodArray(collection, 0, sz[0] - 1)
+ } else {
+ let op = multiplyScalar
+ const dt = collection.datatype()
+ if (dt) op = typed.find(op, [dt, dt])
+ prod = _prodVector(collection, 0, sz[0] - 1, op)
+ }
+ }
- deepForEach(array, function (value) {
- try {
- prod = (prod === undefined) ? value : multiplyScalar(prod, value)
- } catch (err) {
- throw improveErrorMessage(err, 'prod', value)
+ if (typeof prod === 'string') {
+ prod = numeric(prod, safeNumberType(prod, config))
}
- })
+ } catch (err) {
+ throw improveErrorMessage(err, 'prod', collection)
+ }
+ return prod
+ }
- // make sure returning numeric value: parse a string into a numeric value
- if (typeof prod === 'string') {
- prod = numeric(prod, safeNumberType(prod, config))
+ /* Product of a 1d array arr from index first to index last, inclusive. */
+ function _prodArray (arr, first, last) {
+ if (last - first < THRESHOLD) {
+ let prod = arr[first]
+ for (let idx = first + 1; idx <= last; ++idx) {
+ prod = multiplyScalar(prod, arr[idx])
+ }
+ return prod
}
+ const cutoff = Math.floor((first + last) / 2)
+ return multiplyScalar(
+ _prodArray(arr, first, cutoff),
+ _prodArray(arr, cutoff + 1, last))
+ }
- if (prod === undefined) {
- throw new Error('Cannot calculate prod of an empty array')
+ /* Product of a 1d vector v from position first to last, using op */
+ function _prodVector (v, first, last, op) {
+ const lenm1 = last - first
+ if (lenm1 < THRESHOLD) {
+ if (isRange(v) && lenm1 >= RANGE_THRESHOLD) {
+ return _prodRange(v, first, last, op)
+ }
+ let prod = v.layer(first)
+ for (let idx = first + 1; idx <= last; ++idx) {
+ prod = op(prod, v.layer(idx))
+ }
+ return prod
}
+ const cutoff = Math.floor((first + last) / 2)
+ return op(
+ _prodVector(v, first, cutoff, op), _prodVector(v, cutoff + 1, last, op))
+ }
- return prod
+ /* Product of a 1d Range r from position first to last, using op and the
+ "multiply in pairs" trick.
+ */
+ function _prodRange (r, first, last, op) {
+ const a = r.layer(first)
+ const d = r.step
+ const m = last - first // m+1 terms
+ const even = m % 2
+ const pairs = even ? (m + 1) / 2 : m / 2
+ const b = even ? a : r.plus(a, d)
+ const k = even ? m : m - 1 // k is always an odd number
+ // So now the pairs are b*(b+kd), (b+1d)*(b+(k-1)d), (b+2d)*(b+(k-2)d), ...
+ // up to (b+(pairs-1)d)*(b+(k-pairs+1)d). Their products are all
+ // (b^2 + kbd + something), where the somethings go (k-1)d^2, 2(k-2)d^2,
+ // 3(k-3)d, ... with differences (k-3)d^2, (k-5)d^2, (k-7)d^2, ...
+ let pair = op(b, r.layer(last)) // b^2 + kbd
+ let prod = pair
+ const dsq = op(d, d)
+ let pairIncrement = r.times(k - 1, dsq)
+ const pairIncDec = r.times(-2, dsq)
+ for (let j = 1; j < pairs; ++j) {
+ pair = r.plus(pair, pairIncrement)
+ pairIncrement = r.plus(pairIncrement, pairIncDec)
+ prod = op(prod, pair)
+ }
+ return even ? prod : op(a, prod)
+ }
+
+ function _prodAlongDim (collection, dim) {
+ if (!isNumber(dim)) dim = number(dim)
+ const sz = size(collection)
+ if (dim >= sz.length) {
+ throw new Error(
+ `There is no dimension ${dim} in collection of size ${sz}.`)
+ }
+ if (sz.length === 1) return _prod(collection)
+ if (dim === 0) {
+ let result = subset(collection, 0)
+ for (let i = 1; i < sz[0]; ++i) {
+ result = dotMultiply(result, subset(collection, i))
+ }
+ return result
+ }
+ const data = []
+ for (let i = 0; i < sz[0]; ++i) {
+ data.push(_prodAlongDim(subset(collection, i), dim - 1).valueOf())
+ }
+ if (isArray(collection)) return data
+ return collection.create(data)
}
})
diff --git a/src/function/statistics/quantileSeq.js b/src/function/statistics/quantileSeq.js
index 4f79af1df3..b4d4bb5b9d 100644
--- a/src/function/statistics/quantileSeq.js
+++ b/src/function/statistics/quantileSeq.js
@@ -8,12 +8,14 @@ const dependencies = ['typed', '?bignumber', 'add', 'subtract', 'divide', 'multi
export const createQuantileSeq = /* #__PURE__ */ factory(name, dependencies, ({ typed, bignumber, add, subtract, divide, multiply, partitionSelect, compare, isInteger, smaller, smallerEq, larger, mapSlices }) => {
/**
* Compute the prob order quantile of a matrix or a list with values.
- * The sequence is sorted and the middle value is returned.
+ * The sequence is sorted and the quntile value(s) are returned.
* Supported types of sequence values are: Number, BigNumber, Unit
* Supported types of probability are: Number, BigNumber
*
* In case of a multidimensional array or matrix, the prob order quantile
- * of all elements will be calculated.
+ * of all elements will be calculated, unless the additional "dimension"
+ * argument (a number) is specified, in which case the quantiles are
+ * computed for all slices along that dimension.
*
* Syntax:
*
diff --git a/src/function/trigonometry/atan2.js b/src/function/trigonometry/atan2.js
index ec0eb544d3..995952e9b8 100644
--- a/src/function/trigonometry/atan2.js
+++ b/src/function/trigonometry/atan2.js
@@ -9,20 +9,18 @@ import { createMatrixAlgorithmSuite } from '../../type/matrix/utils/matrixAlgori
const name = 'atan2'
const dependencies = [
'typed',
- 'matrix',
'equalScalar',
'BigNumber',
- 'DenseMatrix',
- 'concat'
+ 'DenseMatrix'
]
-export const createAtan2 = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, equalScalar, BigNumber, DenseMatrix, concat }) => {
+export const createAtan2 = /* #__PURE__ */ factory(name, dependencies, ({ typed, equalScalar, BigNumber, DenseMatrix }) => {
const matAlgo02xDS0 = createMatAlgo02xDS0({ typed, equalScalar })
const matAlgo03xDSf = createMatAlgo03xDSf({ typed })
const matAlgo09xS0Sf = createMatAlgo09xS0Sf({ typed, equalScalar })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo12xSfs = createMatAlgo12xSfs({ typed, DenseMatrix })
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Calculate the inverse tangent function with two arguments, y/x.
diff --git a/src/function/unit/to.js b/src/function/unit/to.js
index e0705a5bd1..4140ab64c6 100644
--- a/src/function/unit/to.js
+++ b/src/function/unit/to.js
@@ -4,12 +4,11 @@ import { createMatrixAlgorithmSuite } from '../../type/matrix/utils/matrixAlgori
const name = 'to'
const dependencies = [
'typed',
- 'matrix',
- 'concat'
+ 'DenseMatrix'
]
-export const createTo = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, concat }) => {
- const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
+export const createTo = /* #__PURE__ */ factory(name, dependencies, ({ typed, DenseMatrix }) => {
+ const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, DenseMatrix })
/**
* Change the unit of a value.
diff --git a/src/function/utils/isFinite.js b/src/function/utils/isFinite.js
index 57dd2b10f0..5bf0ee0354 100644
--- a/src/function/utils/isFinite.js
+++ b/src/function/utils/isFinite.js
@@ -34,7 +34,7 @@ export const createIsFinite = /* #__PURE__ */ factory(name, dependencies, ({
* isBounded isNumeric, isPositive, isNegative, isNaN
*
* @param {number | BigNumber | bigint | Complex | Fraction | Unit | Array | Matrix} x Value to be tested
- * @return {boolean | Array | Matrix}
+ * @return {boolean | Array | Matrix} Whether the value, or each entry, is finite.
*/
return typed(name, {
'Array | Matrix': A => map(A, isBounded),
diff --git a/src/plain/number/arithmetic.js b/src/plain/number/arithmetic.js
index bb006d4f9a..fcc1025351 100644
--- a/src/plain/number/arithmetic.js
+++ b/src/plain/number/arithmetic.js
@@ -23,6 +23,14 @@ export function multiplyNumber (a, b) {
}
multiplyNumber.signature = n2
+/* Multiply every entry of A by the number n */
+export function deepMultiply (n, A) {
+ return A.map(item => {
+ if (Array.isArray(item)) return deepMultiply(n, item)
+ return n * item
+ })
+}
+
export function divideNumber (a, b) {
return a / b
}
diff --git a/src/plain/number/probability.js b/src/plain/number/probability.js
index fb15bd3f35..6c62168ef4 100644
--- a/src/plain/number/probability.js
+++ b/src/plain/number/probability.js
@@ -3,19 +3,19 @@
import { isInteger } from '../../utils/number.js'
import { product } from '../../utils/product.js'
-export function gammaNumber (n) {
- let x
+export function factorialNumber (n) {
+ if (n === Infinity) return Infinity
+ if (n < 0 || !isInteger(n)) {
+ throw new RangeError('factorial requires nonnegative integer argument.')
+ }
+ if (n > 171) return Infinity // will overflow
+ return product(1, n)
+}
+export function gammaNumber (n) {
if (isInteger(n)) {
- if (n <= 0) {
- return Number.isFinite(n) ? Infinity : NaN
- }
-
- if (n > 171) {
- return Infinity // Will overflow
- }
-
- return product(1, n - 1)
+ if (n <= 0) return Number.isFinite(n) ? Infinity : NaN
+ return factorialNumber(n - 1)
}
if (n < 0.5) {
@@ -38,7 +38,7 @@ export function gammaNumber (n) {
}
--n
- x = gammaP[0]
+ let x = gammaP[0]
for (let i = 1; i < gammaP.length; ++i) {
x += gammaP[i] / (n + i)
}
diff --git a/src/type/bignumber/function/bignumber.js b/src/type/bignumber/function/bignumber.js
index a38a84ae28..5dd930d9c9 100644
--- a/src/type/bignumber/function/bignumber.js
+++ b/src/type/bignumber/function/bignumber.js
@@ -15,12 +15,12 @@ export const createBignumber = /* #__PURE__ */ factory(name, dependencies, ({ ty
*
* Examples:
*
- * 0.1 + 0.2 // returns number 0.30000000000000004
- * math.bignumber(0.1) + math.bignumber(0.2) // returns BigNumber 0.3
+ * math.add(0.1, 0.2) // returns number 0.30000000000000004
+ * math.add(math.bignumber(0.1), math.bignumber(0.2)) // BigNumber 0.3
*
*
* 7.2e500 // returns number Infinity
- * math.bignumber('7.2e500') // returns BigNumber 7.2e500
+ * math.bignumber('7.2e500') // BigNumber 7.2e500
*
* See also:
*
diff --git a/src/type/complex/function/complex.js b/src/type/complex/function/complex.js
index b94327e2b4..7976e6c87e 100644
--- a/src/type/complex/function/complex.js
+++ b/src/type/complex/function/complex.js
@@ -28,12 +28,16 @@ export const createComplex = /* #__PURE__ */ factory(name, dependencies, ({ type
*
* Examples:
*
- * const a = math.complex(3, -4) // a = Complex 3 - 4i
- * a.re = 5 // a = Complex 5 - 4i
- * const i = a.im // Number -4
- * const b = math.complex('2 + 6i') // Complex 2 + 6i
- * const c = math.complex() // Complex 0 + 0i
- * const d = math.add(a, b) // Complex 5 + 2i
+ * const a = math.complex(3, -4)
+ * a // Complex 3 - 4i ...
+ * a.re = 5
+ * a // Complex 5 - 4i ...
+ * a.im // number -4 ...
+ * const b = math.complex('2 + 6i')
+ * b // Complex 2 + 6i ...
+ * math.add(a, b) // Complex 7 + 2i
+ *
+ * math.complex() // Complex 0 + 0i
*
* See also:
*
diff --git a/src/type/fraction/function/fraction.js b/src/type/fraction/function/fraction.js
index e1cf0dbf4b..b0982b604c 100644
--- a/src/type/fraction/function/fraction.js
+++ b/src/type/fraction/function/fraction.js
@@ -30,7 +30,7 @@ export const createFraction = /* #__PURE__ */ factory(name, dependencies, ({ typ
* math.fraction(1, 3) // returns Fraction 1/3
* math.fraction('2/3') // returns Fraction 2/3
* math.fraction({n: 2, d: 3}) // returns Fraction 2/3
- * math.fraction([0.2, 0.25, 1.25]) // returns Array [1/5, 1/4, 5/4]
+ * math.fraction([0.2, 0.25, 1.25]) // [fraction(1,5), fraction(1,4), fraction(5,4)]
* math.fraction(4, 5.1) // throws Error: Parameters must be integer
*
* See also:
diff --git a/src/type/matrix/DenseMatrix.js b/src/type/matrix/DenseMatrix.js
index 112e0abc33..9633637b25 100644
--- a/src/type/matrix/DenseMatrix.js
+++ b/src/type/matrix/DenseMatrix.js
@@ -90,6 +90,7 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
* @memberOf DenseMatrix
* @return {string} type information; if multiple types are found from the Matrix, it will return "mixed"
*/
+ // TODO: Shouldn't calling this update the internal _datatype?
DenseMatrix.prototype.getDataType = function () {
return getArrayDataType(this._data, typeOf)
}
@@ -145,6 +146,7 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
* new matrix elements will be filled with zeros.
*/
DenseMatrix.prototype.subset = function (index, replacement, defaultValue) {
+ if (isIndex(index)) index = Matrix.parseWithinIndex(index, this._size)
switch (arguments.length) {
case 1:
return _get(this, index)
@@ -159,6 +161,20 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies
}
}
+ /**
+ * Get one of the top-level sections of matrix one level down, e.g.
+ * the `which`th row of a 2D matrix, or the `which`th scalar of a
+ * vector.
+ *
+ * @memberof DenseMatrix
+ * @param {number} which
+ * @return {DenseMatrix | scalar} the `which`th element or full section
+ */
+ DenseMatrix.prototype.layer = function (which) {
+ if (this._size.length === 1) return this._data[which]
+ return this.create(this._data[which], this._datatype)
+ }
+
/**
* Get a single element from the matrix.
* @memberof DenseMatrix
diff --git a/src/type/matrix/Matrix.js b/src/type/matrix/Matrix.js
index 06ab6c162a..c5b3173833 100644
--- a/src/type/matrix/Matrix.js
+++ b/src/type/matrix/Matrix.js
@@ -1,4 +1,5 @@
import { factory } from '../../utils/factory.js'
+import { parseRange } from '../../utils/collection.js'
const name = 'Matrix'
const dependencies = []
@@ -7,15 +8,19 @@ export const createMatrixClass = /* #__PURE__ */ factory(name, dependencies, ()
/**
* @constructor Matrix
*
- * A Matrix is a wrapper around an Array. A matrix can hold a multi dimensional
- * array. A matrix can be constructed as:
+ * A Matrix is an object representing a matrix with any number of
+ * dimensions. A matrix can be generated in math js from an Array (with
+ * nested-Array depth equal to the dimension), via:
*
* let matrix = math.matrix(data)
*
- * Matrix contains the functions to resize, get and set values, get the size,
- * clone the matrix and to convert the matrix to a vector, array, or scalar.
- * Furthermore, one can iterate over the matrix using map and forEach.
- * The internal Array of the Matrix can be accessed using the function valueOf.
+ * This Matrix interface contains the functions to resize,
+ * get and set values, get the size, clone the matrix and to convert
+ * the matrix to a vector, array, or scalar.
+ * Furthermore, one can iterate over the matrix using map and forEach and
+ * JavaScript for loops.
+ * A (nested) Array corresponding to the Matrix can be accessed using the
+ * function valueOf.
*
* Example usage:
*
@@ -91,6 +96,72 @@ export const createMatrixClass = /* #__PURE__ */ factory(name, dependencies, ()
throw new Error('Cannot invoke subset on a Matrix interface')
}
+ /**
+ * Get one of the full sections of the matrix of dimension one less,
+ * at a specific position among all such sections. Note that
+ * `M.layer(n)` is equivalent to `M.subset(new Index(n, ':', ':' ...))`
+ * with the proper number of wildcards to match the dimension of matrix M,
+ * but typically much faster.
+ *
+ * For example, if M is a vector, then `M.layer(0)` is its first element,
+ * whereas if M is an ordinary 2D matrix, then `M.layer(1)` is its second
+ * row.
+ */
+ Matrix.prototype.layer = function (which) {
+ // must be provided by each Matrix implementation
+ throw new Error('Cannot invoke layer on a Matrix interface')
+ }
+
+ /**
+ * Helper for all of the implementations' subset methods.
+ * Parses any string representations of Ranges in the index, filling in
+ * limits with the sizes of the corresponding dimensions, allowing for
+ * wildcards.
+ *
+ * @param {Index} index
+ * @param {number[]} size of the matrix
+ * @return {Index} same index with string Ranges parsed and limits filled
+ */
+ Matrix.parseWithinIndex = function (index, size) {
+ let altered = false
+ const ndim = index.size().length
+ const newRanges = []
+ for (let dim = 0; dim < ndim; ++dim) {
+ let spec = index.dimension(dim)
+ if (typeof spec === 'string') {
+ const fields = parseRange(spec)
+ if (fields === null) {
+ throw new Error(`String '${spec}' does not specify a Range`)
+ }
+ fields.step ||= '1'
+ for (const key in fields) {
+ if (fields[key] === '') continue
+ const val = Number(fields[key])
+ if (isNaN(val)) {
+ throw new SyntaxError(
+ `${key} in '${spec}' does not represent a number`)
+ }
+ fields[key] = val
+ }
+ if (fields.start === '') fields.start = index.shiftPosition
+ if (fields.end === '') fields.end = size[dim] + index.shiftPosition
+ fields.start -= index.shiftPosition
+ fields.end -= index.shiftPosition
+ const attributes = index.includeEnd
+ ? { start: fields.start, last: fields.end, step: fields.step }
+ : fields
+ if (!Matrix.createRange) {
+ throw new Error('Range has not injected its constructor into Matrix')
+ }
+ altered = true
+ spec = Matrix.createRange(attributes)
+ }
+ newRanges.push(spec)
+ }
+ if (altered) return index.create(newRanges)
+ return index
+ }
+
/**
* Get a single element from the matrix.
* @param {number[]} index Zero-based index
@@ -141,7 +212,7 @@ export const createMatrixClass = /* #__PURE__ */ factory(name, dependencies, ()
*
* @return {Matrix} The reshaped matrix
*/
- Matrix.prototype.reshape = function (size, defaultValue) {
+ Matrix.prototype.reshape = function (size) {
// must be implemented by each of the Matrix implementations
throw new Error('Cannot invoke reshape on a Matrix interface')
}
diff --git a/src/type/matrix/MatrixIndex.js b/src/type/matrix/MatrixIndex.js
index 00f5447349..41d1aeae72 100644
--- a/src/type/matrix/MatrixIndex.js
+++ b/src/type/matrix/MatrixIndex.js
@@ -9,21 +9,28 @@ const dependencies = ['ImmutableDenseMatrix', 'getMatrixDataType']
export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ ImmutableDenseMatrix, getMatrixDataType }) => {
/**
* Create an index. An Index can store ranges and sets for multiple dimensions.
- * Matrix.get, Matrix.set, and math.subset accept an Index as input.
+ * The math.subset() function accepts an Index as input.
*
* Usage:
* const index = new Index(range1, range2, matrix1, array1, ...)
*
* Where each parameter can be any of:
* A number
- * A string (containing a name of an object property)
- * An instance of Range
* An Array with the Set values
* An Array with Booleans
- * A Matrix with the Set values
+ * A Matrix with the Set values (this might often be a Range instance)
* A Matrix with Booleans
+ * A string (will be interpreted as the name of an object property when
+ * used to index an object, or converted into a Range when used
+ * to index a Matrix/Array)
*
- * The parameters start, end, and step must be integer numbers.
+ * Note that all numeric values provided will be converted to the ordinary
+ * JavaScript number type when used for indexing.
+ * Further, once an Index has been constructed, you can set the `includeEnd`
+ * property on the Index to indicate that when strings are converted to
+ * Ranges, the end should be included (rather than excluded as by default).
+ * Similarly, you can set a `shiftPosition` property that will be subtracted
+ * from the entries of Ranges constructed from strings.
*
* @class Index
* @Constructor Index
@@ -37,6 +44,8 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
this._dimensions = []
this._sourceSize = []
this._isScalar = true
+ this.includeEnd = false
+ this.shiftPosition = 0
for (let i = 0, ii = ranges.length; i < ii; i++) {
const arg = ranges[i]
@@ -45,7 +54,7 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
const argType = typeof arg
let sourceSize = null
if (isRange(arg)) {
- this._dimensions.push(arg)
+ this._dimensions.push(arg.toNumber())
this._isScalar = false
} else if (argIsArray || argIsMatrix) {
// create matrix
@@ -67,12 +76,13 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
this._dimensions.push(Number(arg))
} else if (argType === 'string') {
// object property (arguments.count should be 1)
+ // or string notation for a Range, possibly with elided limits
+ // (see documention for `index` function) to allow wildcard
this._dimensions.push(arg)
} else {
throw new TypeError('Dimension must be an Array, Matrix, number, bigint, string, or Range')
}
this._sourceSize.push(sourceSize)
- // TODO: implement support for wildcard '*'
}
}
@@ -116,7 +126,7 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
* @return {Index} index
* @private
*/
- Index.create = function (ranges) {
+ Index.prototype.create = function (ranges) {
const index = new Index()
Index.apply(index, ranges)
return index
@@ -290,7 +300,7 @@ export const createIndexClass = /* #__PURE__ */ factory(name, dependencies, ({ I
* @return {Index}
*/
Index.fromJSON = function (json) {
- return Index.create(json.dimensions)
+ return new Index(...json.dimensions)
}
return Index
diff --git a/src/type/matrix/Range.js b/src/type/matrix/Range.js
index 0025ce3728..d5d5edcf35 100644
--- a/src/type/matrix/Range.js
+++ b/src/type/matrix/Range.js
@@ -1,128 +1,529 @@
-import { isBigInt, isBigNumber } from '../../utils/is.js'
-import { format, sign, nearlyEqual } from '../../utils/number.js'
+import { getArrayDataType } from '../../utils/array.js'
import { factory } from '../../utils/factory.js'
+import { parseRange } from '../../utils/collection.js'
+import {
+ isBigInt, isBigNumber, isCollection, isComplex, isFraction,
+ isIndex, isMatrix, isNumber, isRange, isUnit
+} from '../../utils/is.js'
const name = 'Range'
-const dependencies = []
+const dependencies = [
+ 'typed', 'typeOf', '?Index', '?BigNumber', '?Fraction', '?Complex',
+ 'Matrix', '?DenseMatrix', 'size', 'getMatrixDataType',
+ 'one', 'zero', 'add', 'subtract', 'multiply', 'divide', 'scalarDivide',
+ 'floor', 'equal', 'deepEqual', 'smallerEq', 'largerEq', 'isZero', 'isBounded',
+ 'number', 'numeric', 'format'
+]
-export const createRangeClass = /* #__PURE__ */ factory(name, dependencies, () => {
+// Some optimized operator functions used for special cases below. We
+// make them constants up here rather than generate them on the fly so that
+// they will not disrupt `deepStrictEqual`
+const identity = n => n
+const toBigInt = n => BigInt(n)
+const byBigInt = (n, b) => BigInt(n) * b
+
+export const createRangeClass = /* #__PURE__ */ factory(name, dependencies, ({
+ typed, typeOf, Index, BigNumber, Fraction, Complex,
+ Matrix, DenseMatrix, size, getMatrixDataType,
+ one, zero, add, subtract, multiply, divide, scalarDivide,
+ floor, equal, deepEqual, smallerEq, largerEq, isZero, isBounded,
+ number, numeric, format
+}) => {
+ // Helpers for constructor; note the canonical attributes correspond positionally
+ // two-to-one with the first list of attributes that are available for external use
+ const attrs = 'start,end,step,length,last'.split(',')
+ const enoughAttrs = ['start', 'step', 'length'] // determine range uniquely
+ function isAttrs (thing) {
+ if (!thing) return false
+ if (typeof thing !== 'object') return false
+ for (const key in thing) if (!attrs.includes(key)) return false
+ return true
+ }
+ function getBdSegments (attributes) {
+ let bound = null
+ let segments = attributes.length
+ if ('last' in attributes) {
+ bound = attributes.last
+ segments -= 1
+ } else bound = attributes.end
+ return [bound, segments]
+ }
+ const deepZero = entity => deepEqual(entity, zero(entity))
+ function oneUnit (u) {
+ const result = u.clone()
+ let val = one(u.value)
+ // Adjust by the prefixes of the unit
+ for (const spec of u.units) val = multiply(val, spec.prefix.value)
+ result.value = val
+ return result
+ }
+ function firstEntry (collection) {
+ if (Array.isArray(collection)) {
+ while (size(collection).length) collection = collection(0)
+ return collection
+ }
+ const pos = size(collection).map(() => 0)
+ return collection.get(pos)
+ }
+
+ // More optimized operator functions, see above.
+ const toBigNumber = n => new BigNumber(n)
+ const toFraction = n => new Fraction(n)
+ const toComplex = n => new Complex(n)
+ const multBigIntArray = typed.find(multiply, ['bigint', 'Array'])
+ const arrayByBigint = (n, b) => multBigIntArray(BigInt(n), b)
/**
- * Create a range of numbers. A range has a start, step, and end,
- * and contains functions to iterate over the range.
+ * Range Matrix implementation.
+ *
+ * A Range is a matrix representing an arithmetic sequence. The
+ * elements of a Range consist of the values of `a + sd`, where
+ * `a` and `d` are any entities for which the relevant operations are
+ * defined, and `s` is an integer number from 0 to one less than the
+ * length of the Range. A common case is for `a` to be an integer number
+ * and `d` to be 1, in which case the Range becomes a vector of
+ * consecutive numbers. Ranges whose elements can be converted to numbers
+ * may be used to index other Matrices.
+ *
+ * Note that Ranges are "lazy" in that the entries are not stored in memory,
+ * but generated as needed. As a consequence, attempts to alter individual
+ * entries of a Range will throw an error.
+ *
+ * Every Range has several attributes that determine its entries. Once
+ * constructed, these attributes cannot be changed; they are read-only.
+ *
+ * Every Range has these attributes:
+ * * start: the first element of the Range (the value `a` above).
+ * * step: the step or common difference of the Range (the value `d` above).
+ * * length: the number of elements in the Range, or one more than the
+ * largest value of `s` above. Note that this attribute may be Infinity,
+ * so that a Range can represent an unending arithmetic progression.
+ *
+ * In addition, a Range may have one or both of the following attributes:
+ * * last: the inclusive final limit of the Range. This value must be
+ * of the form `a + td` for some number `t`, in which case the Range
+ * consists of `a + sd` for all nonnegative integers `s ≤ t`.
+ * * end: an exclusive limit of the Range. This value must be of the
+ * form `a + ud` for some number `u`, in which case the Range consists
+ * of `a + sd` for all nonnegative integers `s < u`.
*
- * A range can be constructed as:
+ * There is a consistency relation, in that if the last value is the start
+ * value plus _t_ times the step value, then the length must be the floor
+ * of _t_ plus one. Similarly, if the end value is the start value plus _u_
+ * times the step, then the length must be the ceiling of u. If the step
+ * of a Range is zero, then it generally does not have an end or last value
+ * to avoid breaking this consistency relation.
*
- * const range = new Range(start, end)
- * const range = new Range(start, end, step)
+ * A Range can be constructed from a plain object with any of the above
+ * attributes, presuming they are consistent. In addition, for convenience
+ * and backward compatibility, the first constructor argument (if any) that
+ * is not of this form gives the start value, the second gives the end
+ * value, and the third gives the step value.
*
- * Note that the endpoints and step may be specified with other numeric
- * types such as bigint or BigNumber, but they will be demoted to the
- * built-in `number` type and the Range will only contain numbers. The
- * rationale for this demotion is that Range objects are primarily used
- * for indexing Matrix objects, and Matrix objects may only be indexed
- * with `number`s.
+ * Because of the consistency relation and defaults provided for convenience,
+ * some or even all of the attributes may be missing in the constructor.
+ * If any are missing, they are deduced for you in the following order:
+ * * step: filled in via consistency if start, length, and at least one
+ * of last and end are specified; otherwise set to the "one" value of
+ * the type of start, last, or end if specified, or the number 1 if not.
+ * * start: filled in via consistency with the step if length and at
+ * least one of last and end are specified; otherwise set to the "zero"
+ * value of the type of last or end if specified, or the number 0 if not.
+ * * length: filled in via consistency with start and step if at least
+ * one of last and end are specified; otherwise, set to 0.
*
- * To get the result of the range:
- * range.forEach(function (x) {
- * console.log(x)
- * })
- * range.map(function (x) {
- * return math.sin(x)
- * })
- * range.toArray()
+ * In addition, if the length value is finite and the step is nonzero, the
+ * following are set whether or not they were specified, to canonicalize
+ * the attributes of the Range (which makes it easier to use and interpret):
+ * * last: Set to the start value plus the step times the length
+ * minus one.
+ * * end: Set to the start value plus the step times the length.
*
- * Example usage:
+ * Note that the endpoints and increment may be specified with any type
+ * handled by mathjs, but they must support the operations needed by Range
+ * (addition, multiplication by an integer ordinary number, comparison).
+ * The data type of the range is the data type of `start + n*step`, where `n`
+ * is an integer number; the package assumes that this data type does not
+ * depend on the value of `n`.
*
- * const c = new Range(2, 6) // 2:1:5
- * c.toArray() // [2, 3, 4, 5]
- * const d = new Range(2, -3, -1) // 2:-1:-2
- * d.toArray() // [2, 1, 0, -1, -2]
+ * Ranges support any non-modifying Matrix methods.
+ *
+ * Examples:
+ *
+ * const c = new Range(2, 5)
+ * c.toArray() // [2, 3, 4]
+ * const b = new Range({start: 2, last: 5})
+ * b.toArray() // [2, 3, 4, 5]
+ * new Range({start: 2, end: 5}) // [2, 3, 4]
+ *
+ * const d = new Range(2, -2, -1)
+ * d.toArray() // [2, 1, 0, -1]
+ * const d2 = new Range(2, {step: -1}, -2) // same Range
+ *
+ * const e = new Range(3n) // 3n, 4n, 5n, ... forever
+ * e.toArray() // throws
+ * const f = new Range() // []
+ *
+ * const g = new Range({start: 9, step: fraction(2, 3), end: 11})
+ * g.toArray() // [fraction(9), fraction(29, 3), fraction(31, 3)]
*
* @class Range
* @constructor Range
- * @param {number} start included lower bound
- * @param {number} end excluded upper bound
- * @param {number} [step] step size, default value is 1
+ * @param {number} [start] included lower bound
+ * @param {number} [end] excluded upper bound
+ * @param {number} [step] step size, default value is 1
*/
- function Range (start, end, step) {
+ function Range (...specs) {
if (!(this instanceof Range)) {
throw new SyntaxError('Constructor must be called with the new operator')
}
- const hasStart = start !== null && start !== undefined
- const hasEnd = end !== null && end !== undefined
- const hasStep = step !== null && step !== undefined
-
- if (hasStart) {
- if (isBigNumber(start)) {
- start = start.toNumber()
- } else if (typeof start !== 'number' && !isBigInt(start)) {
- throw new TypeError('Parameter start must be a number or bigint')
+ const attributes = {}
+ // Read the first object supplying attributes, if any
+ for (const spec of specs) {
+ if (isAttrs(spec)) {
+ Object.assign(attributes, spec)
+ break
}
}
- if (hasEnd) {
- if (isBigNumber(end)) {
- end = end.toNumber()
- } else if (typeof end !== 'number' && !isBigInt(end)) {
- throw new TypeError('Parameter end must be a number or bigint')
+ let role = 0
+ let attrCount = 0
+ // Now interpret the positional arguments, ignoring one attributes object
+ for (const spec of specs) {
+ if (isAttrs(spec)) {
+ attrCount += 1
+ if (attrCount > 1) {
+ throw new Error('Only one attributes object may specify a Range')
+ }
+ } else {
+ if (role === 3) {
+ throw new Error(
+ 'Only start, end, and step allowed as positional arguments ' +
+ 'of Range constructor')
+ }
+ const key = attrs[role]
+ if (key in attributes) {
+ throw new Error(
+ `May not specify Range attribute "${key}" via key and argument.`)
+ }
+ attributes[key] = spec
+ role += 1
+ }
+ }
+
+ // OK, we have extracted all of the specified attributes. Now fill in
+ // the rest/canonicalize them as specified.
+ // First make sure the length is a number
+ if ('length' in attributes) {
+ attributes.length = number(attributes.length)
+ }
+ // Now deduce "step" if necessary
+ if (attributes.step === undefined || attributes.step === null) {
+ const prereqs = 'start' in attributes && 'length' in attributes
+ if (prereqs && ('last' in attributes || 'end' in attributes)) {
+ const [bound, segments] = getBdSegments(attributes)
+ if (segments === 0) attributes.step = zero(attributes.start)
+ else {
+ const span = subtract(bound, attributes.start)
+ // if the span is computed in bigints, we want a bigint increment
+ let bigi = isBigInt(span)
+ bigi ||= isMatrix(span) && span.datatype() === 'bigint'
+ bigi ||= Array.isArray(span) &&
+ getArrayDataType(span, typeOf) === 'bigint'
+ const denominator = bigi ? BigInt(segments) : segments
+ attributes.step = divide(span, denominator)
+ }
+ } else {
+ let template = attributes.start
+ if (template === undefined) {
+ if ('last' in attributes) template = attributes.last
+ else if ('end' in attributes) template = attributes.end
+ else template = 1
+ }
+ if (size(template).length) template = firstEntry(template)
+ // now we have a scalar!
+ if (isUnit(template)) attributes.step = oneUnit(template)
+ else attributes.step = one(template)
}
+ } else if (!isBounded(attributes.step)) {
+ throw new RangeError('A Range must have a finite increment')
+ }
+ // Now that we have the increment b, we can choose the multiplication
+ // operation. For n an integer JavaScript number, we want n*b to be the sum
+ // of n copies of b. If we simply use mathjs multiply, this property will
+ // hold for most types b might have, but not for bigint (because e.g.
+ // 1.657 * 3n should be 4.971, so mathjs makes that combination always
+ // return number, not bigint). So we need to take care in choosing what
+ // function we will use to multiply:
+ this.times = typed.find(multiply, ['number', typeOf(attributes.step)])
+ const incr = attributes.step
+ // Special cases for times (for speedup when increment is 1)
+ if (!isUnit(incr) && equal(incr, 1)) {
+ if (isNumber(incr)) this.times = identity
+ else if (isBigInt(incr)) this.times = toBigInt
+ else if (isBigNumber(incr)) this.times = toBigNumber
+ else if (isFraction(incr)) this.times = toFraction
+ else if (isComplex(incr)) this.times = toComplex
+ } else if (isBigInt(incr)) { // and special cases b/c of bigint conversions
+ this.times = byBigInt
+ } else if (isMatrix(incr) && incr.datatype() === 'bigint') {
+ const mult = typed.find(multiply, ['bigint', typeOf(incr)])
+ this.times = (n, b) => mult(BigInt(n), b)
+ } else if (Array.isArray(incr) && getArrayDataType(incr) === 'bigint') {
+ this.times = arrayByBigint
+ }
+
+ // Next deduce "start" if necessary
+ if (attributes.start === undefined || attributes.start === null) {
+ if ('length' in attributes &&
+ ('last' in attributes || 'end' in attributes)
+ ) {
+ const [bound, segments] = getBdSegments(attributes)
+ attributes.start = subtract(
+ bound, this.times(segments, attributes.step))
+ } else if ('last' in attributes) {
+ attributes.start = zero(attributes.last)
+ } else if ('end' in attributes) {
+ attributes.start = zero(attributes.end)
+ } else attributes.start = zero(attributes.step)
+ }
+ if (!isBounded(attributes.start)) {
+ throw new RangeError('A Range must start on a finite value')
+ }
+
+ // Now deduce length if need be
+ if (attributes.length === undefined || attributes.length === null) {
+ if ('last' in attributes) {
+ if (!isBounded(attributes.last)) attributes.length = Infinity
+ else {
+ const diff = subtract(attributes.last, attributes.start)
+ let rawLength = scalarDivide(diff, attributes.step)
+ if (rawLength === undefined) {
+ if (size(diff).length > 0 && size(attributes.step) === 0) {
+ const first = firstEntry(diff)
+ if (deepZero(subtract(diff, first))) {
+ rawLength = scalarDivide(first, attributes.step)
+ }
+ }
+ }
+ if (rawLength === undefined) {
+ let message = `No scalar multiple of ${attributes.step} takes `
+ message += `${attributes.start} to ${attributes.last}`
+ throw new Error(message)
+ }
+ attributes.length = Math.floor(number(rawLength)) + 1
+ }
+ } else if ('end' in attributes) {
+ if (!isBounded(attributes.end)) attributes.length = Infinity
+ else {
+ const diff = subtract(attributes.end, attributes.start)
+ let rawLength = scalarDivide(diff, attributes.step)
+ if (rawLength === undefined) {
+ if (size(diff).length > 0 && size(attributes.step).length === 0) {
+ const first = firstEntry(diff)
+ if (deepZero(subtract(diff, first))) {
+ rawLength = scalarDivide(first, attributes.step)
+ }
+ }
+ }
+ if (rawLength === undefined) {
+ let message = `No scalar multiple of ${attributes.step} takes `
+ message += `${attributes.start} to ${attributes.end}`
+ throw new Error(message)
+ }
+ attributes.length = Math.ceil(number(rawLength))
+ }
+ } else attributes.length = 0
}
- if (hasStep) {
- if (isBigNumber(step)) {
- step = step.toNumber()
- } else if (typeof step !== 'number' && !isBigInt(step)) {
- throw new TypeError('Parameter step must be a number or bigint')
+ if (attributes.length < 0) attributes.length = 0
+
+ // Finally fill in last and end as appropriate
+ if (Number.isFinite(attributes.length)) {
+ attributes.length = Math.floor(attributes.length)
+ // canonicalize limits
+ if (deepZero(attributes.step)) {
+ // We certainly know the last entry:
+ attributes.last = attributes.start
+ // But there is no way to have an exclusive limit unless the
+ // length is zero
+ attributes.end =
+ attributes.length === 0 ? attributes.start : undefined
+ } else {
+ attributes.last = add(
+ attributes.start, this.times(attributes.length - 1, attributes.step))
+ attributes.end = add(attributes.last, attributes.step)
}
+ } else {
+ attributes.last = undefined
+ attributes.end = undefined
}
- this.start = hasStart ? parseFloat(start) : 0
- this.end = hasEnd ? parseFloat(end) : 0
- this.step = hasStep ? parseFloat(step) : 1
- if (hasStep && nearlyEqual(this.step, 0)) {
- throw new Error('Step must not be zero')
+ // Canonicalize the type of start to match all the other values:
+ attributes.start = add(attributes.start, this.times(0, attributes.step))
+
+ // set up data type and remaining operations
+ this.plus = typed.find(
+ add, [typeOf(attributes.start), typeOf(this.times(1, attributes.step))])
+ const startsize = size(attributes.start)
+ this.subcollection = !!(startsize.length)
+ if (this.subcollection) {
+ this._datatype = getMatrixDataType(attributes.start)
+ } else this._datatype = typeOf(attributes.start)
+
+ this._size = [attributes.length, ...startsize]
+
+ // Finally, set up the read-only properties:
+ for (const key of attrs) {
+ Object.defineProperty(this, key, {
+ value: attributes[key],
+ enumerable: true
+ })
}
}
+ Range.prototype = new Matrix()
+
/**
* Attach type information
*/
+ Object.defineProperty(Range, 'name', { value: 'Range' })
+ Range.prototype.constructor = Range
Range.prototype.type = 'Range'
Range.prototype.isRange = true
/**
- * Parse a string into a range,
- * The string contains the start, optional step, and end, separated by a colon.
+ * [DEPRECATED; use `math.parse` directly] Parse a string into a range.
+ * The string contains the start, optional step, and end, separated by colons.
* If the string does not contain a valid range, null is returned.
+ * Note that currently only ordinary Javascript floating-point number
+ * items are permitted for start, step, and end in this string notation.
* For example str='0:2:11'.
+ * The default step, if it is not specified, is 1.
+ * If the string begins with a ':', 0 is filled in for the first value.
+ * If it ends with a ':', Infinity is filled in for the last value.
+ * By default, the end value is excluded from the range.
+ *
* @memberof Range
* @param {string} str
+ * @param {?number} limit
* @return {Range | null} range
*/
Range.parse = function (str) {
- if (typeof str !== 'string') {
- return null
+ if (Range.parseMethodMustWarn) {
+ console.warn(
+ 'Using deprecated class method Range.parse(); ' +
+ 'use library function math.parse() instead.')
+ Range.parseMethodMustWarn = false
}
- const args = str.split(':')
- const nums = args.map(function (arg) {
- return parseFloat(arg)
- })
-
- const invalid = nums.some(function (num) {
- return isNaN(num)
- })
- if (invalid) {
- return null
+ if (typeof str !== 'string') return null
+ const fields = parseRange(str)
+ if (fields === null) return null
+ if (fields.start === '') fields.start = '0'
+ if (fields.end === '') fields.end = 'Infinity'
+ if (fields.step === '') fields.step = '1'
+ for (const key in fields) {
+ const value = Number(fields[key])
+ if (isNaN(value)) return null
+ fields[key] = value
}
+ return new Range(fields)
+ }
+ // inject Range constructor into parent class,
+ // for use by all Matrix implementations
+ Matrix.createRange = function (...args) {
+ return new Range(...args)
+ }
+
+ /**
+ * Get the datatype of the entries of the range.
+ *
+ * Usage:
+ * const format = range.datatype() // retrieve renge datatype
+ *
+ * @memberof Range
+ * @return {string} The datatype.
+ */
+ Range.prototype.datatype = function () {
+ return this._datatype
+ }
+
+ /**
+ * Get the matrix type
+ *
+ * Usage:
+ * const matrixType = matrix.getDataType() // retrieves the matrix type
+ *
+ * @memberOf Range
+ * @return {string} type information
+ */
+ Range.prototype.getDataType = function () {
+ return this._datatype
+ }
- switch (nums.length) {
- case 2:
- return new Range(nums[0], nums[1])
- case 3:
- return new Range(nums[0], nums[2], nums[1])
- default:
- return null
+ /**
+ * Get the storage format used by the matrix.
+ *
+ * Usage:
+ * const format = range.storage() // retrieve storage format
+ *
+ * @return {string} The storage format.
+ */
+ Range.prototype.storage = function () {
+ return 'range' // neither 'sparse' or 'dense' seemed fair
+ }
+
+ /**
+ * Create a new Range from data if possible, otherwise DenseMatrix
+ *
+ * Note that to conform with the Matrix prototype, this should accept
+ * an Array of the data and the datatype of the data. Hence, it attempts
+ * to "reverse engineer" a Range that will encode that data with the
+ * proper datatype, and if not, reverts to creating a DenseMatrix.
+ *
+ * @memberof Range
+ * @param {Array} data
+ * @param {string} [datatype]
+ */
+ Range.prototype.create = function (data, datatype) {
+ if (data.length === 0) {
+ return new Range({
+ start: numeric(0, datatype),
+ end: numeric(0, datatype)
+ })
+ }
+ let start = data[0]
+ if (datatype) start = numeric(start, datatype)
+ if (data.length === 1) return new Range({ start, last: start })
+ let last = data[data.length - 1]
+ if (datatype) last = numeric(last, datatype)
+ if (data.length === 2) {
+ return new Range({ start, length: 2, step: subtract(last, start) })
+ }
+ let entry = data[1]
+ if (datatype) entry = numeric(entry, datatype)
+ const step = subtract(entry, start)
+ for (let i = 2; i < data.length; ++i) {
+ entry = add(entry, step)
+ if (!equal(entry, data[i])) {
+ if (DenseMatrix) return new DenseMatrix(data, datatype)
+ throw new Error('Data supplied is not in the form of a Range')
+ }
}
+ return new Range({ start, length: data.length, step })
+ }
+
+ /**
+ * Create a new Range
+ *
+ * Convenience method to call the constructor from an instance of Range.
+ * Takes exactly the same possible arguments as the constructor.
+ *
+ * @memberof Range
+ * @param {..args} arguments
+ * @return {Range} fresh Range
+ */
+ Range.prototype.createRange = function (...args) {
+ return new Range(...args)
}
/**
@@ -130,32 +531,149 @@ export const createRangeClass = /* #__PURE__ */ factory(name, dependencies, () =
* @return {Range} clone
*/
Range.prototype.clone = function () {
- return new Range(this.start, this.end, this.step)
+ const spec = {}
+ for (const key of enoughAttrs) spec[key] = this[key]
+ return new Range(spec)
}
/**
- * Retrieve the size of the range.
- * Returns an array containing one number, the number of elements in the range.
+ * Get a subset of the range (replacement prohibited)
+ *
+ * Usage:
+ * const subset = range.subset(index) // retrieve subset
+ *
+ * @param {Index} index
+ */
+
+ Range.prototype.subset = function (index, replacement, defaultValue) {
+ if (replacement || defaultValue) {
+ throw new Error('Ranges are immutable, cannot replace entries')
+ }
+ if (!isIndex(index)) throw new TypeError('Invalid index')
+ index = Matrix.parseWithinIndex(index, this._size)
+ const wanted = index.dimension(0)
+ const sizes = index.size()
+ if (isNumber(wanted)) {
+ const item = this.layer(wanted)
+ if (sizes.length === 1) return wanted
+ if (!Index) {
+ throw new Error('No Indexing into 2D Range without Matrix support')
+ }
+ // Caller thinks we can index into the result, have at it
+ const newIndex = []
+ for (let d = 1; d < sizes.length; ++d) newIndex.push(index.dimension(d))
+ return item.subset(new Index(...newIndex))
+ }
+ if (!isRange(wanted) || sizes.length > 1) {
+ // Punt to Matrix subsetting
+ if (!DenseMatrix) {
+ throw new Error('No general subset of Range without Matrix support')
+ }
+ return new DenseMatrix(this.toArray(), this._datatype).subset(index)
+ }
+
+ // Indexing a range by a single range produces a range
+ if (this.length < wanted.length) {
+ throw new Error('Cannot subset a Range by a longer Range')
+ }
+ if (!Number.isFinite(wanted.length)) {
+ return new Range({
+ start: this.layer(wanted.start),
+ step: this.step * wanted.step
+ })
+ }
+ return new Range({
+ start: this.layer(wanted.start),
+ end: this.layer(wanted.end),
+ step: this.step * wanted.step
+ })
+ }
+
+ /**
+ * Get the entry at an integer position in a Range.
* @memberof Range
- * @returns {number[]} size
+ * @param {number} which Zero-based position
+ * @return {*} value
*/
- Range.prototype.size = function () {
- let len = 0
- const start = this.start
- const step = this.step
- const end = this.end
- const diff = end - start
+ Range.prototype.layer = function (index) {
+ if (index < 0 || index >= this.length) {
+ throw new RangeError('index out of Range')
+ }
+ return this.plus(this.start, this.times(index, this.step))
+ }
- if (sign(step) === sign(diff)) {
- len = Math.ceil((diff) / step)
- } else if (diff === 0) {
- len = 0
+ /**
+ * Get a single element from a Range.
+ * @memberof Range
+ * @param {number[]} index Zero-based index
+ * @return {*} value
+ */
+ Range.prototype.get = function (index) {
+ const item = this.layer(index[0])
+ if (index.length === 1) return item
+ return item.get(index.slice(1))
+ }
+
+ /**
+ * Replacing a single element of a Range is not supported
+ */
+ Range.prototype.set = function () {
+ throw new Error(
+ 'Replacement of an element of a Range is not supported')
+ }
+
+ /**
+ * Resize the Range to the given size. Returns a fresh Matrix when
+ * `copy=true`, otherwise fails because a Range cannot be resized in place.
+ *
+ * @memberof Range
+ * @param {number[] || Matrix} size The new size the matrix should have.
+ * @param {*} [defaultValue=0] Default value, filled in on new entries.
+ * If not provided, the matrix elements will
+ * be filled with zeros.
+ * @param {boolean} [copy] Return a resized copy of the matrix
+ *
+ * @return {Matrix} The resized matrix
+ */
+ Range.prototype.resize = function (size, defaultValue, copy) {
+ // validate arguments
+ if (!isCollection(size)) {
+ throw new TypeError('Array or Matrix expected for new size')
+ }
+ if (!copy) throw new Error('A Range cannot be resized in place')
+ if (!DenseMatrix) {
+ throw new Error('No Range resize without Matrix support')
}
+ const mat = new DenseMatrix(this.valueOf(), this._datatype)
+ return mat.resize(size, defaultValue)
+ }
- if (isNaN(len)) {
- len = 0
+ /**
+ * Reshape the Range to the given size. Returns a copy of the matrix when
+ * `copy=true`, otherwise fails because a Range cannot be reshaped.
+ *
+ * @memberof Range
+ * @param {number[]} size The new size the matrix should have.
+ * @param {boolean} [copy] Return a reshaped copy of the matrix
+ *
+ * @return {Matrix} The reshaped matrix
+ */
+ Range.prototype.reshape = function (size, copy) {
+ if (!copy) throw new Error('A Range cannot be reshaped in place')
+ if (!DenseMatrix) {
+ throw new Error('No Range reshape without Matrix support')
}
- return [len]
+ return new DenseMatrix(this.valueOf(), this._datatype).reshape(size)
+ }
+
+ /**
+ * Retrieve the size of the Range.
+ * Returns an array containing one number, the number of elements in the range.
+ * @memberof Range
+ * @returns {number[]} size
+ */
+ Range.prototype.size = function () {
+ return this._size
}
/**
@@ -164,19 +682,16 @@ export const createRangeClass = /* #__PURE__ */ factory(name, dependencies, () =
* @return {number | undefined} min
*/
Range.prototype.min = function () {
- const size = this.size()[0]
-
- if (size > 0) {
- if (this.step > 0) {
- // positive step
- return this.start
- } else {
- // negative step
- return this.start + (size - 1) * this.step
- }
- } else {
- return undefined
+ if (this.length === 0) return undefined
+ if (this.length === 1) return this.start
+ if (this.subcollection) {
+ throw new TypeError('Elements of sequence are collections, so unordered')
+ }
+ if (Number.isFinite(this.length)) {
+ return smallerEq(this.start, this.last) ? this.start : this.last
}
+ // Infinite sequence
+ return smallerEq(this.start, this.layer(1)) ? this.start : undefined
}
/**
@@ -185,76 +700,161 @@ export const createRangeClass = /* #__PURE__ */ factory(name, dependencies, () =
* @return {number | undefined} max
*/
Range.prototype.max = function () {
- const size = this.size()[0]
-
- if (size > 0) {
- if (this.step > 0) {
- // positive step
- return this.start + (size - 1) * this.step
- } else {
- // negative step
- return this.start
- }
- } else {
- return undefined
+ if (this.length === 0) return undefined
+ if (this.length === 1) return this.start
+ if (this.subcollection) {
+ throw new TypeError('Elements of sequence are collections, so unordered')
}
+ if (Number.isFinite(this.length)) {
+ return largerEq(this.start, this.last) ? this.start : this.last
+ }
+ // Infinite sequence
+ return largerEq(this.start, this.layer(1)) ? this.start : undefined
}
/**
- * Execute a callback function for each value in the range.
+ * Execute a callback function for each value in theRrange.
* @memberof Range
* @param {function} callback The callback method is invoked with three
* parameters: the value of the element, the index
* of the element, and the Range being traversed.
+ * @param {boolean} skipZeros If true, the callback function is invoked only for non-zero entries
+ * @param {boolean} isUnary If true, the callback function is invoked with one parameter
*/
- Range.prototype.forEach = function (callback) {
+ Range.prototype.forEach = function (
+ callback, skipZeros = false, isUnary = false
+ ) {
+ if (!Number.isFinite(this.length)) throw new Error('Attempt to infinite loop')
let x = this.start
- const step = this.step
- const end = this.end
- let i = 0
-
- if (step > 0) {
- while (x < end) {
- callback(x, [i], this)
- x += step
- i++
- }
- } else if (step < 0) {
- while (x > end) {
- callback(x, [i], this)
- x += step
- i++
+ for (let i = 0; i < this.length; ++i) {
+ if (this.subcollection) {
+ if (isUnary) x.forEach(callback, skipZeros, isUnary)
+ else {
+ const me = this
+ x.forEach((val, ix) => callback(val, [i, ...ix], me), skipZeros)
+ }
+ } else {
+ if (skipZeros && isZero(x)) continue
+ if (isUnary) callback(x)
+ else callback(x, [i], this)
}
+ x = this.plus(x, this.step)
+ }
+ }
+
+ /**
+ * Iterate over the range elements
+ * @return {Iterable<{ value, index: number[] }>}
+ */
+ Range.prototype[Symbol.iterator] = function * () {
+ let x = this.start
+ for (let i = 0; i < this.length; ++i) {
+ if (this.subcollection) {
+ for (const { value, ix } of x) yield ({ value, index: [i, ...ix] })
+ } else yield ({ value: x, index: [i] })
+ x = this.plus(x, this.step)
}
}
/**
* Execute a callback function for each value in the Range, and return the
- * results as an array
+ * results as a Matrix
* @memberof Range
* @param {function} callback The callback method is invoked with three
* parameters: the value of the element, the index
- * of the element, and the Matrix being traversed.
+ * of the element, and the Range being traversed.
* @returns {Array} array
*/
- Range.prototype.map = function (callback) {
+ Range.prototype.map = function (
+ callback, skipZeros = false, isUnary = false
+ ) {
+ if (!Number.isFinite(this.length)) {
+ throw new Error(
+ 'Attempt to infinite loop Range with ' +
+ `start=${this.start} step=${this.step}`)
+ }
const array = []
- this.forEach(function (value, index, obj) {
- array[index[0]] = callback(value, index, obj)
- })
+ let x = this.start
+ for (let i = 0; i < this.length; ++i) {
+ if (this.subcollection) {
+ if (isUnary) array.push(x.map(callback, skipZeros, isUnary))
+ else {
+ const me = this
+ array.push(
+ x.map((val, ix) => callback(val, [i, ...ix], me), skipZeros))
+ }
+ } else {
+ if (skipZeros && isZero(x)) continue
+ if (isUnary) array.push(callback(x))
+ else array.push(callback(x, [i], this))
+ }
+ x = this.plus(x, this.step)
+ }
+ if (DenseMatrix) return new DenseMatrix(array)
return array
}
/**
- * Create an Array with a copy of the Ranges data
+ * Returns an array containing the rows of a 2D Range
+ * @returns {Array}
+ */
+ Range.prototype.rows = function () {
+ if (!Number.isFinite(this.length)) {
+ throw new Error('Attempt to infinite loop')
+ }
+ if (this._size.length !== 2) {
+ throw new TypeError('Rows can only be returned for a 2D matrix.')
+ }
+ const result = []
+ let x = this.start
+ for (let i = 0; i < this.length; ++i) {
+ if (DenseMatrix) result.push(new DenseMatrix(x, this._datatype))
+ else result.push(x)
+ x = this.plus(x, this.step)
+ }
+ return result
+ }
+
+ /**
+ * Returns an array containing the columns of a 2D Range
+ * @returns {Array}
+ */
+ Range.prototype.columns = function () {
+ if (!Number.isFinite(this.length)) {
+ throw new Error('Attempt to infinite loop')
+ }
+ if (this._size.length !== 2) {
+ throw new TypeError('Rows can only be returned for a 2D matrix.')
+ }
+ const colArrays = []
+ let x = this.start
+ if (this.length) for (const { value } of x) colArrays.push([value])
+ for (let i = 1; i < this.length; ++i) {
+ x = this.plus(x, this.step)
+ for (const { value, ix } of x) colArrays[ix[0]].push(value)
+ }
+ if (DenseMatrix) {
+ return colArrays.map(arr => new DenseMatrix(arr, this._datatype))
+ }
+ return colArrays
+ }
+
+ /**
+ * Create an Array with a copy of the Range data
* @memberof Range
* @returns {Array} array
*/
Range.prototype.toArray = function () {
+ if (!Number.isFinite(this.length)) {
+ throw new Error('Attempt to infinite loop')
+ }
const array = []
- this.forEach(function (value, index) {
- array[index[0]] = value
- })
+ let x = this.start
+ for (let i = 0; i < this.length; ++i) {
+ if (this.subcollection) array.push(x.valueOf())
+ else array.push(x)
+ x = this.plus(x, this.step)
+ }
return array
}
@@ -271,6 +871,8 @@ export const createRangeClass = /* #__PURE__ */ factory(name, dependencies, () =
/**
* Get a string representation of the range, with optional formatting options.
* Output is formatted as 'start:step:end', for example '2:6' or '0:0.2:11'
+ * Note that the result is not guaranteed to be parseable unless the
+ * data type of the Range is `number`
* @memberof Range
* @param {Object | number | function} [options] Formatting options. See
* lib/utils/number:format for a
@@ -278,14 +880,29 @@ export const createRangeClass = /* #__PURE__ */ factory(name, dependencies, () =
* options.
* @returns {string} str
*/
- Range.prototype.format = function (options) {
- let str = format(this.start, options)
-
- if (this.step !== 1) {
- str += ':' + format(this.step, options)
+ Range.prototype.format = function (options = {}) {
+ let start = format(this.start, options)
+ let writeStep = false
+ if (size(this.step).length > 0) writeStep = true
+ else {
+ const canonicalStep =
+ isUnit(this.step) ? oneUnit(this.step) : one(this.step)
+ if (!equal(this.step, canonicalStep)) writeStep = true
+ }
+ let step = writeStep ? format(this.step, options) : ''
+ if (typeof this.end === 'undefined') {
+ // Can't display as usual `start:step:end` so use a new
+ // pseudo-constructor notation
+ let str = `Range{start: ${start}, `
+ if (step) str += `step: ${step}, `
+ return str + `length: ${this.length}}`
}
- str += ':' + format(this.end, options)
- return str
+ // Display as `start:step:end` but may need parens for nesting
+ if (isRange(this.start)) start = `(${start})`
+ if (step && isRange(this.step)) step = `(${step})`
+ let end = format(this.end, options)
+ if (isRange(this.end)) end = `(${end})`
+ return step ? `${start}:${step}:${end}` : `${start}:${end}`
}
/**
@@ -297,31 +914,57 @@ export const createRangeClass = /* #__PURE__ */ factory(name, dependencies, () =
return this.format()
}
+ /**
+ * Return itself if it has datatype number, otherwise a new similar range
+ * consisting of numbers.
+ * @memberof Range
+ * @returns {Range}
+ */
+ Range.prototype.toNumber = function () {
+ if (this._datatype === 'number') return this
+ return new Range({
+ start: number(this.start), step: number(this.step), length: this.length
+ })
+ }
+
/**
* Get a JSON representation of the range
* @memberof Range
* @returns {Object} Returns a JSON object structured as:
- * `{"mathjs": "Range", "start": 2, "end": 4, "step": 1}`
+ * `{"mathjs": "Range", ...rangeAttributes}`
*/
Range.prototype.toJSON = function () {
- return {
- mathjs: 'Range',
- start: this.start,
- end: this.end,
- step: this.step
+ const json = { mathjs: 'Range' }
+ for (const key of enoughAttrs) {
+ const attr = this[key]
+ if (attr === undefined || attr === null) continue
+ if (typeof attr === 'object' && 'toJSON' in attr) {
+ json[key] = attr.toJSON()
+ } else json[key] = attr
}
+ return json
}
/**
* Instantiate a Range from a JSON object
* @memberof Range
* @param {Object} json A JSON object structured as:
- * `{"mathjs": "Range", "start": 2, "end": 4, "step": 1}`
+ * `{"mathjs": "Range", "start": 2, "step": 1, "length": 3}`
* @return {Range}
*/
Range.fromJSON = function (json) {
- return new Range(json.start, json.end, json.step)
+ const spec = {}
+ for (const key of attrs) {
+ const item = json[key]
+ if (item === undefined || item === null) continue
+ if (typeof item === 'object' && 'fromJSON' in item) {
+ spec[key] = item.fromJSON()
+ } else spec[key] = item
+ }
+ return new Range(spec)
}
+ Range.parseMethodMustWarn = true
+
return Range
}, { isClass: true })
diff --git a/src/type/matrix/SparseMatrix.js b/src/type/matrix/SparseMatrix.js
index 2eba606ce9..7761044d47 100644
--- a/src/type/matrix/SparseMatrix.js
+++ b/src/type/matrix/SparseMatrix.js
@@ -241,6 +241,14 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie
return rows !== 0 && columns !== 0 ? (this._index.length / (rows * columns)) : 0
}
+ /**
+ * Obtaining a layer (i.e. row) of a SparseMatrix is currently not
+ * supported, as only 2D SparseMatrix is implemented.
+ */
+ SparseMatrix.prototype.layer = function () {
+ throw new Error('SparseMatrix of dimensions != 2 not yet implemented')
+ }
+
/**
* Get a subset of the matrix, or replace a subset of the matrix.
*
@@ -255,8 +263,12 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie
* the matrix is resized. If not provided,
* new matrix elements will be filled with zeros.
*/
- SparseMatrix.prototype.subset = function (index, replacement, defaultValue) { // check it is a pattern matrix
- if (!this._values) { throw new Error('Cannot invoke subset on a Pattern only matrix') }
+ SparseMatrix.prototype.subset = function (index, replacement, defaultValue) {
+ // check if it is a pattern matrix [NB: Where is that term defined?]
+ if (!this._values) {
+ throw new Error('Cannot invoke subset on a Pattern only matrix')
+ }
+ if (isIndex(index)) index = Matrix.parseWithinIndex(index, this._size)
// check arguments
switch (arguments.length) {
diff --git a/src/type/matrix/function/index.js b/src/type/matrix/function/index.js
index a5540527f0..3b6071af5c 100644
--- a/src/type/matrix/function/index.js
+++ b/src/type/matrix/function/index.js
@@ -1,43 +1,70 @@
-import { isBigNumber, isMatrix, isArray } from '../../../utils/is.js'
+import { isBigNumber, isMatrix, isArray, isRange } from '../../../utils/is.js'
import { factory } from '../../../utils/factory.js'
const name = 'index'
-const dependencies = ['typed', 'Index']
+const dependencies = ['typed', 'Index', 'number', 'Range']
-export const createIndex = /* #__PURE__ */ factory(name, dependencies, ({ typed, Index }) => {
+export const createIndex = /* #__PURE__ */ factory(name, dependencies, ({ typed, Index, number, Range }) => {
/**
- * Create an index. An Index can store ranges having start, step, and end
- * for multiple dimensions.
- * Matrix.get, Matrix.set, and math.subset accept an Index as input.
+ * Create an Index. An Index can store a single position, a range of
+ * positions, or an arbitrary collection of positions, for each of possibly
+ * multiple dimensions. This sort of Index can be used to specify some entry
+ * or entries of a Matrix or Array, in which case the number of dimensions
+ * of the Index must equal the number of dimensions of the Matrix/Array.
+ * An Index can also store a single string for accessing a (plain JavaScript)
+ * object -- even if the object is nested, each layer must be indexed
+ * separately.
+ *
+ * Currently, the only place that Index values are used is in the
+ * `math.subset()` function (which see) to specify the collection of entries
+ * to be retrieved or replaced.
*
* Syntax:
*
- * math.index(range1, range2, ...)
+ * math.index(dim1, dim2, ...)
*
- * Where each range can be any of:
+ * where each dimension specifier can be any of:
*
- * - A number
- * - A string for getting/setting an object property
- * - An instance of `Range`
- * - A one-dimensional Array or a Matrix with numbers or booleans
+ * - A natural number (indicating just the one position that should be used
+ * along that dimension).
+ * - A **one**-dimensional Array or Matrix of natural numbers, listing the
+ * positions that should be used along that dimension. Note that repeated
+ * positions are supported, although _replacing_ a subset when the index
+ * has repeated positions may produce somewhat unintuitive results.
+ * For this option, the specifier will commonly be a Range (a Matrix
+ * with entries given by start, step, and end values), since often one
+ * wants a block of consecutive positions.
+ * - A one-dimensional Array or Matrix of booleans, in which case the
+ * positions in which `true` values appear are selected.
+ * - A string. In this case, if the Index is used to access a Matrix or
+ * Array, the string will be interpreted as a Range specification in
+ * the form `'start:end'` or `start:step:end`. On the other hand, if it
+ * is used to access a (plain JavaScript) object, the string value will
+ * be used directly.
*
- * Indexes must be zero-based, integer numbers.
+ * When used via the JavaScript API, the values in an Index are
+ * interpreted as zero-based positions, and Ranges exclude their endpoints.
+ * Conversely, when using `math.evaluate()` to compute a value from a string
+ * expression, Index positions are one-based, and Ranges include their
+ * endpoints.
*
* Examples:
*
* const b = [1, 2, 3, 4, 5]
- * math.subset(b, math.index([1, 2, 3])) // returns [2, 3, 4]
+ * math.subset(b, math.index([1, 2, 3])) // returns [2, 3, 4] ...
* math.subset(b, math.index([false, true, true, true, false])) // returns [2, 3, 4]
*
* const a = math.matrix([[1, 2], [3, 4]])
- * a.subset(math.index(0, 1)) // returns 2
- * a.subset(math.index(0, [false, true])) // returns 2
+ * a.subset(math.index(0, 1)) // returns 2 ...
+ * a.subset(math.index(0, [false, true])) // Matrix [2]
+ * math.evaluate('subset([1, 2; 3, 4], index(1, 2))') // returns 2
*
* See also:
*
- * bignumber, boolean, complex, matrix, number, string, unit
+ * subset, range, matrix
*
- * @param {...*} ranges Zero or more ranges or numbers.
+ * @param {...*} ranges
+ * Zero or more numbers, 1-D matrices (often ranges), or strings.
* @return {Index} Returns the created index
*/
return typed(name, {
@@ -45,6 +72,14 @@ export const createIndex = /* #__PURE__ */ factory(name, dependencies, ({ typed,
const ranges = args.map(function (arg) {
if (isBigNumber(arg)) {
return arg.toNumber() // convert BigNumber to Number
+ } else if (isRange(arg)) {
+ if (arg.datatype() !== 'number') {
+ return new Range({
+ start: number(arg.start),
+ step: number(arg.step),
+ length: arg.length
+ })
+ } else return arg
} else if (isArray(arg) || isMatrix(arg)) {
return arg.map(function (elem) {
// convert BigNumber to Number
diff --git a/src/type/matrix/function/matrix.js b/src/type/matrix/function/matrix.js
index 385301fc3d..e5daec356a 100644
--- a/src/type/matrix/function/matrix.js
+++ b/src/type/matrix/function/matrix.js
@@ -1,9 +1,11 @@
import { factory } from '../../../utils/factory.js'
+import { isRange } from '../../../utils/is.js'
const name = 'matrix'
-const dependencies = ['typed', 'Matrix', 'DenseMatrix', 'SparseMatrix']
+const dependencies = ['typed', 'Matrix', 'DenseMatrix', 'SparseMatrix', 'Range']
-export const createMatrix = /* #__PURE__ */ factory(name, dependencies, ({ typed, Matrix, DenseMatrix, SparseMatrix }) => {
+export const createMatrix = /* #__PURE__ */ factory(name, dependencies, ({ typed, Matrix, DenseMatrix, SparseMatrix, Range }) => {
+ const rangeCreator = new Range() // dummy to use to get access to .create()
/**
* Create a Matrix. The function creates a new `math.Matrix` object from
* an `Array`. A Matrix has utility functions to manipulate the data in the
@@ -22,10 +24,10 @@ export const createMatrix = /* #__PURE__ */ factory(name, dependencies, ({ typed
* Examples:
*
* let m = math.matrix([[1, 2], [3, 4]])
- * m.size() // Array [2, 2]
+ * m.size() // Array [2, 2] ...
* m.resize([3, 2], 5)
- * m.valueOf() // Array [[1, 2], [3, 4], [5, 5]]
- * m.get([1, 0]) // number 3
+ * m.valueOf() // Array [[1, 2], [3, 4], [5, 5]] ...
+ * m.get([1, 0]) // number 3
*
* See also:
*
@@ -81,6 +83,15 @@ export const createMatrix = /* #__PURE__ */ factory(name, dependencies, ({ typed
return new SparseMatrix(data, datatype)
}
+ if (format === 'range') {
+ if (isRange(data)) {
+ return new Range({
+ start: data.start, step: data.step, length: data.length
+ })
+ }
+ return rangeCreator.create(data)
+ }
+
throw new TypeError('Unknown matrix type ' + JSON.stringify(format) + '.')
}
})
diff --git a/src/type/matrix/function/sparse.js b/src/type/matrix/function/sparse.js
index 05941b8cc1..e599e4da6b 100644
--- a/src/type/matrix/function/sparse.js
+++ b/src/type/matrix/function/sparse.js
@@ -21,12 +21,13 @@ export const createSparse = /* #__PURE__ */ factory(name, dependencies, ({ typed
* Examples:
*
* let m = math.sparse([[1, 2], [3, 4]])
- * m.size() // Array [2, 2]
+ * m.size() // Array [2, 2] ...
* m.resize([3, 2], 5)
- * m.valueOf() // Array [[1, 2], [3, 4], [5, 5]]
- * m.get([1, 0]) // number 3
+ * m.valueOf() // Array [[1, 2], [3, 4], [5, 5]] ...
+ * m.get([1, 0]) // number 3
+ *
* let v = math.sparse([0, 0, 1])
- * v.size() // Array [3, 1]
+ * v.size() // Array [3, 1] ...
* v.get([2, 0]) // number 1
*
* See also:
diff --git a/src/type/matrix/utils/matAlgo14xDs.js b/src/type/matrix/utils/matAlgo14xDs.js
index 342b2b19d9..bd8a773dd4 100644
--- a/src/type/matrix/utils/matAlgo14xDs.js
+++ b/src/type/matrix/utils/matAlgo14xDs.js
@@ -1,5 +1,5 @@
import { factory } from '../../../utils/factory.js'
-import { clone } from '../../../utils/object.js'
+import { isDenseMatrix } from '../../../utils/is.js'
const name = 'matAlgo14xDs'
const dependencies = ['typed']
@@ -21,10 +21,11 @@ export const createMatAlgo14xDs = /* #__PURE__ */ factory(name, dependencies, ({
* https://github.com/josdejong/mathjs/pull/346#issuecomment-97659042
*/
return function matAlgo14xDs (a, b, callback, inverse) {
+ const dense = isDenseMatrix(a)
// a arrays
- const adata = a._data
- const asize = a._size
- const adt = a._datatype
+ const adata = dense ? a._data : a.valueOf()
+ const asize = dense ? a._size : a.size()
+ const adt = dense ? a._datatype : a.datatype()
// datatype
let dt
@@ -45,11 +46,7 @@ export const createMatAlgo14xDs = /* #__PURE__ */ factory(name, dependencies, ({
const cdata = asize.length > 0 ? _iterate(cf, 0, asize, asize[0], adata, b, inverse) : []
// c matrix
- return a.createDenseMatrix({
- data: cdata,
- size: clone(asize),
- datatype: dt
- })
+ return a.create(cdata, dt)
}
// recursive function
diff --git a/src/type/matrix/utils/matrixAlgorithmSuite.js b/src/type/matrix/utils/matrixAlgorithmSuite.js
index 216935b20e..3092d98221 100644
--- a/src/type/matrix/utils/matrixAlgorithmSuite.js
+++ b/src/type/matrix/utils/matrixAlgorithmSuite.js
@@ -5,12 +5,13 @@ import { createMatAlgo14xDs } from './matAlgo14xDs.js'
import { broadcast } from './broadcast.js'
const name = 'matrixAlgorithmSuite'
-const dependencies = ['typed', 'matrix']
+const dependencies = ['typed', 'DenseMatrix']
export const createMatrixAlgorithmSuite = /* #__PURE__ */ factory(
- name, dependencies, ({ typed, matrix }) => {
+ name, dependencies, ({ typed, DenseMatrix }) => {
const matAlgo13xDD = createMatAlgo13xDD({ typed })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
+ const mat = a => new DenseMatrix(a)
/**
* Return a signatures object with the usual boilerplate of
@@ -38,9 +39,9 @@ export const createMatrixAlgorithmSuite = /* #__PURE__ */ factory(
matrixSignatures = {
'DenseMatrix, DenseMatrix': (x, y) => matAlgo13xDD(...broadcast(x, y), elop),
'Array, Array': (x, y) =>
- matAlgo13xDD(...broadcast(matrix(x), matrix(y)), elop).valueOf(),
- 'Array, DenseMatrix': (x, y) => matAlgo13xDD(...broadcast(matrix(x), y), elop),
- 'DenseMatrix, Array': (x, y) => matAlgo13xDD(...broadcast(x, matrix(y)), elop)
+ matAlgo13xDD(...broadcast(mat(x), mat(y)), elop).valueOf(),
+ 'Array, DenseMatrix': (x, y) => matAlgo13xDD(...broadcast(mat(x), y), elop),
+ 'DenseMatrix, Array': (x, y) => matAlgo13xDD(...broadcast(x, mat(y)), elop)
}
// Now incorporate sparse matrices
if (options.SS) {
@@ -51,13 +52,13 @@ export const createMatrixAlgorithmSuite = /* #__PURE__ */ factory(
matrixSignatures['DenseMatrix, SparseMatrix'] =
(x, y) => options.DS(...broadcast(x, y), elop, false)
matrixSignatures['Array, SparseMatrix'] =
- (x, y) => options.DS(...broadcast(matrix(x), y), elop, false)
+ (x, y) => options.DS(...broadcast(mat(x), y), elop, false)
}
if (SD) {
matrixSignatures['SparseMatrix, DenseMatrix'] =
(x, y) => SD(...broadcast(y, x), elop, true)
matrixSignatures['SparseMatrix, Array'] =
- (x, y) => SD(...broadcast(matrix(y), x), elop, true)
+ (x, y) => SD(...broadcast(mat(y), x), elop, true)
}
} else {
// No elop, use this
@@ -67,13 +68,13 @@ export const createMatrixAlgorithmSuite = /* #__PURE__ */ factory(
return matAlgo13xDD(...broadcast(x, y), self)
}),
'Array, Array': typed.referToSelf(self => (x, y) => {
- return matAlgo13xDD(...broadcast(matrix(x), matrix(y)), self).valueOf()
+ return matAlgo13xDD(...broadcast(mat(x), mat(y)), self).valueOf()
}),
'Array, DenseMatrix': typed.referToSelf(self => (x, y) => {
- return matAlgo13xDD(...broadcast(matrix(x), y), self)
+ return matAlgo13xDD(...broadcast(mat(x), y), self)
}),
'DenseMatrix, Array': typed.referToSelf(self => (x, y) => {
- return matAlgo13xDD(...broadcast(x, matrix(y)), self)
+ return matAlgo13xDD(...broadcast(x, mat(y)), self)
})
}
// Now incorporate sparse matrices
@@ -90,7 +91,7 @@ export const createMatrixAlgorithmSuite = /* #__PURE__ */ factory(
})
matrixSignatures['Array, SparseMatrix'] =
typed.referToSelf(self => (x, y) => {
- return options.DS(...broadcast(matrix(x), y), self, false)
+ return options.DS(...broadcast(mat(x), y), self, false)
})
}
if (SD) {
@@ -100,7 +101,7 @@ export const createMatrixAlgorithmSuite = /* #__PURE__ */ factory(
})
matrixSignatures['SparseMatrix, Array'] =
typed.referToSelf(self => (x, y) => {
- return SD(...broadcast(matrix(y), x), self, true)
+ return SD(...broadcast(mat(y), x), self, true)
})
}
}
@@ -115,9 +116,9 @@ export const createMatrixAlgorithmSuite = /* #__PURE__ */ factory(
matrixSignatures[scalar + ', DenseMatrix'] =
(x, y) => matAlgo14xDs(y, x, elop, true)
matrixSignatures['Array,' + scalar] =
- (x, y) => matAlgo14xDs(matrix(x), y, elop, false).valueOf()
+ (x, y) => matAlgo14xDs(mat(x), y, elop, false).valueOf()
matrixSignatures[scalar + ', Array'] =
- (x, y) => matAlgo14xDs(matrix(y), x, elop, true).valueOf()
+ (x, y) => matAlgo14xDs(mat(y), x, elop, true).valueOf()
} else {
matrixSignatures['DenseMatrix,' + scalar] =
typed.referToSelf(self => (x, y) => {
@@ -129,11 +130,11 @@ export const createMatrixAlgorithmSuite = /* #__PURE__ */ factory(
})
matrixSignatures['Array,' + scalar] =
typed.referToSelf(self => (x, y) => {
- return matAlgo14xDs(matrix(x), y, self, false).valueOf()
+ return matAlgo14xDs(mat(x), y, self, false).valueOf()
})
matrixSignatures[scalar + ', Array'] =
typed.referToSelf(self => (x, y) => {
- return matAlgo14xDs(matrix(y), x, self, true).valueOf()
+ return matAlgo14xDs(mat(y), x, self, true).valueOf()
})
}
}
diff --git a/src/type/unit/Unit.js b/src/type/unit/Unit.js
index daebd92a87..89d6c60821 100644
--- a/src/type/unit/Unit.js
+++ b/src/type/unit/Unit.js
@@ -13,6 +13,7 @@ const dependencies = [
'subtractScalar',
'multiplyScalar',
'divideScalar',
+ 'oneUnitless',
'pow',
'abs',
'fix',
@@ -33,6 +34,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({
subtractScalar,
multiplyScalar,
divideScalar,
+ oneUnitless,
pow,
abs,
fix,
@@ -45,6 +47,7 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({
BigNumber,
Fraction
}) => {
+ const one = oneUnitless
const toNumber = number
const fixPrefixDefault = false
const skipAutomaticSimplificationDefault = true
@@ -624,6 +627,17 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({
return true
}
+ /**
+ * Returns true if this unit instance is unitless, i.e., a number with
+ * no dimension.
+ *
+ * @memberof Unit
+ * @return {boolean} true if unitless
+ */
+ Unit.prototype.unitless = function () {
+ return this.equalBase(Unit.BASE_UNITS.NONE)
+ }
+
/**
* Check if this unit equals another unit
* @memberof Unit
@@ -776,22 +790,6 @@ export const createUnitClass = /* #__PURE__ */ factory(name, dependencies, ({
}
}
- /**
- * Create a value one with the numeric type of `typeOfValue`.
- * For example, `one(new BigNumber(3))` returns `BigNumber(1)`
- * @param {number | Fraction | BigNumber} typeOfValue
- * @returns {number | Fraction | BigNumber}
- */
- function one (typeOfValue) {
- // TODO: this is a workaround to prevent the following BigNumber conversion error from throwing:
- // "TypeError: Cannot implicitly convert a number with >15 significant digits to BigNumber"
- // see https://github.com/josdejong/mathjs/issues/3450
- // https://github.com/josdejong/mathjs/pull/3375
- const convert = Unit._getNumberConverter(typeOf(typeOfValue))
-
- return convert(1)
- }
-
/**
* Calculate the absolute value of a unit
* @memberof Unit
diff --git a/src/type/unit/function/unit.js b/src/type/unit/function/unit.js
index bae05a6b79..49018901ce 100644
--- a/src/type/unit/function/unit.js
+++ b/src/type/unit/function/unit.js
@@ -19,12 +19,17 @@ export const createUnitFunction = /* #__PURE__ */ factory(name, dependencies, ({
*
* Examples:
*
- * const kph = math.unit('km/h') // returns Unit km/h (valueless)
- * const v = math.unit(25, kph) // returns Unit 25 km/h
- * const a = math.unit(5, 'cm') // returns Unit 50 mm
- * const b = math.unit('23 kg') // returns Unit 23 kg
+ * // A valueless unit:
+ * const kph = math.unit('km/h')
+ * kph // returns Unit km/h ...
+ * math.unit(25, kph) // returns Unit 25 km/h
+ *
+ * const a = math.unit(5, 'cm')
+ * a // returns Unit 5 cm ...
* a.to('m') // returns Unit 0.05 m
*
+ * math.unit('23 kg') // returns Unit 23 kg
+ *
* See also:
*
* bignumber, boolean, complex, index, matrix, number, string, createUnit
diff --git a/src/utils/collection.js b/src/utils/collection.js
index ecdd6a30ad..7b6f7a12c6 100644
--- a/src/utils/collection.js
+++ b/src/utils/collection.js
@@ -176,3 +176,31 @@ export function scatter (a, j, w, x, u, mark, cindex, f, inverse, update, value)
}
}
}
+
+/**
+ * Simple parser for the `start[:step]:end` notation
+ * used for ranges and the like, when the full parser is not being
+ * used. Produces a plain object with properties 'start', 'step',
+ * 'end'. The property values are returned as strings, since they may be
+ * converted into numeric values differently in different locations.
+ * Missing parts are represented by the empty string.
+ *
+ * @param {string} notation to be parsed
+ * @return {object} fields found, or null if the notation was unparseable
+ */
+export function parseRange (str) {
+ const args = str.split(':')
+ const retval = { start: args[0], step: '' }
+ switch (args.length) {
+ case 2:
+ retval.end = args[1]
+ return retval
+ case 3:
+ // Empty _middle_ part is a syntax error; only start/end can be elided
+ if (args[1] === '') return null
+ retval.step = args[1]
+ retval.end = args[2]
+ return retval
+ }
+ return null
+}
diff --git a/src/utils/product.js b/src/utils/product.js
index d3e0fa5bdf..9d9a6ffa84 100644
--- a/src/utils/product.js
+++ b/src/utils/product.js
@@ -1,16 +1,14 @@
-/** @param {number} i
- * @param {number} n
- * @returns {number} product of i to n
+/** @param {number | bigint} i
+ * @param {number | bigint} n [NB: same type as i]
+ * @returns {number | bigint} product of i to n
*/
export function product (i, n) {
- if (n < i) {
- return 1
- }
+ if (i <= 0) return i - i // always 0, for number or bigint
+ // Which is faster: `const one = typeof i === 'number' ? 1 : 1n`, or:
+ const one = i / i // always has the proper type
+ if (n < i) return one
+ if (n === i) return n
- if (n === i) {
- return n
- }
-
- const half = (n + i) >> 1 // divide (n + i) by 2 and truncate to integer
- return product(i, half) * product(half + 1, n)
+ const half = (n + i) >> one // divide (n + i) by 2 and truncate to integer
+ return product(i, half) * product(half + one, n)
}
diff --git a/src/utils/snapshot.js b/src/utils/snapshot.js
index f058da2946..bcc799acda 100644
--- a/src/utils/snapshot.js
+++ b/src/utils/snapshot.js
@@ -155,6 +155,7 @@ export function createSnapshotFromFactories (factories) {
'multiplyScalar',
'print',
'divideScalar',
+ 'oneUnitless',
'parse',
'compile',
'parser',
diff --git a/test/benchmark/factorial.js b/test/benchmark/factorial.js
index 19437d6d80..56352ed913 100644
--- a/test/benchmark/factorial.js
+++ b/test/benchmark/factorial.js
@@ -1,5 +1,6 @@
import BigNumber from 'decimal.js'
import { Bench } from 'tinybench'
+import { prod, Range } from '../../lib/esm/index.js'
import { formatTaskResult } from './utils/formatTaskResult.js'
const results = []
@@ -41,51 +42,122 @@ function betterFactorial (n) {
return prod
}
+function splitFactorial (m, n) {
+ if (n - m < 9) {
+ let product = new BigNumber(m)
+ while (++m <= n) product = product.mul(new BigNumber(m))
+ return product
+ }
+ const split = Math.floor((m + n) / 2)
+ return splitFactorial(m, split).mul(splitFactorial(split + 1, n))
+}
+
+function prodFactorial (n) {
+ return prod(new Range({ start: new BigNumber(1), length: n }))
+}
+
const bench = new Bench({ time: 100, iterations: 100 })
- .add('bigFactorial for small numbers', function () {
+ .add('bigFactorial for 8', function () {
const res = bigFactorial(new BigNumber(8))
results.push(res)
})
- .add('new bigFactorial for small numbers', function () {
+ .add('new bigFactorial for 8', function () {
const res = betterFactorial(new BigNumber(8))
results.push(res)
})
+ .add('split factorial for 8', function () {
+ const res = splitFactorial(1, 8)
+ results.push(res)
+ })
+ .add('prod range for 8', function () {
+ const res = prodFactorial(8)
+ results.push(res)
+ })
- .add('bigFactorial for small numbers 2', function () {
+ .add('bigFactorial for 20', function () {
const res = bigFactorial(new BigNumber(20))
results.push(res)
})
- .add('new bigFactorial for small numbers 2', function () {
+ .add('new bigFactorial for 20', function () {
const res = betterFactorial(new BigNumber(20))
results.push(res)
})
+ .add('split factorial for 20', function () {
+ const res = splitFactorial(1, 20)
+ results.push(res)
+ })
+ .add('prod range for 20', function () {
+ const res = prodFactorial(20)
+ results.push(res)
+ })
- .add('bigFactorial for big numbers', function () {
+ .add('bigFactorial for 600', function () {
const res = bigFactorial(new BigNumber(600))
results.push(res)
})
- .add('new bigFactorial for big numbers', function () {
+ .add('new bigFactorial for 600', function () {
const res = betterFactorial(new BigNumber(600))
results.push(res)
})
+ .add('split factorial for 600', function () {
+ const res = splitFactorial(1, 600)
+ results.push(res)
+ })
+ .add('prod range for 600', function () {
+ const res = prodFactorial(600)
+ results.push(res)
+ })
- .add('bigFactorial for HUGE numbers', function () {
+ .add('bigFactorial for 1.5K', function () {
const res = bigFactorial(new BigNumber(1500))
results.push(res)
})
- .add('new bigFactorial for HUGE numbers', function () {
+ .add('new bigFactorial for 1.5K', function () {
const res = betterFactorial(new BigNumber(1500))
results.push(res)
})
+ .add('split factorial for 1.5K', function () {
+ const res = splitFactorial(1, 1500)
+ results.push(res)
+ })
+ .add('prod range for 1.5K', function () {
+ const res = prodFactorial(1500)
+ results.push(res)
+ })
- .add('bigFactorial for "HUGER" numbers', function () {
+ .add('bigFactorial for 10K', function () {
const res = bigFactorial(new BigNumber(10000))
results.push(res)
})
- .add('new bigFactorial for "HUGER" numbers', function () {
+ .add('new bigFactorial for 10K', function () {
const res = betterFactorial(new BigNumber(10000))
results.push(res)
})
+ .add('split factorial for 10K', function () {
+ const res = splitFactorial(1, 10000)
+ results.push(res)
+ })
+ .add('prod range for 10K', function () {
+ const res = prodFactorial(10000)
+ results.push(res)
+ })
+
+ .add('bigFactorial for 30K', function () {
+ const res = bigFactorial(new BigNumber(30000))
+ results.push(res)
+ })
+ .add('new bigFactorial for 30K', function () {
+ const res = betterFactorial(new BigNumber(30000))
+ results.push(res)
+ })
+ .add('split factorial for 30K', function () {
+ const res = splitFactorial(1, 30000)
+ results.push(res)
+ })
+ .add('prod range for 30K', function () {
+ const res = prodFactorial(30000)
+ results.push(res)
+ })
bench.addEventListener('cycle', (event) => console.log(formatTaskResult(bench, event.task)))
await bench.run()
diff --git a/test/node-tests/browser.test.cjs b/test/node-tests/browser.test.cjs
index 67906a26e2..537758b594 100644
--- a/test/node-tests/browser.test.cjs
+++ b/test/node-tests/browser.test.cjs
@@ -99,7 +99,8 @@ describe('lib/browser', function () {
'compile', 'parse', 'parser', // TODO: add embedded docs for compile, parse, and parser?
'reviver', 'replacer', // TODO: add embedded docs for reviver and replacer?
'apply', // FIXME: apply is not supported right now because of security concerns
- 'addScalar', 'subtractScalar', 'divideScalar', 'multiplyScalar', 'equalScalar'
+ 'addScalar', 'subtractScalar', 'divideScalar', 'multiplyScalar', 'equalScalar',
+ 'oneUnitless'
]
// test whether all functions are documented
diff --git a/test/node-tests/doc.test.js b/test/node-tests/doc.test.js
index 01e3ee8fd5..da250d95f0 100644
--- a/test/node-tests/doc.test.js
+++ b/test/node-tests/doc.test.js
@@ -40,7 +40,7 @@ function extractValue (spec) {
}
const keywords = {
number: 'Number(_)',
- BigNumber: 'math.bignumber(_)',
+ BigNumber: "math.bignumber('_')",
Fraction: 'math.fraction(_)',
Complex: "math.complex('_')",
Unit: "math.unit('_')",
@@ -92,10 +92,10 @@ const ignoreFunctions = new Set([
])
const knownProblems = new Set([
- 'setUnion', 'unequal', 'equal', 'deepEqual', 'compareNatural', 'randomInt',
+ 'unequal', 'equal', 'deepEqual', 'compareNatural', 'randomInt',
'random', 'pickRandom', 'kldivergence',
'parser', 'compile', 're', 'im',
- 'subset', 'squeeze', 'rotationMatrix',
+ 'rotationMatrix',
'rotate', 'reshape', 'partitionSelect', 'matrixFromFunction',
'matrixFromColumns', 'getMatrixDataType', 'eigs', 'diff',
'nthRoots', 'nthRoot',
@@ -104,7 +104,6 @@ const knownProblems = new Set([
'rationalize', 'qr', 'lusolve', 'lup', 'derivative',
'symbolicEqual', 'schur', 'sylvester', 'freqz', 'round',
'import', 'typed',
- 'unit', 'sparse', 'matrix', 'index', 'bignumber', 'fraction', 'complex',
'parse'
])
@@ -175,6 +174,7 @@ function checkExpectation (want, got) {
const OKundocumented = new Set([
'apply', // deprecated backwards-compatibility synonym of mapSlices
'addScalar', 'subtractScalar', 'divideScalar', 'multiplyScalar', 'equalScalar',
+ 'oneUnitless',
'docs', 'FibonacciHeap',
'IndexError', 'DimensionError', 'ArgumentsError'
])
@@ -410,8 +410,13 @@ describe('Testing examples from (jsdoc) comments', function () {
if (accumulation) { accumulation += '\n' }
accumulation += parts[0]
}
+ let resetAccumulation = true
if (accumulation !== '' && expectation === undefined) {
expectationFrom = parts[1]
+ if (expectationFrom.endsWith('...')) {
+ expectationFrom = expectationFrom.slice(0, -3).trimEnd()
+ resetAccumulation = false
+ }
expectation = extractExpectation(expectationFrom)
parts[1] = ''
}
@@ -425,7 +430,7 @@ describe('Testing examples from (jsdoc) comments', function () {
}
maybeCheckExpectation(
doc.name, expectation, expectationFrom, value, accumulation)
- accumulation = ''
+ if (resetAccumulation) accumulation = ''
}
expectationFrom = parts[1]
expectation = extractExpectation(expectationFrom, 'requireSignal')
diff --git a/test/node-tests/treeShaking/treeShaking.test.js b/test/node-tests/treeShaking/treeShaking.test.js
index 9d2ba40cc8..35cc94abfb 100644
--- a/test/node-tests/treeShaking/treeShaking.test.js
+++ b/test/node-tests/treeShaking/treeShaking.test.js
@@ -63,12 +63,17 @@ describe('tree shaking', function () {
}
// Test whether the size is small enough
- // At this moment, the full library size is 559137 bytes (unzipped),
- // and the size of this tree-shaken bundle is 98494 bytes (unzipped)
- // this may grow or shrink in the future
+ // When this was first written, the full library size wass 559137 bytes
+ // (unzipped), and the size of this tree-shaken bundle was 98494 bytes
+ // (unzipped). These values generally grow slowly as features are added
+ // to the library; they can sometimes shrink if the code is made more
+ // efficient. But in general, a slight increase in the tree-shaken
+ // bundle is not a problem, and the maxSize can be bumped up to
+ // accommodate. But a sudden jump in size indicates a tree-shaking/
+ // bundling problem.
assert.strictEqual(info.assets[0].name, bundleName)
const size = info.assets[0].size
- const maxSize = 135000
+ const maxSize = 137000
assert(size < maxSize,
'bundled size must be small enough ' +
'(actual size: ' + size + ' bytes, max size: ' + maxSize + ' bytes)')
diff --git a/test/typescript-tests/testTypes.ts b/test/typescript-tests/testTypes.ts
index 4b38e7710c..6f9116c373 100644
--- a/test/typescript-tests/testTypes.ts
+++ b/test/typescript-tests/testTypes.ts
@@ -85,15 +85,20 @@ Basic usage examples
math.sqrt(-4)
math.pow(m2by2, 2)
+ // @ts-expect-error: since one(number) returns 1, this comparison must fail
+ if (math.one(3.5) === 2) {
+ console.error('This should not happen')
+ }
+ // @ts-expect-error: since zero(bigint) returns 0n, this comparison fails
+ if (math.zero(-23n) === 1n) {
+ console.error('Nor should this happen')
+ }
const angle = 0.2
math.add(math.pow(math.sin(angle), 2), math.pow(math.cos(angle), 2))
math.add(2, 3, 4)
math.add(2, 3, math.bignumber(4))
- // @ts-expect-error: string arguments are not supported by the types, but it works (if the string contains a number)
math.add(2, '3')
- // @ts-expect-error: string arguments are not supported by the types, but it works (if the string contains a number), but should throw an error if it is something else
assert.throws(() => math.add(2, '3 + 5'))
- // @ts-expect-error: string arguments are not supported by the types, but it works (if the string contains a number), but should throw an error if it is something else
assert.throws(() => math.add(2, '3 cm'))
// @ts-expect-error: no arguments are not supported by the types, and should throw an error
assert.throws(() => math.add())
@@ -102,11 +107,8 @@ Basic usage examples
math.multiply(2, 3, 4)
math.multiply(2, 3, math.bignumber(4))
- // @ts-expect-error: string arguments are not supported by the types, but it works (if the string contains a number)
math.multiply(2, '2') // currently not supported by the types, but turns out to work
- // @ts-expect-error: string arguments are not supported by the types, but it works (if the string contains a number), but should throw an error if it is something else
assert.throws(() => math.multiply(2, '3 + 5'))
- // @ts-expect-error: string arguments are not supported by the types, but it works (if the string contains a number), but should throw an error if it is something else
assert.throws(() => math.multiply(2, '3 cm'))
// @ts-expect-error: no arguments are not supported by the types, and should throw an error
assert.throws(() => math.multiply())
@@ -782,6 +784,17 @@ Chaining examples
.dotDivide(2)
).toMatchTypeOf>()
+ // scalarDivide
+ expectTypeOf(math.chain(1).scalarDivide(2)).toMatchTypeOf<
+ MathJsChain
+ >()
+ expectTypeOf(math.chain([1, 2, 3]).scalarDivide(3)).toMatchTypeOf<
+ MathJsChain
+ >()
+ expectTypeOf(math.chain([2, 5, 6]).scalarDivide([1, 2, 3])).toMatchTypeOf<
+ MathJsChain
+ >()
+
// dotMultiply
expectTypeOf(math.chain(1).dotMultiply(2)).toMatchTypeOf<
MathJsChain
@@ -997,7 +1010,7 @@ Chaining examples
MathJsChain
>()
expectTypeOf(math.chain([1, Infinity]).isFinite()).toMatchTypeOf<
- MathJsChain
+ MathJsChain>
>()
// bernoulli
@@ -1399,8 +1412,8 @@ Matrices examples
// create matrices and arrays. a matrix is just a wrapper around an Array,
// providing some handy utilities.
- const a: Matrix = math.matrix([1, 4, 9, 16, 25])
- const b: Matrix = math.matrix(math.ones([2, 3]))
+ const a: Matrix = math.matrix([1, 4, 9, 16, 25])
+ const b: Matrix = math.ones(math.matrix([2, 3]))
b.size()
// @ts-expect-error ... ones() in a chain cannot take more dimensions
@@ -1444,17 +1457,17 @@ Matrices examples
(i: number[]) => math.fraction(i[0], i[1] + 1),
'dense'
)
- const j: number[][] = math.matrixFromRows(
+ const j: Matrix = math.matrixFromRows(
[1, 2, 3],
math.matrix([[4], [5], [6]])
)
- assert.strictEqual(j[1][2], 6)
+ assert.strictEqual(j.get([1, 2]), 6)
const _k: Matrix = math.matrixFromRows(f, math.row(h, 1))
- const l: number[][] = math.matrixFromColumns(
+ const l: Matrix = math.matrixFromColumns(
[1, 2, 3],
math.matrix([[4], [5], [6]])
)
- assert.strictEqual(l[2][1], 6)
+ assert.strictEqual(l.get([2, 1]), 6)
const _m: Matrix = math.matrixFromColumns(f, math.row(h, 1))
}
@@ -1679,8 +1692,8 @@ Math types examples: Type results after multiplying 'MathTypes' with matrices
{
const math = create(all, {})
- const abc: MathArray = [1, 2, 3, 4]
- const bcd: MathArray = [
+ const abc = [1, 2, 3, 4]
+ const bcd = [
[1, 2, 3, 4],
[2, 3, 4, 5],
[4, 5, 6, 7],
@@ -1694,7 +1707,7 @@ Math types examples: Type results after multiplying 'MathTypes' with matrices
const hij: number[][] = [[1], [2], [3], [4]]
const ijk: number[][] = [[1, 2, 3, 4]]
- const Mbcd = math.matrix(bcd)
+ const Mbcd: Matrix = math.matrix(bcd)
const Mabc = math.matrix(abc)
// Number
@@ -1894,6 +1907,8 @@ Units examples
math.multiply(b, 2)
math.divide(math.unit('1 m'), math.unit('1 s'))
math.pow(math.unit('12 in'), 3)
+ math.one(math.unit('5m'))
+ math.zero(math.unit('22 m/secs^2'))
// units can be converted to a specific type, or to a number
b.to('cm')
@@ -3014,7 +3029,7 @@ Statistics functions' return types
],
1
)
- ).toMatchTypeOf()
+ ).toMatchTypeOf>()
expectTypeOf(math.max(1, 2, 3)).toMatchTypeOf()
expectTypeOf(math.max([1, 2, 3])).toMatchTypeOf()
@@ -3061,23 +3076,27 @@ Statistics functions' return types
number | BigNumber | bigint | Fraction | Complex | Unit
>()
- expectTypeOf(math.quantileSeq([1, 2, 3], 0.75)).toMatchTypeOf()
+ expectTypeOf(math.quantileSeq([1, 2, 3], 0.75)).toMatchTypeOf<
+ number | MathCollection
+ >()
expectTypeOf(math.quantileSeq([1, 2, 3, 4, 5], [0.25, 0.75])).toMatchTypeOf<
- MathArray | MathScalarType
+ MathCollection
>()
expectTypeOf(
math.quantileSeq([1, 2, 3, 4, 5], [0.25, 0.75]) as number[]
).toMatchTypeOf()
- expectTypeOf(math.quantileSeq([[1, 2, 3]], 0.75)).toMatchTypeOf()
- expectTypeOf(
- math.quantileSeq([math.bignumber('123')], 0.75)
- ).toMatchTypeOf()
+ expectTypeOf(math.quantileSeq([[1, 2, 3]], 0.75)).toMatchTypeOf<
+ number | MathCollection
+ >()
+ expectTypeOf(math.quantileSeq([math.bignumber('123')], 0.75)).toMatchTypeOf<
+ BigNumber | MathCollection
+ >()
expectTypeOf(math.quantileSeq(math.matrix([1, 2, 3]), 0.75)).toMatchTypeOf<
- MathScalarType | MathArray
+ number | MathCollection
>()
expectTypeOf(
math.quantileSeq([math.unit('5cm'), math.unit('10cm')], 0.75)
- ).toMatchTypeOf()
+ ).toMatchTypeOf>()
}
/*
diff --git a/test/unit-tests/core/typed.test.js b/test/unit-tests/core/typed.test.js
index 39908b3c3b..6184db2529 100644
--- a/test/unit-tests/core/typed.test.js
+++ b/test/unit-tests/core/typed.test.js
@@ -370,7 +370,7 @@ describe('typed', function () {
assert.strictEqual(math.isChain(), false)
})
- it('should convert a bigint to number if possible', function () {
+ it('should convert a bigint/Fraction to number if possible', function () {
const double = math.typed('double', {
number: (x) => x + x
})
@@ -378,6 +378,7 @@ describe('typed', function () {
assert.strictEqual(double(2), 4)
assert.strictEqual(double(2n), 4)
assert.throws(() => double(12345678901234567890n), /value exceeds the max safe integer/)
+ assert.strictEqual(double(math.fraction(5, 2)), 5)
})
it('should convert a bigint to BigNumber', function () {
@@ -390,12 +391,14 @@ describe('typed', function () {
assert.deepStrictEqual(double(12345678901234567890n), math.bignumber('24691357802469135780'))
})
- it('should convert a bigint to Fraction', function () {
+ it('should convert a bigint or number to Fraction', function () {
+ const frac4 = math.fraction(4)
const double = math.typed('double', {
Fraction: (x) => x.add(x)
})
- assert.deepStrictEqual(double(math.fraction(2)), math.fraction(4))
- assert.deepStrictEqual(double(2n), math.fraction(4))
+ assert.deepStrictEqual(double(math.fraction(2)), frac4)
+ assert.deepStrictEqual(double(2n), frac4)
+ assert.deepStrictEqual(double(2), frac4)
})
})
diff --git a/test/unit-tests/expression/function/help.test.js b/test/unit-tests/expression/function/help.test.js
index adc65100ec..2d12933abd 100644
--- a/test/unit-tests/expression/function/help.test.js
+++ b/test/unit-tests/expression/function/help.test.js
@@ -5,8 +5,9 @@ import { embeddedDocs } from '../../../../src/expression/embeddedDocs/embeddedDo
let mathDocs = math.create(math.all)
const originalConfig = mathDocs.config()
// Add names to the skipDocs array if they are not meant to have embedded docs
-const skipDocs = new Set(['import', 'addScalar', 'divideScalar', 'equalScalar', 'multiplyScalar',
- 'subtractScalar', 'apply', 'replacer', 'reviver'])
+const skipDocs = new Set([
+ 'import', 'addScalar', 'divideScalar', 'equalScalar', 'multiplyScalar',
+ 'subtractScalar', 'oneUnitless', 'apply', 'replacer', 'reviver'])
// Add names to skipExamples if their examples in the embedded docs contain acceptable errors
const skipExamples = new Set([])
diff --git a/test/unit-tests/expression/node/RangeNode.test.js b/test/unit-tests/expression/node/RangeNode.test.js
index 97e7b2056d..808b3e60dd 100644
--- a/test/unit-tests/expression/node/RangeNode.test.js
+++ b/test/unit-tests/expression/node/RangeNode.test.js
@@ -6,6 +6,7 @@ const Node = math.Node
const ConstantNode = math.ConstantNode
const SymbolNode = math.SymbolNode
const RangeNode = math.RangeNode
+const Range = math.Range
const OperatorNode = math.OperatorNode
describe('RangeNode', function () {
@@ -50,7 +51,8 @@ describe('RangeNode', function () {
const n = new RangeNode(start, end, step)
const expr = n.compile()
- assert.deepStrictEqual(expr.evaluate(), math.matrix([0, 2, 4, 6, 8, 10]))
+ assert.deepStrictEqual(
+ expr.evaluate(), new Range({ start: 0, step: 2, last: 10 }))
})
it('should filter a RangeNode', function () {
diff --git a/test/unit-tests/expression/parse.test.js b/test/unit-tests/expression/parse.test.js
index 444247244f..eef4d81612 100644
--- a/test/unit-tests/expression/parse.test.js
+++ b/test/unit-tests/expression/parse.test.js
@@ -174,8 +174,10 @@ describe('parse', function () {
})
it('should spread a range over multiple lines', function () {
- assert.deepStrictEqual(parse('2:\n4').compile().evaluate(), math.matrix([2, 3, 4]))
- assert.deepStrictEqual(parse('2:\n2:\n6').compile().evaluate(), math.matrix([2, 4, 6]))
+ assert.deepStrictEqual(
+ parse('2:\n4').compile().evaluate(), math.range(2, 5))
+ assert.deepStrictEqual(
+ parse('2:\n2:\n6').compile().evaluate(), math.range(2, 7, 2))
})
it('should spread an index over multiple lines', function () {
@@ -850,9 +852,9 @@ describe('parse', function () {
assert.deepStrictEqual(parseAndEval('c=concat([[1,2]], [[3,4]], 1)', scope), math.matrix([[1, 2], [3, 4]]))
assert.deepStrictEqual(parseAndEval('c=concat([[1,2]], [[3,4]], 2)', scope), math.matrix([[1, 2, 3, 4]]))
assert.deepStrictEqual(parseAndEval('c=concat([[1]], [2;3], 1)', scope), math.matrix([[1], [2], [3]]))
- assert.deepStrictEqual(parseAndEval('d=1:3', scope), math.matrix([1, 2, 3]))
+ assert.deepStrictEqual(parseAndEval('d=1:3', scope), math.range(1, 4))
assert.deepStrictEqual(parseAndEval('concat(d,d)', scope), math.matrix([1, 2, 3, 1, 2, 3]))
- assert.deepStrictEqual(parseAndEval('e=1+d', scope), math.matrix([2, 3, 4]))
+ assert.deepStrictEqual(parseAndEval('e=1+d', scope), math.range(2, 5))
assert.deepStrictEqual(parseAndEval('size(e)', scope), [3])
assert.deepStrictEqual(parseAndEval('concat(e,e)', scope), math.matrix([2, 3, 4, 2, 3, 4]))
assert.deepStrictEqual(parseAndEval('[[],[]]', scope), math.matrix([[], []]))
@@ -888,6 +890,19 @@ describe('parse', function () {
const scope = {}
assert.throws(function () { parseAndEval('c=concat(a, [1,2,3])', scope) })
})
+
+ it(
+ 'should interpret comma-separated expressions in parentheses as arrays',
+ function () {
+ assert.deepStrictEqual(parseAndEval('(3,4,5)'), [3, 4, 5])
+ assert.deepStrictEqual(parseAndEval('(5,12,13,)'), [5, 12, 13])
+ assert.deepStrictEqual(parseAndEval('(5,)'), [5])
+ assert.deepStrictEqual(parseAndEval('()'), [])
+ assert.strictEqual(parseAndEval('(7,24,25)[2]'), 24)
+ assert.deepStrictEqual(parseAndEval('size((8, 15, 17))'), [3])
+ assert.deepStrictEqual(
+ parseAndEval('((),(0,),(0,1))'), [[], [0], [0, 1]])
+ })
})
describe('objects', function () {
@@ -2213,17 +2228,17 @@ describe('parse', function () {
it('should parse : (range)', function () {
assert.ok(parseAndEval('2:5') instanceof Matrix)
- assert.deepStrictEqual(parseAndEval('2:5'), math.matrix([2, 3, 4, 5]))
- assert.deepStrictEqual(parseAndEval('10:-2:0'), math.matrix([10, 8, 6, 4, 2, 0]))
- assert.deepStrictEqual(parseAndEval('2:4.0'), math.matrix([2, 3, 4]))
- assert.deepStrictEqual(parseAndEval('2:4.5'), math.matrix([2, 3, 4]))
- assert.deepStrictEqual(parseAndEval('2:4.1'), math.matrix([2, 3, 4]))
- assert.deepStrictEqual(parseAndEval('2:3.9'), math.matrix([2, 3]))
- assert.deepStrictEqual(parseAndEval('2:3.5'), math.matrix([2, 3]))
- assert.deepStrictEqual(parseAndEval('3:-1:0.5'), math.matrix([3, 2, 1]))
- assert.deepStrictEqual(parseAndEval('3:-1:0.5'), math.matrix([3, 2, 1]))
- assert.deepStrictEqual(parseAndEval('3:-1:0.1'), math.matrix([3, 2, 1]))
- assert.deepStrictEqual(parseAndEval('3:-1:-0.1'), math.matrix([3, 2, 1, 0]))
+ assert.deepStrictEqual(parseAndEval('2:5'), math.range(2, 6))
+ assert.deepStrictEqual(
+ parseAndEval('10:-2:0'), math.range(10, 0, -2, true))
+ assert.deepStrictEqual(parseAndEval('2:4.0'), math.range(2, 5))
+ assert.deepStrictEqual(parseAndEval('2:4.5'), math.range(2, 5))
+ assert.deepStrictEqual(parseAndEval('2:4.1'), math.range(2, 5))
+ assert.deepStrictEqual(parseAndEval('2:3.9'), math.range(2, 4))
+ assert.deepStrictEqual(parseAndEval('2:3.5'), math.range(2, 4))
+ assert.deepStrictEqual(parseAndEval('3:-1:0.5'), math.range(3, 0, -1))
+ assert.deepStrictEqual(parseAndEval('3:-1:0.1'), math.range(3, 0, -1))
+ assert.deepStrictEqual(parseAndEval('3:-1:-0.1'), math.range(3, -1, -1))
})
it('should parse to', function () {
@@ -2358,7 +2373,7 @@ describe('parse', function () {
assert.deepStrictEqual(parseAndEval('3 ? true : false; 22'), new ResultSet([22]))
assert.deepStrictEqual(parseAndEval('3 ? 5cm to m : 5cm in mm'), new Unit(5, 'cm').to('m'))
assert.deepStrictEqual(parseAndEval('2 == 4-2 ? [1,2] : false'), math.matrix([1, 2]))
- assert.deepStrictEqual(parseAndEval('false ? 1:2:6'), math.matrix([2, 3, 4, 5, 6]))
+ assert.deepStrictEqual(parseAndEval('false ? 1:2:6'), math.range(2, 7))
})
it('should respect precedence between left/right shift and relational operators', function () {
@@ -2518,10 +2533,13 @@ describe('parse', function () {
})
it('should create a range from bignumbers', function () {
- assert.deepStrictEqual(bigmath.evaluate('4:6'),
- bigmath.matrix([new BigNumber(4), new BigNumber(5), new BigNumber(6)]))
- assert.deepStrictEqual(bigmath.evaluate('0:2:4'),
- bigmath.matrix([new BigNumber(0), new BigNumber(2), new BigNumber(4)]))
+ const four = new BigNumber(4)
+ assert.deepStrictEqual(
+ bigmath.evaluate('4:6'),
+ bigmath.range(four, new BigNumber(6), true))
+ assert.deepStrictEqual(
+ bigmath.evaluate('0:2:4'),
+ bigmath.range(new BigNumber(0), four, new BigNumber(2), true))
})
it('should create a matrix with bignumbers', function () {
diff --git a/test/unit-tests/expression/security.test.js b/test/unit-tests/expression/security.test.js
index 6ea71ffce4..7b1f8e3886 100644
--- a/test/unit-tests/expression/security.test.js
+++ b/test/unit-tests/expression/security.test.js
@@ -214,7 +214,7 @@ describe('security', function () {
'i={isIndex:true,isScalar:f,size:g,min:h,max:h,dimension:k};' +
'f=subset(subset([[[0]]],i),index(1,1,1))("console.log(\'hacked...\')");' +
'f()')
- }, /TypeError: Unexpected type of argument in function subset \(expected: Index, actual: Object, index: 1\)/)
+ }, /TypeError: Unexpected type of argument in function subset/)
})
it('should not allow accessing proto via dimension', function () {
@@ -248,9 +248,17 @@ describe('security', function () {
it('should not allow replacing validateSafeMethod with a local variant', function () {
assert.throws(function () {
math.evaluate("evaluate(\"f(validateSafeMethod)=cos.constructor(\\\"return evaluate\\\")()\")(evaluate(\"f(x,y)=0\"))(\"console.log('hacked...')\")")
- }, /SyntaxError: Value expected/)
+ }, /TypeError: Unexpected type of argument/)
})
+ it(
+ 'should not allow replacing validateSafeMethod with a local variant (2)',
+ function () {
+ assert.throws(function () {
+ math.evaluate("evaluate(\"f(validateSafeMethod)=cos.constructor(\\\"return evaluate\\\")()\")?.(evaluate(\"f(x,y)=0\"))(\"console.log('hacked...')\")")
+ }, /No access to method .constructor/)
+ })
+
it('should not allow abusing toString', function () {
assert.throws(function () {
math.evaluate("badToString = evaluate(\"f() = 1\"); badReplace = evaluate(\"f(a, b) = \\\"evaluate\\\"\"); badNumber = {toString:badToString, replace:badReplace}; badNode = {\"isNode\": true, \"type\": \"ConstantNode\", \"valueType\": \"number\", \"value\": badNumber}; x = evaluate(\"f(child, path, parent) = badNode\", {badNode:badNode}); parse(\"(1)\").map(x).compile().evaluate()(\"console.log('hacked...')\")")
@@ -260,13 +268,13 @@ describe('security', function () {
it('should not allow creating a bad FunctionAssignmentNode', function () {
assert.throws(function () {
math.evaluate("badNode={isNode:true,type:\"FunctionAssignmentNode\",expr:parse(\"1\"),types:{join:evaluate(\"f(a)=\\\"\\\"\")},params:{\"forEach\":evaluate(\"f(x)=1\"),\"join\":evaluate(\"f(x)=\\\"){return evaluate;}});return fn;})())}});return fn;})());}};//\\\"\")}};parse(\"f()=x\").map(evaluate(\"f(a,b,c)=badNode\",{\"badNode\":badNode})).compile().evaluate()()()(\"console.log('hacked...')\")")
- }, /SyntaxError: Value expected/)
+ }, /TypeError: Callback function must return a Node/)
})
it('should not allow creating a bad OperatorNode (1)', function () {
assert.throws(function () {
math.evaluate("badNode={isNode:true,type:\"FunctionAssignmentNode\",expr:parse(\"1\"),types:{join:evaluate(\"f(a)=\\\"\\\"\")},params:{\"forEach\":evaluate(\"f(x)=1\"),\"join\":evaluate(\"f(x)=\\\"){return evaluate;}});return fn;})())}});return fn;})());}};//\\\"\")}};parse(\"f()=x\").map(evaluate(\"f(a,b,c)=badNode\",{\"badNode\":badNode})).compile().evaluate()()()(\"console.log('hacked...')\")")
- }, /SyntaxError: Value expected/)
+ }, /TypeError: Callback function must return a Node/)
})
it('should not allow creating a bad OperatorNode (2)', function () {
@@ -316,7 +324,7 @@ describe('security', function () {
'evilMath=x.create().done();' +
'evilMath.import({"_compile":f(a,b,c)="evaluate","isNode":f()=true}); ' +
"parse(\"(1)\").map(g(a,b,c)=evilMath.chain()).compile().evaluate()(\"console.log('hacked...')\")")
- }, /SyntaxError: Value expected/)
+ }, /Error: Undefined symbol Chain/)
})
it('should not allow passing a function name containg bad contents', function () {
diff --git a/test/unit-tests/expression/transform/subset.transform.test.js b/test/unit-tests/expression/transform/subset.transform.test.js
new file mode 100644
index 0000000000..6c2c800b0f
--- /dev/null
+++ b/test/unit-tests/expression/transform/subset.transform.test.js
@@ -0,0 +1,16 @@
+import assert from 'assert'
+import math from '../../../../src/defaultInstance.js'
+
+const ev = math.evaluate
+const matrix = math.matrix
+const nummat = x => matrix(x, 'dense', 'number')
+
+describe('subset.transform', function () {
+ it('should obey indexing conventions of expressions', function () {
+ assert.strictEqual(ev('subset([1,2;3,4], (2, 1))'), 3)
+ assert.deepStrictEqual(
+ ev('subset(range(2,10), ([9,7,5],))'), nummat([10, 8, 6]))
+ assert.deepStrictEqual(
+ ev("subset([1,2,3;4,5,6], ('1:2', '2:3'))"), matrix([[2, 3], [5, 6]]))
+ })
+})
diff --git a/test/unit-tests/function/arithmetic/add.test.js b/test/unit-tests/function/arithmetic/add.test.js
index aa5c385bf3..fdd6b39244 100644
--- a/test/unit-tests/function/arithmetic/add.test.js
+++ b/test/unit-tests/function/arithmetic/add.test.js
@@ -248,6 +248,19 @@ describe('add', function () {
})
})
+ describe('Range', function () {
+ it('should keep the result a Range when possible', function () {
+ assert.deepStrictEqual(add(math.range(2, 5), 7), new math.Range(9, 12))
+ assert.deepStrictEqual(
+ add(-3, math.range(6, 11, 2)), new math.Range(3, 8, 2))
+ assert.deepStrictEqual(
+ add(math.range(0.5, 4.5), math.range(0, 2.1, 0.5)),
+ new math.Range({ start: 0.5, step: 1.5, length: 4 }))
+ assert.deepStrictEqual(
+ add(math.range(2, 5), [1, 3, 6]), math.matrix([3, 6, 10]))
+ })
+ })
+
describe('multiple arguments', function () {
it('should add more than two arguments', function () {
assert.deepStrictEqual(add(2, 3, 4), 9)
diff --git a/test/unit-tests/function/arithmetic/divide.test.js b/test/unit-tests/function/arithmetic/divide.test.js
index d14353d03e..370bc95683 100644
--- a/test/unit-tests/function/arithmetic/divide.test.js
+++ b/test/unit-tests/function/arithmetic/divide.test.js
@@ -210,6 +210,14 @@ describe('divide', function () {
assert.throws(function () { divide(a, [[1]]) })
})
+ describe('Range', function () {
+ it('should keep the result a Range when possible', function () {
+ assert.deepStrictEqual(
+ divide(math.range(2, 5), 7),
+ new math.Range({ start: 2 / 7, step: 1 / 7, length: 3 }))
+ })
+ })
+
/*
// These are supported now --ericman314
it('should throw an error if dividing a number by a unit', function() {
diff --git a/test/unit-tests/function/arithmetic/dotDivide.test.js b/test/unit-tests/function/arithmetic/dotDivide.test.js
index e99fa1cde9..aa22fcea86 100644
--- a/test/unit-tests/function/arithmetic/dotDivide.test.js
+++ b/test/unit-tests/function/arithmetic/dotDivide.test.js
@@ -24,7 +24,7 @@ describe('dotDivide', function () {
assert.ok(isNaN(dotDivide(false, false)))
})
- it('should add mixed numbers and booleans', function () {
+ it('should divide mixed numbers and booleans', function () {
assert.strictEqual(dotDivide(2, true), 2)
assert.strictEqual(dotDivide(2, false), Infinity)
approxEqual(dotDivide(true, 2), 0.5)
diff --git a/test/unit-tests/function/arithmetic/log.test.js b/test/unit-tests/function/arithmetic/log.test.js
index 3ad86017b2..5d7f26e58a 100644
--- a/test/unit-tests/function/arithmetic/log.test.js
+++ b/test/unit-tests/function/arithmetic/log.test.js
@@ -1,7 +1,7 @@
// test log
import assert from 'assert'
-import { approxDeepEqual } from '../../../../tools/approx.js'
+import { approxEqual, approxDeepEqual } from '../../../../tools/approx.js'
import math from '../../../../src/defaultInstance.js'
const mathPredictable = math.create({ predictable: true })
const complex = math.complex
@@ -96,9 +96,7 @@ describe('log', function () {
it('should return the log of a Fraction', function () {
approxDeepEqual(log(fraction(27, 8), fraction(9, 4)), fraction(3, 2))
- assert.throws(() => log(fraction(27, 8), fraction(-2, 5)),
- /Cannot implicitly convert a Fraction to BigNumber or vice versa/
- )
+ approxEqual(log(fraction(27, 8), fraction(2, 5)), -1.32752114804928)
})
it('should handle complex number with large imaginary part', function () {
diff --git a/test/unit-tests/function/arithmetic/multiply.test.js b/test/unit-tests/function/arithmetic/multiply.test.js
index d25420481e..e21e068e16 100644
--- a/test/unit-tests/function/arithmetic/multiply.test.js
+++ b/test/unit-tests/function/arithmetic/multiply.test.js
@@ -899,6 +899,18 @@ describe('multiply', function () {
})
})
+ describe('Range', function () {
+ it('should keep the result a Range when possible', function () {
+ assert.deepStrictEqual(
+ multiply(math.range(2, 5), 7),
+ new math.Range({ start: 14, step: 7, length: 3 }))
+ const frac = math.fraction
+ assert.deepStrictEqual(
+ multiply(frac(1, 3), math.range(2n, 5n)),
+ new math.Range(frac(2, 3), frac(5, 3), frac(1, 3)))
+ })
+ })
+
describe('multiple arguments', function () {
it('should multiply more than two arguments', function () {
assert.deepStrictEqual(multiply(2, 3, 4), 24)
diff --git a/test/unit-tests/function/arithmetic/one.test.js b/test/unit-tests/function/arithmetic/one.test.js
new file mode 100644
index 0000000000..a546c92fcf
--- /dev/null
+++ b/test/unit-tests/function/arithmetic/one.test.js
@@ -0,0 +1,18 @@
+// test one
+import assert from 'assert'
+
+import math from '../../../../src/defaultInstance.js'
+const one = math.one
+
+describe('one', function () {
+ it('should return multiplicative identity', function () {
+ assert.strictEqual(one(Infinity), 1)
+ assert.deepStrictEqual(one(math.bignumber(7)), math.bignumber(1))
+ assert.strictEqual(one(-17n), 1n)
+ assert.deepStrictEqual(one(math.complex(0, 1)), math.complex(1))
+ assert.strictEqual(one(false), true)
+ assert.deepStrictEqual(one(math.fraction(3, 10)), math.fraction(1))
+ assert.deepStrictEqual(one(math.zeros(3, 3)), math.identity(3))
+ assert.throws(() => one(math.zeros(5)))
+ })
+})
diff --git a/test/unit-tests/function/arithmetic/round.test.js b/test/unit-tests/function/arithmetic/round.test.js
index 30b03ee21a..deeeb51bf0 100644
--- a/test/unit-tests/function/arithmetic/round.test.js
+++ b/test/unit-tests/function/arithmetic/round.test.js
@@ -173,7 +173,7 @@ describe('round', function () {
})
it('should round each element in a matrix, array, range', function () {
- assert.deepStrictEqual(round(math.range(0, 2.1, 1 / 3), 2), math.matrix([0, 0.33, 0.67, 1, 1.33, 1.67, 2]))
+ assert.deepStrictEqual(round(math.range(0, 2.1, 1 / 3), 2), math.matrix([0, 0.33, 0.67, 1, 1.33, 1.67, 2], 'dense', 'number'))
assert.deepStrictEqual(round(math.range(0, 2.1, 1 / 3)), math.matrix([0, 0, 1, 1, 1, 2, 2]))
assert.deepStrictEqual(round([1.7, 2.3]), [2, 2])
assert.deepStrictEqual(round(math.matrix([1.7, 2.3])).valueOf(), [2, 2])
diff --git a/test/unit-tests/function/arithmetic/scalarDivide.test.js b/test/unit-tests/function/arithmetic/scalarDivide.test.js
new file mode 100644
index 0000000000..d26fcc5e5f
--- /dev/null
+++ b/test/unit-tests/function/arithmetic/scalarDivide.test.js
@@ -0,0 +1,92 @@
+// test scalarDivide (check if an element is a scalar multiple of another)
+import assert from 'assert'
+
+import math from '../../../../src/defaultInstance.js'
+const sD = math.scalarDivide
+const C = math.complex
+const frac = math.fraction
+const Big = math.bignumber
+const M = math.matrix
+
+describe('scalarDivide', function () {
+ it('should determine when one scalar is a multiple of another', function () {
+ assert.strictEqual(sD(4, 2), 2)
+ assert.strictEqual(sD(-4, 2), -2)
+ assert.strictEqual(sD(4, -2), -2)
+ assert.strictEqual(sD(-4, -2), 2)
+ assert.strictEqual(sD(4, 0), undefined)
+ assert.strictEqual(sD(0, -5), 0)
+ assert.strictEqual(sD(0, 0), 0)
+ assert.strictEqual(sD(true, true), 1)
+ assert.strictEqual(sD(true, false), undefined)
+ assert.strictEqual(sD(false, true), 0)
+ assert.strictEqual(sD(false, false), false)
+ assert.strictEqual(sD(2, true), 2)
+ assert.strictEqual(sD(2, false), undefined)
+ assert.strictEqual(sD(null, 1), undefined)
+ assert.deepStrictEqual(sD(C(2, 3), 2), C(1, 1.5))
+ assert.deepStrictEqual(sD(C(2, 3), C(0, 4)), C(0.75, -0.5))
+ assert.strictEqual(sD(C(0, 2), C(0, 4)), 0.5)
+ assert.deepStrictEqual(sD(5, C(1, 2)), C(1, -2))
+ assert.deepStrictEqual(sD(math.unit('5m'), 10), math.unit('0.5m'))
+ const result = math.unit('2m^-1')
+ result.skipAutomaticSimplification = false
+ assert.deepStrictEqual(sD(10, math.unit('5m')), result)
+ assert.strictEqual(sD(math.unit('10m'), math.unit('5m')), 2)
+ assert.strictEqual(sD(math.unit(10), 5), 2)
+ assert.strictEqual(sD(4n, 2n), 2n)
+ assert.deepStrictEqual(sD(5n, 3n), frac(5, 3))
+ assert.deepStrictEqual(sD(frac(4), frac(2)), frac(2))
+ assert.deepStrictEqual(sD(frac(5, 4), frac(3, 4)), frac(5, 3))
+ assert.deepStrictEqual(sD(Big(4), 2), Big(2))
+ assert.deepStrictEqual(sD(Big(1.44), Big(1.2)), Big(1.2))
+ assert.strictEqual(sD(Big(0.3), 0), undefined)
+ })
+
+ it('should error with incorrect arguments', function () {
+ assert.throws(() => sD(2, 3, 4))
+ assert.throws(() => sD(2))
+ })
+
+ it('should determine when arrays are scalar multiples', function () {
+ assert.strictEqual(sD([2, 4, 6], [1, 2, 3]), 2)
+ assert.strictEqual(sD([[0.5, 1], [1.5, 2]], [[1, 2], [3, 4]]), 0.5)
+ assert.strictEqual(sD([], []), 0)
+ assert.strictEqual(sD([2, 4, 7], [1, 2, 3]), undefined)
+ assert.strictEqual(sD([[0.5, 1], [1.5, 2]], [[1, 2], [3, 3]]), undefined)
+ assert.strictEqual(sD([], [0]), undefined)
+ assert.strictEqual(sD([0], [0]), 0)
+ assert.strictEqual(sD([0, 5], [0, 2]), 2.5)
+ assert.strictEqual(sD([0, 0, 5], [0, 2, 1]), undefined)
+ assert.strictEqual(sD([0, 5, 1], [0, 0, 1]), undefined)
+ })
+
+ it('should never relate scalars and arrays', function () {
+ assert.strictEqual(sD(4, [1, 2, 3]), undefined)
+ assert.strictEqual(sD([1, 2, 3], 4), undefined)
+ })
+
+ it('should determine when matrices are scalar multiples', function () {
+ assert.strictEqual(sD(M([2, 4, 6]), M([1, 2, 3])), 2)
+ assert.strictEqual(sD(M([[0.5, 1], [1.5, 2]]), M([[1, 2], [3, 4]])), 0.5)
+ assert.strictEqual(sD(M([]), M([])), 0)
+ assert.strictEqual(sD(M([2, 4, 7]), M([1, 2, 3])), undefined)
+ assert.strictEqual(
+ sD(M([[0.5, 1], [1.5, 2]]), M([[1, 2], [3, 3]])), undefined)
+ assert.strictEqual(sD(M([]), M([0])), undefined)
+ assert.strictEqual(sD(M([0]), M([0])), 0)
+ assert.strictEqual(sD(M([0, 5]), M([0, 2])), 2.5)
+ assert.strictEqual(sD(M([0, 0, 5]), M([0, 2, 1])), undefined)
+ assert.strictEqual(sD(M([0, 5, 1]), M([0, 0, 1])), undefined)
+ })
+
+ it('should never relate scalars and arrays', function () {
+ assert.strictEqual(sD(4, [1, 2, 3]), undefined)
+ assert.strictEqual(sD([1, 2, 3], 4), undefined)
+ })
+
+ it('should never relate scalars and matrices', function () {
+ assert.strictEqual(sD(4, M([1, 2, 3])), undefined)
+ assert.strictEqual(sD(M([1, 2, 3]), 4), undefined)
+ })
+})
diff --git a/test/unit-tests/function/arithmetic/subtract.test.js b/test/unit-tests/function/arithmetic/subtract.test.js
index 1302032171..dd10d6b791 100644
--- a/test/unit-tests/function/arithmetic/subtract.test.js
+++ b/test/unit-tests/function/arithmetic/subtract.test.js
@@ -245,6 +245,20 @@ describe('subtract', function () {
})
})
+ describe('Range', function () {
+ it('should keep the result a Range when possible', function () {
+ assert.deepStrictEqual(
+ subtract(math.range(2, 5), 7), new math.Range(-5, -2))
+ assert.deepStrictEqual(
+ subtract(-3, math.range(6, 11, 2)), new math.Range(-9, -14, -2))
+ assert.deepStrictEqual(
+ subtract(math.range(0.5, 4.5), math.range(0, 2.1, 0.5)),
+ new math.Range({ start: 0.5, step: 0.5, length: 4 }))
+ assert.deepStrictEqual(
+ subtract(math.range(2, 5), [1, 3, 6]), math.matrix([1, 0, -2]))
+ })
+ })
+
it('should throw an error in case of invalid number of arguments', function () {
assert.throws(function () { subtract(1) }, /TypeError: Too few arguments/)
assert.throws(function () { subtract(1, 2, 3) }, /TypeError: Too many arguments/)
diff --git a/test/unit-tests/function/arithmetic/zero.test.js b/test/unit-tests/function/arithmetic/zero.test.js
new file mode 100644
index 0000000000..d098026888
--- /dev/null
+++ b/test/unit-tests/function/arithmetic/zero.test.js
@@ -0,0 +1,20 @@
+// test zero
+import assert from 'assert'
+
+import math from '../../../../src/defaultInstance.js'
+const zero = math.zero
+
+describe('zero', function () {
+ it('should return additive identity', function () {
+ assert.strictEqual(zero(-Infinity), 0)
+ assert.deepStrictEqual(zero(math.bignumber(7)), math.bignumber(0))
+ assert.strictEqual(zero(117n), 0n)
+ assert.deepStrictEqual(zero(math.complex(0, 1)), math.complex(0))
+ assert.strictEqual(zero(false), false)
+ assert.deepStrictEqual(zero(math.fraction(3, 10)), math.fraction(0))
+ assert.deepStrictEqual(zero(math.unit(3, 'm')), math.unit(0, 'm'))
+ assert.deepStrictEqual(
+ zero(math.identity(3, 3)), math.zeros(math.matrix([3, 3])))
+ assert.deepStrictEqual(zero([1, 2, 3]), math.zeros([3]))
+ })
+})
diff --git a/test/unit-tests/function/matrix/concat.test.js b/test/unit-tests/function/matrix/concat.test.js
index d27aa2e4af..c3a7d57ce3 100644
--- a/test/unit-tests/function/matrix/concat.test.js
+++ b/test/unit-tests/function/matrix/concat.test.js
@@ -79,6 +79,11 @@ describe('concat', function () {
])
})
+ it('should keep Ranges if possible', function () {
+ assert.deepStrictEqual(
+ math.concat(math.range(2, 10), math.range(10, 20)), new math.Range(2, 20))
+ })
+
it('should concatenate strings', function () {
assert.strictEqual(math.concat('a', 'b'), 'ab')
assert.strictEqual(math.concat('a', 'b', 'c'), 'abc')
diff --git a/test/unit-tests/function/matrix/matrixFrom.test.js b/test/unit-tests/function/matrix/matrixFrom.test.js
index dd4fa080ca..da07bb5800 100644
--- a/test/unit-tests/function/matrix/matrixFrom.test.js
+++ b/test/unit-tests/function/matrix/matrixFrom.test.js
@@ -62,9 +62,9 @@ describe('matrixFrom...', function () {
actual = math.matrixFromRows(matrix([[1, 2, 3]], 'sparse'), matrix([[4], [5], [6]], 'sparse'), matrix([[7, 8, 9]], 'sparse'))
assert.deepStrictEqual(actual, matrix(expected))
- // for a mixed type, returns an array
+ // for a mixed type, returns an Matrix
actual = math.matrixFromRows([1, 2, 3], [4, 5, 6], matrix([7, 8, 9]))
- assert.deepStrictEqual(actual, expected)
+ assert.deepStrictEqual(actual, matrix(expected))
})
it('...Columns', function () {
@@ -91,8 +91,8 @@ describe('matrixFrom...', function () {
actual = math.matrixFromColumns(matrix([[1, 2, 3]], 'sparse'), matrix([[4], [5], [6]], 'sparse'), matrix([[7, 8, 9]], 'sparse'))
assert.deepStrictEqual(actual, matrix(expected))
- // for a mixed type, returns an array
+ // for a mixed type, returns a Matrix
actual = math.matrixFromColumns([1, 2, 3], [4, 5, 6], matrix([7, 8, 9]))
- assert.deepStrictEqual(actual, expected)
+ assert.deepStrictEqual(actual, matrix(expected))
})
})
diff --git a/test/unit-tests/function/matrix/ones.test.js b/test/unit-tests/function/matrix/ones.test.js
index 3485a109c4..f8e34a43af 100644
--- a/test/unit-tests/function/matrix/ones.test.js
+++ b/test/unit-tests/function/matrix/ones.test.js
@@ -44,6 +44,19 @@ describe('ones', function () {
assert.deepStrictEqual(ones([three]), [one, one, one])
})
+ it('should create a Range of all-one vectors', function () {
+ const oneRange = ones(2, 3, 'range')
+ assert.strictEqual(oneRange.length, 2)
+ assert.strictEqual(oneRange.step, 0)
+ assert.deepStrictEqual(
+ oneRange.start, new math.Range({ start: 1, step: 0, length: 3 }))
+ assert.strictEqual(oneRange.get([1, 1]), 1)
+ assert.deepStrictEqual(oneRange.toArray(), [[1, 1, 1], [1, 1, 1]])
+ assert.strictEqual(
+ oneRange.toString(),
+ 'Range{start: Range{start: 1, step: 0, length: 3}, step: 0, length: 2}')
+ })
+
it('should create a 3D matrix with ones', function () {
const res = [
[
diff --git a/test/unit-tests/function/matrix/range.test.js b/test/unit-tests/function/matrix/range.test.js
index ac9eff92e5..eb01fb89a9 100644
--- a/test/unit-tests/function/matrix/range.test.js
+++ b/test/unit-tests/function/matrix/range.test.js
@@ -1,61 +1,73 @@
import assert from 'assert'
import math from '../../../../src/defaultInstance.js'
const range = math.range
-const matrix = math.matrix
+const Range = math.Range
const bignumber = math.bignumber
const unit = math.unit
const evaluate = math.evaluate
describe('range', function () {
it('should parse a valid string correctly', function () {
- assert.deepStrictEqual(range('1:6'), matrix([1, 2, 3, 4, 5]))
- assert.deepStrictEqual(range('0:2:10'), matrix([0, 2, 4, 6, 8]))
- assert.deepStrictEqual(range('5:-1:0'), matrix([5, 4, 3, 2, 1]))
- assert.deepStrictEqual(range('2:-2:-3'), matrix([2, 0, -2]))
+ assert.deepStrictEqual(range('1:6'), new Range(1, 6))
+ assert.deepStrictEqual(range('0:2:10'), new Range(0, 10, 2))
+ assert.deepStrictEqual(range('5:-1:0'), new Range(5, 0, -1))
+ assert.deepStrictEqual(range('2:-2:-3'), new Range(2, -4, -2))
})
it('should throw an error in case of invalid string', function () {
- assert.throws(function () { range('1:2:6:4') }, /is no valid range/)
- assert.throws(function () { range('1') }, /is no valid range/)
- assert.throws(function () { range('1,3:4') }, /is no valid range/)
- assert.throws(function () { range('1:2,4') }, /is no valid range/)
- assert.throws(function () { range('1:a') }, /is no valid range/)
+ assert.throws(function () { range('1:2:6:4') }, SyntaxError)
+ assert.throws(function () { range('1') }, SyntaxError)
+ assert.throws(function () { range('1,3:4') }, /Error: Cannot convert/)
+ assert.throws(function () { range('1:2,4') }, /Error: Cannot convert/)
+ assert.throws(function () { range('1:a') }, /Error: Cannot convert/)
})
it('should create a range start:1:end if called with 2 numbers', function () {
- assert.deepStrictEqual(range(3, 6), matrix([3, 4, 5]))
- assert.deepStrictEqual(range(1, 6), matrix([1, 2, 3, 4, 5]))
- assert.deepStrictEqual(range(1, 6.1), matrix([1, 2, 3, 4, 5, 6]))
- assert.deepStrictEqual(range(1, 5.9), matrix([1, 2, 3, 4, 5]))
- assert.deepStrictEqual(range(6, 1), matrix([]))
+ assert.deepStrictEqual(range(3, 6), new Range(3, 6))
+ assert.deepStrictEqual(range(3, 6).valueOf(), [3, 4, 5])
+ assert.deepStrictEqual(range(1, 6).valueOf(), [1, 2, 3, 4, 5])
+ assert.deepStrictEqual(range(1, 6.1).valueOf(), [1, 2, 3, 4, 5, 6])
+ assert.deepStrictEqual(range(1, 5.9).valueOf(), [1, 2, 3, 4, 5])
+ assert.deepStrictEqual(range(6, 1).valueOf(), [])
})
it('should create a range start:step:end if called with 3 numbers', function () {
- assert.deepStrictEqual(range(0, 10, 2), matrix([0, 2, 4, 6, 8]))
- assert.deepStrictEqual(range(5, 0, -1), matrix([5, 4, 3, 2, 1]))
- assert.deepStrictEqual(range(2, -4, -2), matrix([2, 0, -2]))
+ assert.deepStrictEqual(range(0, 10, 2), new Range(0, 10, 2))
+ assert.deepStrictEqual(range(0, 10, 2).valueOf(), [0, 2, 4, 6, 8])
+ assert.deepStrictEqual(range(5, 0, -1).valueOf(), [5, 4, 3, 2, 1])
+ assert.deepStrictEqual(range(2, -4, -2).valueOf(), [2, 0, -2])
})
- it('should throw an error when step==0', function () {
- assert.throws(function () { range(0, 0, 0) }, /Step must be non-zero/)
- assert.throws(function () { range(0, 10, 0) }, /Step must be non-zero/)
- assert.throws(function () { range(0, 10, 0, true) }, /Step must be non-zero/)
+ it('should take care handling step==0', function () {
+ assert.deepStrictEqual(range(0, 0, 0).valueOf(), [])
+ assert.throws(function () { range(0, 10, 0) }, /No scalar/)
+ assert.throws(function () { range(0, 10, 0, true) }, /No scalar/)
})
it('should create an empty range when start and stop are equal', function () {
- assert.deepStrictEqual(range(0, 0), matrix([]))
- assert.deepStrictEqual(range(1, 1, 2), matrix([]))
- assert.deepStrictEqual(range('0:0'), matrix([]))
- assert.deepStrictEqual(range('0:1:0'), matrix([]))
- assert.deepStrictEqual(range('1:2:1'), matrix([]))
- assert.deepStrictEqual(range('1:1:1'), matrix([]))
+ assert.deepStrictEqual(range(0, 0).valueOf(), [])
+ assert.deepStrictEqual(range(1, 1, 2).valueOf(), [])
+ assert.deepStrictEqual(range('0:0').valueOf(), [])
+ assert.deepStrictEqual(range('0:1:0').valueOf(), [])
+ assert.deepStrictEqual(range('1:2:1').valueOf(), [])
+ assert.deepStrictEqual(range('1:1:1').valueOf(), [])
})
it('should create an array with the end value when start and stop are equal and includeEnd=true', function () {
- assert.deepStrictEqual(range(0, 0, true), matrix([0]))
- assert.deepStrictEqual(range(1, 1, 2, true), matrix([1]))
- assert.deepStrictEqual(range('0:0', true), matrix([0]))
- assert.deepStrictEqual(range('1:1:1', true), matrix([1]))
+ assert.deepStrictEqual(range(0, 0, true).valueOf(), [0])
+ assert.deepStrictEqual(range(1, 1, 2, true).valueOf(), [1])
+ assert.deepStrictEqual(range('0:0', true).valueOf(), [0])
+ assert.deepStrictEqual(range('1:1:1', true).valueOf(), [1])
+ })
+
+ it('should accept an object of attributes', function () {
+ const frac = math.fraction
+ assert.deepStrictEqual(range({ start: 0, last: 3 }).valueOf(), [0, 1, 2, 3])
+ assert.deepStrictEqual(
+ range({ start: 0n, end: 8n, step: 2n }).valueOf(), [0n, 2n, 4n, 6n])
+ assert.deepStrictEqual(
+ range({ start: frac(1, 2), length: 3 }).valueOf(),
+ [frac(1, 2), frac(3, 2), frac(5, 2)])
})
it('should output an array when setting matrix==="array"', function () {
@@ -68,135 +80,178 @@ describe('range', function () {
})
it('should create a range with bigints', function () {
- assert.deepStrictEqual(range(1n, 3n), matrix([1n, 2n]))
- assert.deepStrictEqual(range(3n, 1n, -1n), matrix([3n, 2n]))
- assert.deepStrictEqual(range(1n, 3n, true), matrix([1n, 2n, 3n]))
- assert.deepStrictEqual(range(3n, 1n, -1n, true), matrix([3n, 2n, 1n]))
+ assert.deepStrictEqual(range(1n, 3n), new Range(1n, 3n))
+ assert.deepStrictEqual(range(1n, 3n).valueOf(), [1n, 2n])
+ assert.deepStrictEqual(range(3n, 1n, -1n).valueOf(), [3n, 2n])
+ assert.deepStrictEqual(range(1n, 3n, true).valueOf(), [1n, 2n, 3n])
+ assert.deepStrictEqual(range(3n, 1n, -1n, true).valueOf(), [3n, 2n, 1n])
})
it('should handle mixed numbers and bigints appropriately', function () {
- assert.deepStrictEqual(range(1n, 3), matrix([1n, 2n]))
- assert.deepStrictEqual(range(3, 1n, -1n), matrix([3n, 2n]))
- assert.deepStrictEqual(range(3n, 1, -1), matrix([3n, 2n]))
- assert.deepStrictEqual(range(1, 3n, true), matrix([1n, 2n, 3n]))
- assert.deepStrictEqual(range(3n, 1, -1n, true), matrix([3n, 2n, 1n]))
- assert.deepStrictEqual(range(3, 1n, -1, true), matrix([3n, 2n, 1n]))
- assert.deepStrictEqual(range(1, 5, 2n), matrix([1, 3]))
- assert.deepStrictEqual(range(5, 1, -2n, true), matrix([5, 3, 1]))
+ assert.deepStrictEqual(range(1n, 3), new Range(1n, 3n))
+ assert.deepStrictEqual(range(1n, 3).valueOf(), [1n, 2n])
+ assert.deepStrictEqual(range(3, 1n, -1n).valueOf(), [3, 2])
+ assert.deepStrictEqual(range(3n, 1, -1).valueOf(), [3, 2])
+ assert.deepStrictEqual(range(1, 3n, true).valueOf(), [1, 2, 3])
+ assert.deepStrictEqual(range(3n, 1, -1n, true).valueOf(), [3n, 2n, 1n])
+ assert.deepStrictEqual(range(3, 1n, -1, true).valueOf(), [3, 2, 1])
+ assert.deepStrictEqual(range(1, 5, 2n).valueOf(), [1, 3])
+ assert.deepStrictEqual(range(5, 1, -2n, true).valueOf(), [5, 3, 1])
})
it('should create a range with bignumbers', function () {
- assert.deepStrictEqual(range(bignumber(1), bignumber(3)), matrix([bignumber(1), bignumber(2)]))
- assert.deepStrictEqual(range(bignumber(3), bignumber(1), bignumber(-1)), matrix([bignumber(3), bignumber(2)]))
+ const bigRange = range(bignumber(1), bignumber(3))
+ assert.deepStrictEqual(bigRange, new Range(bignumber(1), bignumber(3)))
+ assert.deepStrictEqual(bigRange.valueOf(), [bignumber(1), bignumber(2)])
+ assert.deepStrictEqual(
+ range(bignumber(3), bignumber(1), bignumber(-1)).valueOf(),
+ [bignumber(3), bignumber(2)])
})
it('should throw an error from bignumbers when step==0', function () {
- assert.throws(function () { range(bignumber(0), bignumber(10), bignumber(0)) }, /Step must be non-zero/)
- assert.throws(function () { range(bignumber(0), bignumber(10), bignumber(0), true) }, /Step must be non-zero/)
+ assert.throws(function () { range(bignumber(0), bignumber(10), bignumber(0)) }, /No scalar/)
+ assert.throws(function () { range(bignumber(0), bignumber(10), bignumber(0), true) }, /No scalar/)
})
it('should create a range with mixed numbers and bignumbers', function () {
- assert.deepStrictEqual(range(bignumber(1), 3), matrix([bignumber(1), bignumber(2)]))
- assert.deepStrictEqual(range(1, bignumber(3)), matrix([bignumber(1), bignumber(2)]))
-
- assert.deepStrictEqual(range(1, bignumber(3), bignumber(1)), matrix([bignumber(1), bignumber(2)]))
- assert.deepStrictEqual(range(bignumber(1), 3, bignumber(1)), matrix([bignumber(1), bignumber(2)]))
- assert.deepStrictEqual(range(bignumber(1), bignumber(3), 1), matrix([bignumber(1), bignumber(2)]))
-
- assert.deepStrictEqual(range(bignumber(1), 3, 1), matrix([bignumber(1), bignumber(2)]))
- assert.deepStrictEqual(range(1, bignumber(3), 1), matrix([bignumber(1), bignumber(2)]))
- assert.deepStrictEqual(range(1, 3, bignumber(1)), matrix([bignumber(1), bignumber(2)]))
+ assert.deepStrictEqual(
+ range(bignumber(1), 3).valueOf(), [bignumber(1), bignumber(2)])
+ assert.deepStrictEqual(range(1, bignumber(3)).valueOf(), [1, 2])
+
+ assert.deepStrictEqual(
+ range(1, bignumber(3), bignumber(1)).valueOf(),
+ [bignumber(1), bignumber(2)])
+ assert.deepStrictEqual(
+ range(bignumber(1), 3, bignumber(1)).valueOf(),
+ [bignumber(1), bignumber(2)])
+ assert.deepStrictEqual(
+ range(bignumber(1), bignumber(3), 1).valueOf(),
+ [bignumber(1), bignumber(2)])
+
+ assert.deepStrictEqual(
+ range(bignumber(1), 3, 1).valueOf(), [bignumber(1), bignumber(2)])
+ assert.deepStrictEqual(range(1, bignumber(3), 1).valueOf(), [1, 2])
+ assert.deepStrictEqual(
+ range(1, 3, bignumber(1)).valueOf(), [bignumber(1), bignumber(2)])
})
- it('should parse a range with bignumbers', function () {
+ it('should interpret strings as numbers regardless', function () {
const bigmath = math.create({ number: 'BigNumber' })
- const bignumber = bigmath.bignumber
- const matrix = bigmath.matrix
- assert.deepStrictEqual(bigmath.range('1:3'), matrix([bignumber(1), bignumber(2)]))
- assert.deepStrictEqual(bigmath.range('3:-1:0'), matrix([bignumber(3), bignumber(2), bignumber(1)]))
+ assert.deepStrictEqual(bigmath.range('1:3').valueOf(), [1, 2])
+ assert.deepStrictEqual(bigmath.range('3:-1:0').valueOf(), [3, 2, 1])
})
it('should throw an error when parsing a an invalid string to a bignumber range', function () {
const bigmath = math.create({ number: 'BigNumber' })
- assert.throws(function () { bigmath.range('1:a') }, /is no valid range/)
+ assert.throws(function () { bigmath.range('1:a') }, /Error: Cannot convert/)
})
it('should create a range with units', function () {
- assert.deepStrictEqual(range(unit(1, 'm'), unit(3, 'm'), unit(1, 'm')), matrix([unit(1, 'm'), unit(2, 'm')]))
- assert.deepStrictEqual(range(unit(3, 'm'), unit(1, 'm'), unit(-1, 'm')), matrix([unit(3, 'm'), unit(2, 'm')]))
+ assert.deepStrictEqual(
+ range(unit(1, 'm'), unit(3, 'm'), unit(1, 'm')).valueOf(),
+ [unit(1, 'm'), unit(2, 'm')])
+ assert.deepStrictEqual(
+ range(unit(3, 'm'), unit(1, 'm'), unit(-1, 'm')).valueOf(),
+ [unit(3, 'm'), unit(2, 'm')])
})
it('should parse a range with units', function () {
- assert.deepStrictEqual(evaluate('1m:1m:3m'), matrix([unit(1, 'm'), unit(2, 'm'), unit(3, 'm')]))
- assert.deepStrictEqual(evaluate('3m:-1m:0m'), matrix([unit(3, 'm'), unit(2, 'm'), unit(1, 'm'), unit(0, 'm')]))
- assert.deepStrictEqual(evaluate('range(1m,3m,1m)'), matrix([unit(1, 'm'), unit(2, 'm'), unit(3, 'm')]))
- assert.deepStrictEqual(evaluate('range(3m,0m,-1m)'), matrix([unit(3, 'm'), unit(2, 'm'), unit(1, 'm'), unit(0, 'm')]))
+ assert.deepStrictEqual(
+ evaluate('1m:1m:3m').valueOf(),
+ [unit(1, 'm'), unit(2, 'm'), unit(3, 'm')])
+ assert.deepStrictEqual(
+ evaluate('3m:-1m:0m').valueOf(),
+ [unit(3, 'm'), unit(2, 'm'), unit(1, 'm'), unit(0, 'm')])
+ assert.deepStrictEqual(
+ evaluate('range(1m,3m,1m)').valueOf(),
+ [unit(1, 'm'), unit(2, 'm'), unit(3, 'm')])
+ assert.deepStrictEqual(
+ evaluate('range(3m,0m,-1m)').valueOf(),
+ [unit(3, 'm'), unit(2, 'm'), unit(1, 'm'), unit(0, 'm')])
})
it('should gracefully handle round-off errors', function () {
- assert.deepStrictEqual(range(1, 2, 0.1, true)._size, [11])
- assert.deepStrictEqual(range(0.1, 0.2, 0.01, true)._size, [11])
- assert.deepStrictEqual(range(1, 5, 0.1)._size, [40])
- assert.deepStrictEqual(range(2, 1, -0.1, true)._size, [11])
- assert.deepStrictEqual(range(5, 1, -0.1)._size, [40])
- assert.deepStrictEqual(range(-3.2909135802469143, 3.2909135802469143, (3.2909135802469143 + 3.2909135802469143) / 10, true)._size, [11])
- assert.deepStrictEqual(range(-3.2909135802469143, 3.2909135802469143, (3.2909135802469143 + 3.2909135802469143) / 9, true)._size, [10])
- assert.deepStrictEqual(range(-3.2909135802469143, 3.2909135802469143, (3.2909135802469143 + 3.2909135802469143) / 10)._size, [10])
- assert.deepStrictEqual(range(-3.2909135802469143, 3.2909135802469143, (3.2909135802469143 + 3.2909135802469143) / 9)._size, [9])
+ assert.deepStrictEqual(range(1, 2, 0.1, true).size(), [11])
+ assert.deepStrictEqual(range(0.1, 0.2, 0.01, true).size(), [11])
+ assert.deepStrictEqual(range(1, 5, 0.1).size(), [40])
+ assert.deepStrictEqual(range(2, 1, -0.1, true).size(), [11])
+ assert.deepStrictEqual(range(5, 1, -0.1).size(), [40])
+ assert.deepStrictEqual(range(
+ -3.2909135802469143,
+ 3.2909135802469143,
+ (3.2909135802469143 + 3.2909135802469143) / 10,
+ true).size(), [11])
+ assert.deepStrictEqual(range(
+ -3.2909135802469143,
+ 3.2909135802469143,
+ (3.2909135802469143 + 3.2909135802469143) / 9,
+ true).size(), [10])
+ assert.deepStrictEqual(range(
+ -3.2909135802469143,
+ 3.2909135802469143,
+ (3.2909135802469143 + 3.2909135802469143) / 10).size(), [10])
+ assert.deepStrictEqual(range(
+ -3.2909135802469143,
+ 3.2909135802469143,
+ (3.2909135802469143 + 3.2909135802469143) / 9).size(), [9])
})
describe('option includeEnd', function () {
it('should parse a string and include end', function () {
- assert.deepStrictEqual(range('1:6', false), matrix([1, 2, 3, 4, 5]))
- assert.deepStrictEqual(range('1:2:6', false), matrix([1, 3, 5]))
- assert.deepStrictEqual(range('1:6', true), matrix([1, 2, 3, 4, 5, 6]))
+ assert.deepStrictEqual(range('1:6', false).valueOf(), [1, 2, 3, 4, 5])
+ assert.deepStrictEqual(range('1:2:6', false).valueOf(), [1, 3, 5])
+ assert.deepStrictEqual(range('1:6', true).valueOf(), [1, 2, 3, 4, 5, 6])
})
it('should create a range start:1:end and include end', function () {
- assert.deepStrictEqual(range(3, 6, false), matrix([3, 4, 5]))
- assert.deepStrictEqual(range(3, 6, true), matrix([3, 4, 5, 6]))
+ assert.deepStrictEqual(range(3, 6, false).valueOf(), [3, 4, 5])
+ assert.deepStrictEqual(range(3, 6, true).valueOf(), [3, 4, 5, 6])
})
it('should create a range start:step:end and include end', function () {
- assert.deepStrictEqual(range(0, 10, 2, false), matrix([0, 2, 4, 6, 8]))
- assert.deepStrictEqual(range(0, 10, 2, true), matrix([0, 2, 4, 6, 8, 10]))
+ assert.deepStrictEqual(range(0, 10, 2, false).valueOf(), [0, 2, 4, 6, 8])
+ assert.deepStrictEqual(
+ range(0, 10, 2, true).valueOf(), [0, 2, 4, 6, 8, 10])
})
it('should create a range with bignumbers and include end', function () {
- assert.deepStrictEqual(range(bignumber(1), bignumber(3), true), matrix([bignumber(1), bignumber(2), bignumber(3)]))
- assert.deepStrictEqual(range(bignumber(3), bignumber(1), bignumber(-1), true), matrix([bignumber(3), bignumber(2), bignumber(1)]))
+ assert.deepStrictEqual(
+ range(bignumber(1), bignumber(3), true).valueOf(),
+ [bignumber(1), bignumber(2), bignumber(3)])
+ assert.deepStrictEqual(
+ range(bignumber(3), bignumber(1), bignumber(-1), true).valueOf(),
+ [bignumber(3), bignumber(2), bignumber(1)])
})
it('should handle Fractions', function () {
const frac = math.fraction
+ const fRange = range(frac(1, 3), frac(10, 3))
+ assert.deepStrictEqual(fRange, new Range(frac(1, 3), frac(10, 3)))
assert.deepStrictEqual(
- range(frac(1, 3), frac(10, 3)),
- matrix([frac(1, 3), frac(4, 3), frac(7, 3)]))
+ fRange.valueOf(), [frac(1, 3), frac(4, 3), frac(7, 3)])
assert.deepStrictEqual(
- range(frac(1, 3), frac(7, 3), true),
- matrix([frac(1, 3), frac(4, 3), frac(7, 3)]))
+ range(frac(1, 3), frac(7, 3), true).valueOf(),
+ [frac(1, 3), frac(4, 3), frac(7, 3)])
assert.deepStrictEqual(
- range(frac(1, 3), frac(4, 3), frac(1, 3)),
- matrix([frac(1, 3), frac(2, 3), frac(1)]))
+ range(frac(1, 3), frac(4, 3), frac(1, 3)).valueOf(),
+ [frac(1, 3), frac(2, 3), frac(1)])
assert.deepStrictEqual(
- range(frac(1, 3), frac(4, 3), frac(1, 3), true),
- matrix([frac(1, 3), frac(2, 3), frac(1), frac(4, 3)]))
+ range(frac(1, 3), frac(4, 3), frac(1, 3), true).valueOf(),
+ [frac(1, 3), frac(2, 3), frac(1), frac(4, 3)])
})
it('should allow mixed number and Fraction', function () {
const frac = math.fraction
+ assert.deepStrictEqual(range(1, frac(10, 3)).valueOf(), [1, 2, 3])
assert.deepStrictEqual(
- range(1, frac(10, 3)),
- matrix([frac(1), frac(2), frac(3)]))
- assert.deepStrictEqual(
- range(frac(1, 3), 3, true),
- matrix([frac(1, 3), frac(4, 3), frac(7, 3)]))
+ range(frac(1, 3), 3, true).valueOf(),
+ [frac(1, 3), frac(4, 3), frac(7, 3)])
assert.deepStrictEqual(
- range(frac(1, 3), 2, frac(1, 3)),
- matrix([frac(1, 3), frac(2, 3), frac(1), frac(4, 3), frac(5, 3)]))
+ range(frac(1, 3), 2, frac(1, 3)).valueOf(),
+ [frac(1, 3), frac(2, 3), frac(1), frac(4, 3), frac(5, 3)])
assert.deepStrictEqual(
- range(0, frac(4, 3), frac(1, 3), true),
- matrix([frac(0), frac(1, 3), frac(2, 3), frac(1), frac(4, 3)]))
+ range(0, frac(4, 3), frac(1, 3), true).valueOf(),
+ [frac(0), frac(1, 3), frac(2, 3), frac(1), frac(4, 3)])
})
it('should throw an error in case of invalid type of include end', function () {
@@ -214,8 +269,11 @@ describe('range', function () {
assert.throws(function () { range(math.unit('5cm')) }, TypeError)
})
- it('should throw an error if called with only two units value', function () {
- assert.throws(function () { range(math.unit('0cm'), math.unit('5cm')) }, TypeError)
+ it('should use a step of 1 unit when called with two units', function () {
+ const unitRange = range(math.unit(0, 'cm'), math.unit(5, 'cm'))
+ assert.deepStrictEqual(unitRange.step, math.unit(1, 'cm'))
+ assert.strictEqual(unitRange.length, 5)
+ assert.strictEqual(unitRange.toString(), '0 cm:5 cm')
})
it('should throw an error when called with mismatching units', function () {
@@ -242,7 +300,7 @@ describe('range', function () {
assert.throws(function () { range(1, 2, 3, true, 5) }, /TypeError: Too many arguments/)
})
- it('should not cast a single number or boolean to string', function () {
+ it('should not cast a single number or boolean to range', function () {
assert.throws(function () { range(2) }, /TypeError: Too few arguments/)
assert.throws(function () { range(true) }, /TypeError: Unexpected type of argument/)
})
diff --git a/test/unit-tests/function/matrix/subset.test.js b/test/unit-tests/function/matrix/subset.test.js
index a99fcfcac4..aab10dc5b8 100644
--- a/test/unit-tests/function/matrix/subset.test.js
+++ b/test/unit-tests/function/matrix/subset.test.js
@@ -6,6 +6,7 @@ import sinon from 'sinon'
const subset = math.subset
const matrix = math.matrix
const Range = math.Range
+const range = math.range
const index = math.index
describe('subset', function () {
@@ -16,10 +17,13 @@ describe('subset', function () {
assert.deepStrictEqual(subset(a, index(new Range(0, 2), 1)), [2, 4])
assert.deepStrictEqual(subset(a, index(1, 0)), 3)
assert.deepStrictEqual(subset([math.bignumber(2)], index(0)), math.bignumber(2))
+ assert.deepStrictEqual(subset(a, 1), [3, 4])
+ assert.deepStrictEqual(subset(a, [range(0, 2), 1]), [2, 4])
})
it('should get the right subset of an array of booleans', function () {
assert.deepStrictEqual(subset(a, index([true, true], [1])), [[2], [4]])
+ assert.deepStrictEqual(subset(a, [[true, true], [1]]), [[2], [4]])
assert.deepStrictEqual(subset(a, index([false, true], [true, false])), [[3]])
assert.deepStrictEqual(subset([math.bignumber(2)], index([true])), [math.bignumber(2)])
})
@@ -29,6 +33,7 @@ describe('subset', function () {
assert.deepStrictEqual(subset(a, index(new math.Range(0, 0), 1)), [])
assert.deepStrictEqual(subset(b, index([], 1)), math.matrix())
assert.deepStrictEqual(subset(b, index(new math.Range(0, 0), 1)), math.matrix())
+ assert.deepStrictEqual(subset(b, [range(0, 0), 1]), math.matrix())
assert.deepStrictEqual(subset({ a: 1 }, index('')), undefined)
assert.deepStrictEqual(subset('hello', index('')), '')
})
@@ -66,6 +71,7 @@ describe('subset', function () {
it('should get the right subset of an object', function () {
const obj = { foo: 'bar' }
assert.deepStrictEqual(subset(obj, index('foo')), 'bar')
+ assert.deepStrictEqual(subset(obj, ['foo']), 'bar')
assert.deepStrictEqual(subset(obj, index('bla')), undefined)
})
@@ -78,6 +84,8 @@ describe('subset', function () {
it('should get the right subset of a matrix', function () {
assert.deepStrictEqual(subset(b, index(new Range(0, 2), 1)), matrix([2, 4]))
+ assert.deepStrictEqual(subset(b, [range(0, 2), 1]), matrix([2, 4]))
+ assert.deepStrictEqual(subset(b, 0), matrix([1, 2]))
assert.deepStrictEqual(subset(b, index(1, 0)), 3)
})
@@ -244,10 +252,11 @@ describe('subset', function () {
assert.throws(function () { subset('hello', index(10), '!', 'foo') }, /Single character expected as defaultValue/)
})
- it('should throw an error if in case of an invalid index type', function () {
- assert.throws(function () { subset('hello', 2) }, /TypeError: Unexpected type of argument/)
- assert.throws(function () { subset('hello', 2, 'A') }, /TypeError: Unexpected type of argument/)
- assert.throws(function () { subset('hello', 2, 'A', 'B') }, /TypeError: Unexpected type of argument/)
+ it('should handle integer indices', function () {
+ // These now all work:
+ assert.strictEqual(subset('hello', 2), 'l')
+ assert.strictEqual(subset('hello', 2, 'A'), 'heAlo')
+ assert.strictEqual(subset('hello', 2, 'A', 'B'), 'heAlo')
})
})
@@ -258,9 +267,10 @@ describe('subset', function () {
})
it('should throw an error in case of invalid type of arguments', function () {
- assert.throws(function () { subset([1, 2], [0]) }, /TypeError: Unexpected type of argument/)
- // assert.throws(function () {subset(new Date(), index(0))}, /TypeError: Unexpected type of argument/) // FIXME: should fail too. Problem is, Date is also an Object
- // assert.throws(function () {subset(/foo/, index(0))}, /TypeError: Unexpected type of argument/) // FIXME: should fail too. Problem is, Date is also an Object
+ // This first one now works:
+ assert.strictEqual(subset([1, 2], [0]), 1)
+ // assert.throws(function () {subset(new Date(), index(0))}, /TypeError: Unexpected type of argument/) // FIXME: should fail. Problem is, Date is also an Object
+ // assert.throws(function () {subset(/foo/, index(0))}, /TypeError: Unexpected type of argument/) // FIXME: should fail too. Problem is, RegExp is also an Object
})
it('should LaTeX subset', function () {
diff --git a/test/unit-tests/function/matrix/zeros.test.js b/test/unit-tests/function/matrix/zeros.test.js
index d386c012b0..46b1950890 100644
--- a/test/unit-tests/function/matrix/zeros.test.js
+++ b/test/unit-tests/function/matrix/zeros.test.js
@@ -33,6 +33,20 @@ describe('zeros', function () {
assert.deepStrictEqual(zeros([three]), [zero, zero, zero])
})
+ it('should create a sparse matrix with zeros', function () {
+ assert.deepStrictEqual(
+ zeros(2, 3, 'sparse'), matrix([[0, 0, 0], [0, 0, 0]], 'sparse'))
+ })
+
+ it('should create a Range of zero vectors', function () {
+ const zeroRange = zeros(2, 3, 'range')
+ assert.strictEqual(zeroRange.length, 2)
+ assert.strictEqual(zeroRange.step, 0)
+ assert.deepStrictEqual(
+ zeroRange.start, new math.Range({ start: 0, step: 0, length: 3 }))
+ assert.strictEqual(zeroRange.get([1, 1]), 0)
+ })
+
it('should create a 2D matrix with zeros from an array', function () {
assert.deepStrictEqual(zeros(2, 3), matrix([[0, 0, 0], [0, 0, 0]]))
assert.deepStrictEqual(zeros(3, 2), matrix([[0, 0], [0, 0], [0, 0]]))
diff --git a/test/unit-tests/function/probability/factorial.test.js b/test/unit-tests/function/probability/factorial.test.js
index dde6bf970c..fb1e3c94ed 100644
--- a/test/unit-tests/function/probability/factorial.test.js
+++ b/test/unit-tests/function/probability/factorial.test.js
@@ -1,5 +1,4 @@
import assert from 'assert'
-import { approxEqual } from '../../../../tools/approx.js'
import math from '../../../../src/defaultInstance.js'
const factorial = math.factorial
@@ -34,6 +33,19 @@ describe('factorial', function () {
assert.deepStrictEqual(bigmath20.factorial(bigmath20.bignumber(22)), bigmath20.bignumber('1124000727777607680000'))
})
+ it('should calculate the factorial of a bigint', function () {
+ assert.strictEqual(factorial(23n), 25852016738884976640000n)
+ })
+
+ it('should return factorial of integer Fraction as bigint', function () {
+ assert.strictEqual(factorial(math.fraction(24)), 620448401733239439360000n)
+ })
+
+ it('should handle complex numbers (trivially)', function () {
+ assert.throws(() => factorial(math.complex(1, 1)), RangeError)
+ assert.strictEqual(factorial(math.complex(6, 0)), 720)
+ })
+
it('should calculate the factorial of a boolean', function () {
assert.strictEqual(factorial(true), 1)
assert.strictEqual(factorial(false), 1)
@@ -47,18 +59,18 @@ describe('factorial', function () {
assert.deepStrictEqual(factorial([0, 1, 2, 3, 4, 5]), [1, 1, 2, 6, 24, 120])
})
- it('should calculate the factorial of a non-integer', function () {
- approxEqual(factorial(1.5), 1.32934038817914)
- approxEqual(factorial(7.5), 14034.40729348)
+ it('should throw on non-integers (use gamma)', function () {
+ assert.throws(() => factorial(1.5), RangeError)
})
it('should throw error if called with negative number', function () {
- assert.throws(function () { factorial(-1) }, /Value must be non-negative/)
- assert.throws(function () { factorial(-1.5) }, /Value must be non-negative/)
+ assert.throws(function () { factorial(-1) }, RangeError)
+ assert.throws(function () { factorial(-1.5) }, RangeError)
- assert.throws(function () { factorial(math.bignumber(-1)) }, /Value must be non-negative/)
- assert.throws(function () { factorial(math.bignumber(-1.5)) }, /Value must be non-negative/)
- assert.throws(function () { factorial(math.bignumber(-Infinity)) }, /Value must be non-negative/)
+ assert.throws(function () { factorial(math.bignumber(-1)) }, RangeError)
+ assert.throws(function () { factorial(math.bignumber(-1.5)) }, RangeError)
+ assert.throws(
+ function () { factorial(math.bignumber(-Infinity)) }, RangeError)
})
it('should throw an error if called with non-integer bignumber', function () {
diff --git a/test/unit-tests/function/relational/deepEqual.test.js b/test/unit-tests/function/relational/deepEqual.test.js
index 98d3c20ce0..7fc7ad375f 100644
--- a/test/unit-tests/function/relational/deepEqual.test.js
+++ b/test/unit-tests/function/relational/deepEqual.test.js
@@ -49,6 +49,11 @@ describe('deepEqual', function () {
assert.deepStrictEqual(deepEqual([complex(2, 3), 3], [complex(2, 4), 3]), false)
})
+ it('should compare two units', function () {
+ assert.strictEqual(deepEqual(math.unit(1, 'm'), math.unit(100, 'cm')), true)
+ assert.strictEqual(deepEqual(math.unit(1, 'm'), math.unit(99, 'cm')), false)
+ })
+
it('should throw an error in case of invalid number of arguments', function () {
assert.throws(function () { deepEqual(1) }, /TypeError: Too few arguments/)
assert.throws(function () { deepEqual(1, 2, 3) }, /TypeError: Too many arguments/)
diff --git a/test/unit-tests/function/statistics/prod.test.js b/test/unit-tests/function/statistics/prod.test.js
index 87d43d5aa7..f1cbbd491f 100644
--- a/test/unit-tests/function/statistics/prod.test.js
+++ b/test/unit-tests/function/statistics/prod.test.js
@@ -59,6 +59,13 @@ describe('prod', function () {
assert.strictEqual(prod(new DenseMatrix([1, 3, 5, 2])), 30)
})
+ it('should return the prod of a Range', function () {
+ assert.strictEqual(prod(math.range(8, 1, -2)), 8 * 6 * 4 * 2) // 8!!
+ // and 2.5 rising 4:
+ assert.strictEqual(
+ prod(math.range({ start: 2.5, length: 4 })), 2.5 * 3.5 * 4.5 * 5.5)
+ })
+
it('should return the prod element from a 2d array', function () {
assert.deepStrictEqual(prod([
[1, 7, 2],
@@ -86,12 +93,18 @@ describe('prod', function () {
assert.throws(function () { prod() })
})
- it('should throw an error if called with not yet supported argument dim', function () {
- assert.throws(function () { prod([], 2) }, /not yet supported/)
+ it('should throw an error if argument dim is too large', function () {
+ assert.throws(function () { prod([], 2) }, /no dimension 2/)
+ })
+
+ it('should multiply along either dimension of a 2D array', function () {
+ const nums = [[1, 2], [3, 4], [5, 6]]
+ assert.deepStrictEqual(prod(nums, 0), [15, 48])
+ assert.deepStrictEqual(prod(nums, 1), [2, 12, 30])
})
- it('should throw an error if called with an empty array', function () {
- assert.throws(function () { prod([]) })
+ it('should return 1 on an empty array', function () {
+ assert.strictEqual(prod([]), 1)
})
it('should throw an error if called with invalid type of arguments', function () {
diff --git a/test/unit-tests/function/string/format.test.js b/test/unit-tests/function/string/format.test.js
index 8469a571d6..7fc943be6c 100644
--- a/test/unit-tests/function/string/format.test.js
+++ b/test/unit-tests/function/string/format.test.js
@@ -65,7 +65,8 @@ describe('format', function () {
})
it('should format ranges with given precision', function () {
- assert.strictEqual(math.format(new math.Range(1 / 3, 4 / 3, 2 / 3), 3), '0.333:0.667:1.33')
+ assert.strictEqual(
+ math.format(new math.Range(1 / 3, 4 / 3, 2 / 3), 3), '0.333:0.667:1.67')
})
})
diff --git a/test/unit-tests/json/replacer.test.js b/test/unit-tests/json/replacer.test.js
index e1695eeca3..202dcac74e 100644
--- a/test/unit-tests/json/replacer.test.js
+++ b/test/unit-tests/json/replacer.test.js
@@ -50,7 +50,7 @@ describe('replacer', function () {
it('should stringify a Range', function () {
const r = new math.Range(2, 10)
- const json = '{"mathjs":"Range","start":2,"end":10,"step":1}'
+ const json = '{"mathjs":"Range","start":2,"step":1,"length":8}'
assert.deepStrictEqual(JSON.stringify(r), json)
assert.deepStrictEqual(JSON.stringify(r, replacer), json)
})
@@ -58,16 +58,14 @@ describe('replacer', function () {
it('should stringify an Index', function () {
const i = new math.Index(new math.Range(0, 10), 2)
const json = '{"mathjs":"Index","dimensions":[' +
- '{"mathjs":"Range","start":0,"end":10,"step":1},' +
- '2' +
- ']}'
+ '{"mathjs":"Range","start":0,"step":1,"length":10},2]}'
assert.deepStrictEqual(JSON.stringify(i), json)
assert.deepStrictEqual(JSON.stringify(i, replacer), json)
})
it('should stringify a Range (2)', function () {
const r = new math.Range(2, 10, 2)
- const json = '{"mathjs":"Range","start":2,"end":10,"step":2}'
+ const json = '{"mathjs":"Range","start":2,"step":2,"length":4}'
assert.deepStrictEqual(JSON.stringify(r), json)
assert.deepStrictEqual(JSON.stringify(r, replacer), json)
})
diff --git a/test/unit-tests/type/matrix/DenseMatrix.test.js b/test/unit-tests/type/matrix/DenseMatrix.test.js
index 1540eb3146..4d9d61242b 100644
--- a/test/unit-tests/type/matrix/DenseMatrix.test.js
+++ b/test/unit-tests/type/matrix/DenseMatrix.test.js
@@ -488,6 +488,8 @@ describe('DenseMatrix', function () {
m = new DenseMatrix(math.range(0, 10))
assert.deepStrictEqual(m.size(), [10])
assert.deepStrictEqual(m.subset(index(new Range(2, 5))).valueOf(), [2, 3, 4])
+ assert.deepStrictEqual(
+ m.subset(index('6:')), new DenseMatrix([6, 7, 8, 9], 'number'))
// get 2-dimensional
m = new DenseMatrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
@@ -499,6 +501,7 @@ describe('DenseMatrix', function () {
assert.deepStrictEqual(m.subset(index(new Range(1, 3), 1)).valueOf(), [5, 8])
assert.deepStrictEqual(m.subset(index(new Range(1, 3), 2)).valueOf(), [6, 9])
assert.deepStrictEqual(m.subset(index([0, 1, 2], [1])).valueOf(), [[2], [5], [8]])
+ assert.deepStrictEqual(m.layer(1), new DenseMatrix([4, 5, 6]))
// get n-dimensional
m = new DenseMatrix([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
@@ -514,7 +517,8 @@ describe('DenseMatrix', function () {
it('should squeeze the output when index contains a scalar', function () {
let m = new DenseMatrix(math.range(0, 10))
assert.deepStrictEqual(m.subset(index(1)), 1)
- assert.deepStrictEqual(m.subset(index(new Range(1, 2))), new DenseMatrix([1]))
+ assert.deepStrictEqual(
+ m.subset(index(new Range(1, 2))), new DenseMatrix([1], 'number'))
m = new DenseMatrix([[1, 2], [3, 4]])
assert.deepStrictEqual(m.subset(index(1, 1)), 4)
@@ -550,9 +554,11 @@ describe('DenseMatrix', function () {
// set 1-dimensional
let m = new DenseMatrix(math.range(0, 7))
m.subset(index(new Range(2, 4)), [20, 30])
- assert.deepStrictEqual(m, new DenseMatrix([0, 1, 20, 30, 4, 5, 6]))
+ assert.deepStrictEqual(
+ m, new DenseMatrix([0, 1, 20, 30, 4, 5, 6], 'number'))
m.subset(index(4), 40)
- assert.deepStrictEqual(m, new DenseMatrix([0, 1, 20, 30, 40, 5, 6]))
+ assert.deepStrictEqual(
+ m, new DenseMatrix([0, 1, 20, 30, 40, 5, 6], 'number'))
// set 2-dimensional
m = new DenseMatrix()
diff --git a/test/unit-tests/type/matrix/ImmutableDenseMatrix.test.js b/test/unit-tests/type/matrix/ImmutableDenseMatrix.test.js
index 57d8d33436..e82d1f2be6 100644
--- a/test/unit-tests/type/matrix/ImmutableDenseMatrix.test.js
+++ b/test/unit-tests/type/matrix/ImmutableDenseMatrix.test.js
@@ -274,7 +274,9 @@ describe('ImmutableDenseMatrix', function () {
it('should squeeze the output when index contains a scalar', function () {
let m = new ImmutableDenseMatrix(math.range(0, 10))
assert.deepStrictEqual(m.subset(index(1)), 1)
- assert.deepStrictEqual(m.subset(index(new Range(1, 2))), new ImmutableDenseMatrix([1]))
+ assert.deepStrictEqual(
+ m.subset(index(new Range(1, 2))),
+ new ImmutableDenseMatrix([1], 'number'))
m = new ImmutableDenseMatrix([[1, 2], [3, 4]])
assert.deepStrictEqual(m.subset(index(1, 1)), 4)
diff --git a/test/unit-tests/type/matrix/Index.test.js b/test/unit-tests/type/matrix/Index.test.js
index 1e5abccd47..f7269f915e 100644
--- a/test/unit-tests/type/matrix/Index.test.js
+++ b/test/unit-tests/type/matrix/Index.test.js
@@ -52,18 +52,6 @@ describe('Index', function () {
assert.deepStrictEqual(new Index(new ImmutableDenseMatrix([0, 10]))._dimensions, [new ImmutableDenseMatrix([0, 10])])
})
- it('should create an Index from an array with ranges', function () {
- const index = Index.create([new Range(0, 10), new Range(4)])
- assert(index instanceof Index)
- assert.deepStrictEqual(index._dimensions, [new Range(0, 10), new Range(4)])
- })
-
- it('should create an Index from an array with sets', function () {
- const index = Index.create([new ImmutableDenseMatrix([0, 10]), new ImmutableDenseMatrix([4])])
- assert(index instanceof Index)
- assert.deepStrictEqual(index._dimensions, [new ImmutableDenseMatrix([0, 10]), new ImmutableDenseMatrix([4])])
- })
-
it('should calculate the size of an Index', function () {
assert.deepStrictEqual(new Index(new Range(0, 10)).size(), [10])
assert.deepStrictEqual(new Index(new Range(0, 10, 2)).size(), [5])
diff --git a/test/unit-tests/type/matrix/Range.test.js b/test/unit-tests/type/matrix/Range.test.js
index 69a54f992f..3b22f90d72 100644
--- a/test/unit-tests/type/matrix/Range.test.js
+++ b/test/unit-tests/type/matrix/Range.test.js
@@ -42,26 +42,124 @@ describe('range', function () {
assert.deepStrictEqual(r.size(), [0])
})
+ it('should create ranges from attributes', function () {
+ const frac = math.fraction
+ assert.deepStrictEqual(new Range({}).toArray(), [])
+
+ assert.deepStrictEqual(new Range({ start: 3 }).toArray(), [])
+ assert.deepStrictEqual(new Range({ end: 3 }).toArray(), [0, 1, 2])
+ assert.deepStrictEqual(new Range({ step: 3 }).toArray(), [])
+ assert.deepStrictEqual(new Range({ last: 3 }).toArray(), [0, 1, 2, 3])
+ assert.deepStrictEqual(new Range({ length: 3 }).toArray(), [0, 1, 2])
+
+ assert.deepStrictEqual(
+ new Range({ start: 3, end: 7 }).toArray(), [3, 4, 5, 6])
+ assert.deepStrictEqual(
+ new Range({ start: 3, step: 3 }).toArray(), [])
+ assert.deepStrictEqual(
+ new Range({ start: 3, last: 7 }).toArray(), [3, 4, 5, 6, 7])
+ assert.deepStrictEqual(
+ new Range({ start: 3, length: 3 }).toArray(), [3, 4, 5])
+ assert.deepStrictEqual(
+ new Range({ end: 7, step: 3 }).toArray(), [0, 3, 6])
+ assert.deepStrictEqual(
+ new Range({ end: 7, last: 6 }).toArray(), [0, 1, 2, 3, 4, 5, 6])
+ assert.deepStrictEqual( // last takes precedence:
+ new Range({ end: 7, last: 3 }).toArray(), [0, 1, 2, 3])
+ assert.deepStrictEqual(
+ new Range({ end: 7, length: 3 }).toArray(), [4, 5, 6])
+ assert.deepStrictEqual(
+ new Range({ step: 3, last: 7 }).toArray(), [0, 3, 6])
+ assert.deepStrictEqual(
+ new Range({ step: 3, length: 3 }).toArray(), [0, 3, 6])
+ assert.deepStrictEqual(
+ new Range({ length: 3, last: 7 }).toArray(), [5, 6, 7])
+
+ assert.deepStrictEqual(
+ new Range({ step: 3, last: 7, length: 3 }).toArray(), [1, 4, 7])
+ assert.deepStrictEqual(
+ new Range({ end: 3, last: 7, length: 3 }).toArray(), [5, 6, 7])
+ assert.deepStrictEqual(
+ new Range({ step: 3, end: 12, length: 3 }).toArray(), [3, 6, 9])
+ assert.deepStrictEqual(
+ new Range({ step: 3, end: 3, last: 10 }).toArray(), [0, 3, 6, 9])
+ assert.deepStrictEqual(
+ new Range({ start: 7, last: 3, length: 3 }).toArray(), [7, 5, 3])
+ assert.deepStrictEqual(
+ new Range({ step: 7, start: 6, length: 3 }).toArray(), [6, 13, 20])
+ assert.deepStrictEqual(
+ new Range({ step: 7, start: 3, last: 12 }).toArray(), [3, 10])
+ assert.deepStrictEqual(
+ new Range({ start: frac(3), end: frac(7), length: 3 }).toArray(),
+ [frac(3), frac(13, 3), frac(17, 3)])
+ assert.deepStrictEqual(
+ new Range({ start: 3, end: 3, last: 4 }).toArray(), [3, 4])
+ assert.deepStrictEqual(
+ new Range({ start: 3, step: 2, end: 7 }).toArray(), [3, 5])
+
+ assert.deepStrictEqual(
+ new Range({ end: -3, step: -1, last: 3, length: 4 }).toArray(),
+ [6, 5, 4, 3])
+ assert.deepStrictEqual( // last overridden when start, step, length given
+ new Range({ start: 5, step: -1, last: 3, length: 4 }).toArray(),
+ [5, 4, 3, 2])
+ assert.deepStrictEqual(
+ new Range({ start: 5, end: 3, last: 3.5, length: 4 }).toArray(),
+ [5, 4.5, 4, 3.5])
+ assert.deepStrictEqual( // end overridden similarly
+ new Range({ start: 5, end: 3, step: 2, length: 3 }).toArray(),
+ [5, 7, 9])
+ assert.deepStrictEqual(
+ new Range({ start: 5, end: 3, step: 3, last: 10 }).toArray(),
+ [5, 8])
+
+ assert.deepStrictEqual(
+ new Range({ start: 2, end: 20, step: -0.25, last: 10, length: 3 })
+ .toArray(),
+ [2, 1.75, 1.5])
+ })
+
+ it('can make 2D ranges', function () {
+ const rng2d = new Range({ start: [1, 3], step: [1, -1], length: 3 })
+ assert.deepStrictEqual(rng2d.toArray(), [[1, 3], [2, 2], [3, 1]])
+ assert.strictEqual(rng2d.toString(), '[1, 3]:[1, -1]:[4, 0]')
+ assert.deepStrictEqual(
+ new Range({ start: [1, 3, 5], last: [0, 0, 0], length: 3 }).toArray(),
+ [[1, 3, 5], [0.5, 1.5, 2.5], [0, 0, 0]])
+ const numberGrid = new Range(new Range(1, 11), new Range(101, 111), 10)
+ assert.strictEqual(numberGrid.get([4, 5]), 46)
+ assert.deepStrictEqual(numberGrid.last, new Range(91, 101))
+ assert.strictEqual(numberGrid.toString(), '(1:11):10:(101:111)')
+ })
+
it('should throw an error when created without new keyword', function () {
assert.throws(function () { Range(0, 10) }, /Constructor must be called with the new operator/)
})
it('should throw an error for wrong type of arguments', function () {
- assert.throws(function () { console.log(new Range('str', 10, 1)) }, /Parameter start must be a number/)
- assert.throws(function () { console.log(new Range(0, 'str', 1)) }, /Parameter end must be a number/)
- assert.throws(function () { console.log(new Range(0, 10, 'str')) }, /Parameter step must be a number/)
+ assert.throws(function () { console.log(new Range('str', 10, 1)) }, /Cannot convert/)
+ assert.throws(function () { console.log(new Range(0, 'str', 1)) }, /Cannot convert/)
+ assert.throws(function () { console.log(new Range(0, 10, 'str')) }, /Cannot convert/)
})
- it('should throw an error for step size zero', function () {
- assert.throws(function () { console.log(new Range(0, 0, 0)) }, /Step must not be zero/)
- assert.throws(function () { console.log(new Range(10, 10, 0)) }, /Step must not be zero/)
- assert.throws(function () { console.log(new Range(0, 10, math.bignumber(0))) }, /Step must not be zero/)
- assert.throws(function () { console.log(new Range(0, 10, math.bigint(0))) }, /Step must not be zero/)
+ it('should deal carefully with step size zero', function () {
+ let empty = new Range(0, 0, 0)
+ assert.strictEqual(empty.length, 0)
+ empty = new Range(10, 10, 0)
+ assert.strictEqual(empty.length, 0)
+ assert.throws(function () {
+ console.log(new Range(0, 10, math.bignumber(0)))
+ }, /No scalar/)
+ assert.throws(function () {
+ console.log(new Range(0, 10, math.bigint(0)))
+ }, /No scalar/)
})
})
describe('parse', function () {
- it('should create a range from a string', function () {
+ it('should create a range from a string [deprecated]', function () {
+ Range.parseMethodMustWarn = false // suppress deprecation warning
+
let r = Range.parse('10:-1:4')
assert.deepStrictEqual(r.toArray(), [10, 9, 8, 7, 6, 5])
assert.deepStrictEqual(r.size(), [6])
@@ -71,7 +169,7 @@ describe('range', function () {
assert.deepStrictEqual(r.size(), [4])
})
- it('should return null when parsing an invalid string', function () {
+ it('should return null when parsing an invalid string [deprecated]', function () {
assert.strictEqual(Range.parse('a:4'), null)
assert.strictEqual(Range.parse('3'), null)
assert.strictEqual(Range.parse(''), null)
@@ -144,15 +242,30 @@ describe('range', function () {
})
describe('toString', function () {
- it('should stringify a range to format start:step:end', function () {
+ it('should stringify a range to format from:by:to', function () {
assert.strictEqual(new math.Range(0, 10).toString(), '0:10')
assert.strictEqual(new math.Range(0, 10, 2).toString(), '0:2:10')
})
- it('should stringify a range to format start:step:end with given precision', function () {
- assert.strictEqual(new math.Range(1 / 3, 4 / 3, 2 / 3).format(3), '0.333:0.667:1.33')
- assert.strictEqual(new math.Range(1 / 3, 4 / 3, 2 / 3).format(4), '0.3333:0.6667:1.333')
- assert.strictEqual(new math.Range(1 / 3, 4 / 3, 2 / 3).format(14), '0.33333333333333:0.66666666666667:1.3333333333333')
+ it(
+ 'should stringify a range to format start:step:end with given precision',
+ function () {
+ assert.strictEqual(
+ new math.Range(1 / 3, 4 / 3, 2 / 3).format(3), '0.333:0.667:1.67')
+ assert.strictEqual(
+ new math.Range(1 / 3, 4 / 3, 2 / 3).format(4), '0.3333:0.6667:1.667')
+ assert.strictEqual(
+ new math.Range(1 / 3, 4 / 3, 2 / 3).format(14),
+ '0.33333333333333:0.66666666666667:1.6666666666667'
+ )
+ })
+ })
+
+ describe('immutable', function () {
+ it('should not allow property changes', function () {
+ const r1 = new Range(0, 10, 2)
+ assert.throws(() => { r1.start = 2 }, TypeError)
+ assert.throws(() => { r1.length = 3 }, TypeError)
})
})
@@ -163,18 +276,6 @@ describe('range', function () {
assert.deepStrictEqual(r1, r2)
assert.notStrictEqual(r1, r2)
-
- // changes in r1 should not affect r2
- r1.start = 2
- r1.end = 8
- r1.step = 1
-
- assert.strictEqual(r1.start, 2)
- assert.strictEqual(r1.end, 8)
- assert.strictEqual(r1.step, 1)
- assert.strictEqual(r2.start, 0)
- assert.strictEqual(r2.end, 10)
- assert.strictEqual(r2.step, 2)
})
})
@@ -196,12 +297,12 @@ describe('range', function () {
assert.deepStrictEqual(r.map(function (value, index, range) {
assert.strictEqual(range, r)
return 'range[' + index[0] + ']=' + value
- }), [
+ }), math.matrix([
'range[0]=2',
'range[1]=3',
'range[2]=4',
'range[3]=5'
- ])
+ ]))
})
})
@@ -228,10 +329,12 @@ describe('range', function () {
assert.strictEqual(new Range(0, 4).format(), '0:4')
assert.strictEqual(new Range(0, 4, 2).format(), '0:2:4')
- assert.strictEqual(new Range(0.01, 0.09, 0.02).format(), '0.01:0.02:0.09')
+ assert.strictEqual(
+ new Range(0.01, 0.09, 0.02).format(2), '0.01:0.02:0.09')
assert.strictEqual(new Range(0.01, 0.09, 0.02).format({
- notation: 'exponential'
+ notation: 'exponential',
+ precision: 1
}), '1e-2:2e-2:9e-2')
})
})
@@ -251,8 +354,12 @@ describe('range', function () {
})
it('toJSON', function () {
- assert.deepStrictEqual(new Range(2, 4).toJSON(), { mathjs: 'Range', start: 2, end: 4, step: 1 })
- assert.deepStrictEqual(new Range(0, 10, 2).toJSON(), { mathjs: 'Range', start: 0, end: 10, step: 2 })
+ assert.deepStrictEqual(
+ new Range(2, 4).toJSON(),
+ { mathjs: 'Range', start: 2, step: 1, length: 2 })
+ assert.deepStrictEqual(
+ new Range(0, 10, 2).toJSON(),
+ { mathjs: 'Range', start: 0, step: 2, length: 5 })
})
it('fromJSON', function () {
@@ -267,5 +374,10 @@ describe('range', function () {
assert.strictEqual(r2.start, 0)
assert.strictEqual(r2.end, 10)
assert.strictEqual(r2.step, 2)
+
+ // Make sure old format works:
+ const oldToJSON = '{"mathjs": "Range", "start": 2, "end": 4, "step": 1}'
+ const revived = JSON.parse(oldToJSON, math.reviver)
+ assert.deepStrictEqual(revived, new Range(2, 4))
})
})
diff --git a/test/unit-tests/type/matrix/function/matrix.test.js b/test/unit-tests/type/matrix/function/matrix.test.js
index 7b335f1f86..93753b3c51 100644
--- a/test/unit-tests/type/matrix/function/matrix.test.js
+++ b/test/unit-tests/type/matrix/function/matrix.test.js
@@ -72,10 +72,18 @@ describe('matrix', function () {
it('should create a matrix from a range correctly', function () {
const d = matrix(math.range(1, 6))
assert.ok(d instanceof math.Matrix)
- assert.deepStrictEqual(d, matrix([1, 2, 3, 4, 5]))
+ assert.deepStrictEqual(d.valueOf(), [1, 2, 3, 4, 5])
assert.deepStrictEqual(math.size(d), [5])
})
+ it('should create Ranges', function () {
+ const r1 = math.range(1, 6)
+ const r2 = matrix(r1, 'range')
+ assert.ok(r1 !== r2)
+ assert.deepStrictEqual(r1, r2)
+ assert.deepStrictEqual(matrix([1, 2, 3, 4, 5], 'range'), r1)
+ })
+
it('should throw an error if called with an invalid argument', function () {
assert.throws(function () { matrix(new Date()) }, TypeError)
})
diff --git a/test/unit-tests/type/unit/Unit.test.js b/test/unit-tests/type/unit/Unit.test.js
index 2803daf984..9254e6a6a8 100644
--- a/test/unit-tests/type/unit/Unit.test.js
+++ b/test/unit-tests/type/unit/Unit.test.js
@@ -80,6 +80,7 @@ describe('Unit', function () {
it('should create a unitless Unit if second parameter is undefined', function () {
const a = new Unit(6)
assert(a.dimensions.every(d => d === 0))
+ assert.strictEqual(a.unitless(), true)
})
it('should ignore properties on Object.prototype', function () {
diff --git a/types/index.d.ts b/types/index.d.ts
index 05b2711423..f88004f0ca 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -14,16 +14,22 @@ export type NoLiteralType = T extends number
: T
export type MathNumericType = number | BigNumber | bigint | Fraction | Complex
-export type MathScalarType = MathNumericType | Unit
-export type MathGeneric = T
-export type MathArray = T[] | Array>
-export type MathCollection = MathArray | Matrix
-export type MathType = MathScalarType | MathCollection
+type MathScalarTypeOut = MathNumericType | Unit
+type MathScalarTypeIn = MathScalarTypeOut | string | boolean
+export type MathScalarType = MathScalarTypeOut
+
+export type MathArray =
+ | T[]
+ | Array>
+export type MathCollection =
+ | MathArray
+ | Matrix
+export type MathType = MathScalarTypeOut | MathCollection
export type MathExpression = string | string[] | MathCollection
// add type for Matrix Callback Function and Matrix Storage Format
-export type MatrixStorageFormat = 'dense' | 'sparse'
-export type MatrixFromFunctionCallback = (
+export type MatrixStorageFormat = 'dense' | 'sparse' | 'range'
+export type MatrixFromFunctionCallback = (
index: number[]
) => T
@@ -677,7 +683,8 @@ export interface MathJsInstance extends MathJsFactory {
bignumber(
x?: number | string | Fraction | BigNumber | bigint | Unit | boolean | null
): BigNumber
- bignumber(x: T): T
+ bignumber(A: MathArray): MathArray
+ bignumber(M: Matrix): Matrix
/**
* Create a bigint, which can store integers with arbitrary precision.
@@ -815,12 +822,19 @@ export interface MathJsInstance extends MathJsFactory {
* @param dataType The Matrix data type
* @returns The created Matrix
*/
+ // We give the two most important types, because the type inference
+ // in the generic seems not to narrow well
matrix(
- data: MathCollection | string[],
+ data: MathCollection,
format?: MatrixStorageFormat,
dataType?: string
- ): Matrix
- matrix(
+ ): Matrix
+ matrix(
+ data: MathCollection,
+ format?: MatrixStorageFormat,
+ dataType?: string
+ ): Matrix
+ matrix(
data: MathCollection,
format?: MatrixStorageFormat,
dataType?: string
@@ -922,7 +936,7 @@ export interface MathJsInstance extends MathJsFactory {
* @returns The created unit
*/
unit(value: MathNumericType, unit?: string): Unit
- unit(value: MathCollection): Unit[]
+ unit(value: MathCollection): Unit[]
/*************************************************************************
* Expression functions
@@ -1129,15 +1143,9 @@ export interface MathJsInstance extends MathJsFactory {
* @param node Tree to replace variable nodes in
* @param scope Scope to read/write variables
*/
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
resolve(node: MathNode | string, scope?: MathScope): MathNode
- resolve(
- node: (MathNode | string)[],
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- scope?: MathScope
- ): MathNode[]
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- resolve(node: Matrix, scope?: MathScope): Matrix
+ resolve(node: (MathNode | string)[], scope?: MathScope): MathNode[]
+ resolve(node: Matrix, scope?: MathScope): Matrix
/**
* Calculate the Sparse Matrix LU decomposition with full pivoting.
@@ -1193,7 +1201,11 @@ export interface MathJsInstance extends MathJsFactory {
add(x: T, y: T): T
add(x: T, y: T, ...values: T[]): T
add(x: MathType, y: MathType): MathType
- add(x: MathType, y: MathType, ...values: MathType[]): MathType
+ add(
+ x: MathType | string,
+ y: MathType | string,
+ ...values: (MathType | string)[]
+ ): MathType
/**
* Calculate the cubic root of a value.
@@ -1329,6 +1341,31 @@ export interface MathJsInstance extends MathJsFactory {
dotDivide(x: MathType, y: Unit): Unit
dotDivide(x: MathNumericType, y: MathNumericType): MathNumericType
+ /**
+ * Determine if one entity is a scalar multiple of another
+ * @param x Numerator
+ * @param y Denominator
+ * @returns
+ * If there is a scalar (including Complex) r such that
+ * x = r*y, returns r, otherwise undefined
+ */
+ scalarDivide(
+ x: MathScalarTypeIn,
+ y: MathScalarTypeIn
+ ): MathScalarTypeOut | undefined
+ scalarDivide(
+ x: MathCollection,
+ y: MathCollection
+ ): MathScalarTypeOut | undefined
+ scalarDivide(
+ x: MathScalarTypeIn,
+ y: MathCollection
+ ): undefined
+ scalarDivide(
+ x: MathCollection,
+ y: MathScalarTypeOut
+ ): undefined
+
/**
* Multiply two matrices element wise. The function accepts both
* matrices and scalar values.
@@ -1392,19 +1429,19 @@ export interface MathJsInstance extends MathJsFactory {
* Create a dense matrix from vectors as individual rows. If you pass column vectors, they will be transposed (but not conjugated!)
* @param rows - a multi-dimensional number array or matrix
*/
- matrixFromRows(...rows: Matrix[]): Matrix
- matrixFromRows(
- ...rows: (T[] | [T][] | Matrix)[]
- ): T[][]
+ matrixFromRows(...rows: T[][]): T[][]
+ matrixFromRows(
+ ...rows: (T[] | T[][] | Matrix)[]
+ ): Matrix
/**
* Create a dense matrix from vectors as individual columns. If you pass row vectors, they will be transposed (but not conjugated!)
* @param cols - a multi-dimensional number array or matrix
*/
- matrixFromColumns(...cols: Matrix[]): Matrix
- matrixFromColumns(
- ...cols: (T[] | [T][] | Matrix)[]
- ): T[][]
+ matrixFromColumns(...cols: T[][]): T[][]
+ matrixFromColumns(
+ ...cols: (T[] | T[][] | Matrix)[]
+ ): Matrix
/**
* Create a matrix by evaluating a generating function at each index. The simplest overload returns a multi-dimensional array as long as size is an array. Passing size as a Matrix or specifying a format will result in returning a Matrix.
* @param size - the size of the matrix to be created
@@ -1412,32 +1449,32 @@ export interface MathJsInstance extends MathJsFactory {
* @param format - The Matrix storage format, either 'dense' or 'sparse'
* @param datatype - Type of the values
*/
- matrixFromFunction(
+ matrixFromFunction(
size: [number],
fn: MatrixFromFunctionCallback
): T[]
- matrixFromFunction(
+ matrixFromFunction(
size: [number, number],
fn: MatrixFromFunctionCallback
): T[][]
- matrixFromFunction(
+ matrixFromFunction(
size: number[],
fn: MatrixFromFunctionCallback
): MathArray
matrixFromFunction(
size: Matrix,
- fn: MatrixFromFunctionCallback
+ fn: MatrixFromFunctionCallback
): Matrix
matrixFromFunction(
size: number[] | Matrix,
- fn: MatrixFromFunctionCallback,
+ fn: MatrixFromFunctionCallback,
format: MatrixStorageFormat,
datatype?: string
): Matrix
matrixFromFunction(
size: number[] | Matrix,
format: MatrixStorageFormat,
- fn: MatrixFromFunctionCallback,
+ fn: MatrixFromFunctionCallback,
datatype?: string
): Matrix
/**
@@ -1514,14 +1551,16 @@ export interface MathJsInstance extends MathJsFactory {
multiply(x: T, y: MathType): Matrix
multiply(x: MathType, y: T): Matrix
- multiply(x: T, y: T[]): T
- multiply(x: T[], y: T): T
- multiply(x: T[], y: T[]): T[]
- multiply(x: T, y: T): MathScalarType
- multiply(x: Unit, y: Unit): Unit
- multiply(x: number, y: number): number
- multiply(x: MathType, y: MathType, ...values: MathType[]): MathType
+ multiply(x: T[], y: T[][]): T[]
+ multiply(x: T[][], y: T[]): T[]
+ multiply(x: T[][], y: T[][]): T[][]
+ multiply(x: T, y: T): T
multiply(x: T, y: T, ...values: T[]): T
+ multiply(
+ x: MathType | string,
+ y: MathType | string,
+ ...values: (MathType | string)[]
+ ): MathType
/**
* Calculate the norm of a number, vector or matrix. The second
@@ -1559,6 +1598,21 @@ export interface MathJsInstance extends MathJsFactory {
*/
nthRoots(a: number | BigNumber | Complex, n?: number): Array
+ /**
+ * Returns the multiplicative identity of the same type as x
+ *
+ * @param x Any math entity
+ * @return Multiplicative identity of the type of x
+ */
+ one(x: number): 1
+ one(x: BigNumber): BigNumber
+ one(x: Complex): Complex
+ one(x: bigint): 1n
+ one(x: Fraction): Fraction
+ one(x: boolean): true
+ one(x: Unit): Unit
+ one(x: MathCollection): MathCollection
+
/**
* Calculates the power of x to y, x ^ y. Matrix exponentiation is
* supported for square matrices x, and positive integer exponents y.
@@ -1634,6 +1688,21 @@ export interface MathJsInstance extends MathJsFactory {
*/
xgcd(a: number | BigNumber, b: number | BigNumber): MathArray
+ /**
+ * Returns the multiplicative identity of the same type as x
+ *
+ * @param x Any math entity
+ * @return Multiplicative identity of the type of x
+ */
+ zero(x: number): 0
+ zero(x: BigNumber): BigNumber
+ zero(x: Complex): Complex
+ zero(x: bigint): 0n
+ zero(x: Fraction): Fraction
+ zero(x: boolean): true
+ zero(x: Unit): Unit
+ zero(x: MathCollection): MathCollection
+
/*************************************************************************
* Bitwise functions
************************************************************************/
@@ -2229,21 +2298,25 @@ export interface MathJsInstance extends MathJsFactory {
* @param format The matrix storage format
* @returns A matrix filled with ones
*/
- ones(
- size?: number | number[] | BigNumber | BigNumber[],
- format?: string
- ): MathCollection
+ ones(): MathCollection
+ ones(size: number, format?: string): MathCollection
+ ones(size: BigNumber, format?: string): MathCollection
+ ones(size: number[], format?: string): MathArray
+ ones(size: (number | BigNumber)[], format?: string): MathArray
+ ones(size: Matrix, format?: string): Matrix
+ ones(size: Matrix, format?: string): Matrix
/**
* @param m The x dimension of the matrix
* @param n The y dimension of the matrix
* @param format The matrix storage format
* @returns A matrix filled with ones
*/
+ ones(m: number, n: number, format?: string): MathCollection
ones(
m: number | BigNumber,
n: number | BigNumber,
format?: string
- ): MathCollection
+ ): MathCollection
/**
* @param m The x dimension of the matrix
* @param n The y dimension of the matrix
@@ -2251,12 +2324,13 @@ export interface MathJsInstance extends MathJsFactory {
* @param format The matrix storage format
* @returns A matrix filled with ones
*/
+ ones(m: number, n: number, p: number, format?: string): MathCollection
ones(
m: number | BigNumber,
n: number | BigNumber,
p: number | BigNumber,
format?: string
- ): MathCollection
+ ): MathCollection
/** Actually ones can take an arbitrary number of dimensions before the
** optional format, not sure how to write that in TypeScript
**/
@@ -2345,7 +2419,7 @@ export interface MathJsInstance extends MathJsFactory {
rotationMatrix(
theta?: number | BigNumber | Complex | Unit,
axis?: T,
- format?: 'sparse' | 'dense'
+ format?: MatrixStorageFormat
): T
/**
@@ -2960,27 +3034,37 @@ export interface MathJsInstance extends MathJsFactory {
* @param args Multiple scalar values
* @returns The maximum value
*/
- max(...args: T[]): T
+ // special case for common usage with two values
+ max(
+ x: T,
+ y: U
+ ): T | U
+ max(x: string, y: MathScalarTypeIn): MathScalarTypeOut
+ max(x: MathScalarTypeIn, y: string): MathScalarTypeOut
+ max(...args: T[]): T
/**
* @param args Multiple scalar values
* @returns The maximum value
*/
- max(...args: MathScalarType[]): MathScalarType
+ max(...args: MathScalarTypeIn[]): MathScalarTypeOut
/**
* @param A A single matrix
- * @param dimension The maximum over the selected dimension
* @returns The maximum value
*/
- max(
- A: T[] | T[][],
- dimension?: number | BigNumber
- ): T
+ max(A: MathCollection): T
/**
* @param A A single matrix
* @param dimension The maximum over the selected dimension
* @returns The maximum value
*/
- max(A: MathCollection, dimension?: number | BigNumber): MathScalarType
+ max(
+ A: MathCollection,
+ dimension?: number | BigNumber
+ ): T | MathCollection
+ max(
+ A: MathCollection,
+ dimension?: number | BigNumber
+ ): MathType
/**
* Compute the mean value of matrix or a list with values. In case of a
@@ -2990,27 +3074,26 @@ export interface MathJsInstance extends MathJsFactory {
* @param args Multiple scalar values
* @returns The mean of all values
*/
- mean(...args: T[]): T
+ mean(...args: T[]): T
/**
* @param args Multiple scalar values
* @returns The mean value
*/
- mean(...args: MathScalarType[]): MathScalarType
+ mean(...args: MathScalarTypeIn[]): MathScalarTypeOut
/**
* @param A A single matrix
- * @param dimension The mean over the selected dimension
* @returns The mean value
*/
- mean(
- A: T[] | T[][],
- dimension?: number | BigNumber
- ): T
+ mean(A: MathCollection): T
/**
* @param A A single matrix
* @param dimension The mean over the selected dimension
* @returns The mean value
*/
- mean(A: MathCollection, dimension?: number | BigNumber): MathScalarType
+ mean(
+ A: MathCollection,
+ dimension?: number | BigNumber
+ ): MathType
/**
* Compute the median of a matrix or a list with values. The values are
@@ -3022,22 +3105,22 @@ export interface MathJsInstance extends MathJsFactory {
* @param args Multiple scalar values
* @returns The median value
*/
- median(...args: T[]): T
+ median(...args: T[]): T
/**
* @param args Multiple scalar values
* @returns The median value
*/
- median(...args: MathScalarType[]): MathScalarType
+ median(...args: MathScalarTypeIn[]): MathScalarTypeOut
/**
* @param A A single matrix
* @returns The median value
*/
- median(A: T[] | T[][]): T
+ median(A: MathCollection): T
/**
* @param A A single matrix
* @returns The median value
*/
- median(A: MathCollection): MathScalarType
+ median(A: MathCollection): MathScalarTypeOut
/**
* Compute the minimum value of a matrix or a list of values. In case of
@@ -3047,27 +3130,37 @@ export interface MathJsInstance extends MathJsFactory {
* @param args multiple scalar values
* @returns The minimum value
*/
- min(...args: T[]): T
+ // special case for common usage of two types
+ min(
+ x: T,
+ y: U
+ ): T | U
+ min(x: string, y: MathScalarTypeIn): MathScalarTypeOut
+ min(x: MathScalarTypeIn, y: string): MathScalarTypeOut
+ min(...args: T[]): T
/**
* @param args Multiple scalar values
* @returns The minimum value
*/
- min(...args: MathScalarType[]): MathScalarType
+ min(...args: MathScalarTypeIn[]): MathScalarTypeOut
/**
* @param A A single matrix
- * @param dimension The minimum over the selected dimension
* @returns The minimum value
*/
- min(
- A: T[] | T[][],
- dimension?: number | BigNumber
- ): T
+ min(A: MathCollection): T
/**
* @param A A single matrix
* @param dimension The minimum over the selected dimension
* @returns The minimum value
*/
- min(A: MathCollection, dimension?: number | BigNumber): MathScalarType
+ min(
+ A: MathCollection,
+ dimension?: number | BigNumber
+ ): T | MathCollection
+ min(
+ A: MathCollection,
+ dimension?: number | BigNumber
+ ): MathType
/**
* Computes the mode of a set of numbers or a list with values(numbers
@@ -3076,22 +3169,17 @@ export interface MathJsInstance extends MathJsFactory {
* @param args Multiple scalar values
* @returns The mode of all values
*/
- mode(...args: T[]): T[]
+ mode(...args: T[]): T[]
/**
* @param args Multiple scalar values
* @returns The mode of all values
*/
- mode(...args: MathScalarType[]): MathScalarType[]
+ mode(...args: MathScalarTypeIn[]): MathScalarTypeIn[]
/**
* @param A A single matrix
* @returns The mode value
*/
- mode(A: T[] | T[][]): T[]
- /**
- * @param A A single matrix
- * @returns The mode of all values
- */
- mode(A: MathCollection): MathScalarType[]
+ mode(A: MathCollection): T[]
/**
* Compute the product of a matrix or a list with values. In case of a
@@ -3100,43 +3188,30 @@ export interface MathJsInstance extends MathJsFactory {
* @param args Multiple scalar values
* @returns The product of all values
*/
- prod(...args: T[]): T
+ prod(...args: T[]): T
/**
* @param args Multiple scalar values
* @returns The product of all values
*/
- prod(...args: MathScalarType[]): MathScalarType
+ prod(...args: MathScalarTypeIn[]): MathScalarTypeOut
/**
* @param A A single matrix
* @returns The product of all values
*/
- prod(A: T[] | T[][]): T
+ prod(A: MathCollection): T
/**
* @param A A single matrix
* @returns The product of all values
*/
- prod(A: MathCollection): MathScalarType
+ prod(A: MathCollection): MathScalarTypeOut
- /**
- * @param A A single matrix
- * @param probOrN prob is the order of the quantile, while N is the
- * amount of evenly distributed steps of probabilities; only one of
- * these options can be provided
- * @param sorted =false is data sorted in ascending order
- * @returns Quantile(s)
- */
- quantileSeq(
- A: T[] | T[][],
- prob: number | BigNumber,
- sorted?: boolean
- ): T
/**
* Compute the prob order quantile of a matrix or a list with values.
- * The sequence is sorted and the middle value is returned. Supported
+ * The sequence is sorted and the qunatile value(s) are returned. Supported
* types of sequence values are: Number, BigNumber, Unit Supported types
* of probability are: Number, BigNumber In case of a (multi
* dimensional) array or matrix, the prob order quantile of all elements
- * will be calculated.
+ * will be calculated, unless the "dimension" argument is specified.
* @param A A single matrix or array
* @param probOrN prob is the order of the quantile, while N is the
* amount of evenly distributed steps of probabilities; only one of
@@ -3144,11 +3219,28 @@ export interface MathJsInstance extends MathJsFactory {
* @param sorted =false is data sorted in ascending order
* @returns Quantile(s)
*/
- quantileSeq(
- A: MathCollection,
- prob: number | BigNumber | MathArray,
- sorted?: boolean
- ): MathScalarType | MathArray
+ quantileSeq(
+ A: MathCollection,
+ probOrN: number | BigNumber,
+ dimension: number
+ ): T | MathCollection // could be a cutoff or a number of quantiles
+ quantileSeq(
+ A: MathCollection,
+ probOrN: number | BigNumber,
+ sorted?: boolean,
+ dimension?: number
+ ): T | MathCollection // could be a cutoff or a number of quantiles
+ quantileSeq(
+ A: MathCollection,
+ prob: MathCollection,
+ dimension: number
+ ): MathCollection
+ quantileSeq(
+ A: MathCollection,
+ prob: MathCollection,
+ sorted?: boolean,
+ dimension?: number
+ ): MathCollection
/**
* Compute the standard deviation of a matrix or a list with values. The
@@ -3163,12 +3255,12 @@ export interface MathJsInstance extends MathJsFactory {
* @param args variadic argument of number to calculate standard deviation
* @returns The standard deviation
*/
- std(...args: T[]): T
+ std(...args: T[]): T
/**
* @param args Multiple scalar values
* @returns The standard deviation
*/
- std(...args: MathScalarType[]): MathScalarType
+ std(...args: MathScalarTypeIn[]): MathScalarTypeOut
/**
* Compute the standard deviation of a matrix or a list with values. The
* standard deviations is defined as the square root of the variance:
@@ -3187,10 +3279,10 @@ export interface MathJsInstance extends MathJsFactory {
* @returns The standard deviation array
*/
std(
- array: MathCollection,
+ array: MathCollection,
dimension?: number,
normalization?: 'unbiased' | 'uncorrected' | 'biased'
- ): MathNumericType[]
+ ): MathType
/**
* Compute the standard deviation of a matrix or a list with values. The
* standard deviations is defined as the square root of the variance:
@@ -3208,9 +3300,9 @@ export interface MathJsInstance extends MathJsFactory {
* @returns The standard deviation
*/
std(
- array: MathCollection,
+ array: MathCollection,
normalization: 'unbiased' | 'uncorrected' | 'biased'
- ): MathNumericType
+ ): MathScalarTypeOut
/**
* Compute the sum of a matrix or a list with values. In case of a
@@ -3219,27 +3311,26 @@ export interface MathJsInstance extends MathJsFactory {
* @param args A single matrix or multiple scalar values
* @returns The sum of all values
*/
- sum(...args: T[]): T
+ sum(...args: T[]): T
/**
* @param args Multiple scalar values
* @returns The sum of all values
*/
- sum(...args: MathScalarType[]): MathScalarType
+ sum(...args: MathScalarTypeIn[]): MathScalarTypeOut
/**
* @param A A single matrix
- * @param dimension The sum over the selected dimension
* @returns The sum of all values
*/
- sum(
- A: T[] | T[][],
- dimension?: number | BigNumber
- ): T
+ sum(A: MathCollection): T
/**
* @param A A single matrix
* @param dimension The sum over the selected dimension
* @returns The sum of all values
*/
- sum(A: MathCollection, dimension?: number | BigNumber): MathScalarType
+ sum(
+ A: MathCollection,
+ dimension?: number | BigNumber
+ ): MathType
/**
* Count the number of elements of a matrix, array or string.
@@ -3734,8 +3825,8 @@ export interface MathJsInstance extends MathJsFactory {
* @param x Value to be tested
* @returns Boolean | MathCollection
*/
- isFinite(x: MathScalarType): boolean
- isFinite(A: MathCollection): MathCollection
+ isFinite(x: MathScalarTypeIn): boolean
+ isFinite(A: MathCollection): MathCollection
/**
* Test whether a value is an integer number. The function supports
@@ -4033,6 +4124,7 @@ export const {
divideDependencies,
divideScalarDependencies,
dotDivideDependencies,
+ scalarDivideDependencies,
dotMultiplyDependencies,
dotPowDependencies,
expDependencies,
@@ -4055,6 +4147,7 @@ export const {
normDependencies,
nthRootDependencies,
nthRootsDependencies,
+ oneDependencies,
powDependencies,
roundDependencies,
signDependencies,
@@ -4064,6 +4157,7 @@ export const {
unaryMinusDependencies,
unaryPlusDependencies,
xgcdDependencies,
+ zeroDependencies,
// bitwise dependencies
bitAndDependencies,
@@ -4316,16 +4410,16 @@ export const {
printTransformDependencies
}: Record
-export interface Matrix {
+export interface Matrix {
type: string
storage(): string
datatype(): string
- create(data: MathArray, datatype?: string): void
+ create(data: MathArray, datatype?: string): Matrix
density(): number
// eslint-disable-next-line @typescript-eslint/no-explicit-any
subset(index: Index, replacement?: any, defaultValue?: any): Matrix
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- get(index: number[]): any
+ get(index: number[]): T
+ layer(index: number): T | Matrix
// eslint-disable-next-line @typescript-eslint/no-explicit-any
set(index: number[], value: any, defaultValue?: number | string): Matrix
resize(size: MathCollection, defaultValue?: number | string): Matrix
@@ -4333,12 +4427,11 @@ export interface Matrix {
size(): number[]
map(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- callback: (a: any, b: number[], c: Matrix) => any,
+ callback: (a: T, b: number[], c: Matrix) => any,
skipZeros?: boolean
): Matrix
forEach(
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- callback: (a: any, b: number[], c: Matrix) => void,
+ callback: (a: T, b: number[], c: Matrix) => void,
skipZeros?: boolean
): void
toArray(): MathArray
@@ -4350,9 +4443,6 @@ export interface Matrix {
toString(): string
// eslint-disable-next-line @typescript-eslint/no-explicit-any
toJSON(): any
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- diagonal(k?: number | BigNumber): any[]
- swapRows(i: number, j: number): Matrix
}
export interface MatrixCtor {
@@ -4997,7 +5087,7 @@ export interface MathJsChain {
*/
matrix(
this: MathJsChain,
- format?: 'sparse' | 'dense',
+ format?: MatrixStorageFormat,
dataType?: string
): MathJsChain
@@ -5500,6 +5590,27 @@ export interface MathJsChain {
y: MathNumericType
): MathJsChain
+ /**
+ * Check if one entity is a scalar multiple of another.
+ * @param y Denominator
+ */
+ scalarDivide(
+ this: MathJsChain,
+ y: MathScalarTypeIn
+ ): MathJsChain
+ scalarDivide(
+ this: MathJsChain,
+ y: MathCollection
+ ): MathJsChain
+ scalarDivide(
+ this: MathJsChain,
+ y: MathCollection
+ ): MathJsChain
+ scalarDivide(
+ this: MathJsChain,
+ y: MathScalarTypeIn
+ ): MathJsChain
+
/**
* Multiply two matrices element wise. The function accepts both
* matrices and scalar values.
@@ -5695,6 +5806,16 @@ export interface MathJsChain {
y: number | BigNumber | bigint | Complex
): MathJsChain
+ /**
+ * Generate the multiplicative identity of the current type
+ */
+ one(this: MathJsChain): MathJsChain<1>
+ one(this: MathJsChain): MathJsChain
+ one(this: MathJsChain): MathJsChain<1n>
+ one(this: MathJsChain): MathJsChain
+ one(this: MathJsChain): MathJsChain
+ one(this: MathJsChain): MathJsChain
+
/**
* Compute the sign of a value. The sign of a value x is: 1 when x > 1
* -1 when x < 0 0 when x == 0 For matrices, the function is evaluated
@@ -5754,6 +5875,16 @@ export interface MathJsChain {
b: number | BigNumber
): MathJsChain
+ /**
+ * Generate the additive identity of the current type
+ */
+ zero(this: MathJsChain): MathJsChain<0>
+ zero(this: MathJsChain): MathJsChain
+ zero(this: MathJsChain): MathJsChain<0n>
+ zero(this: MathJsChain): MathJsChain
+ zero(this: MathJsChain): MathJsChain
+ zero(this: MathJsChain): MathJsChain
+
/**
* Count the number of elements of a matrix, array or string.
*/
@@ -6874,7 +7005,12 @@ export interface MathJsChain {
* calculated.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- prod(this: MathJsChain): MathJsChain
+ prod(
+ this: MathJsChain>
+ ): MathJsChain
+ prod(
+ this: MathJsChain>
+ ): MathJsChain
/**
* Compute the prob order quantile of a matrix or a list with values.
@@ -7347,8 +7483,10 @@ export interface MathJsChain {
/**
* Test whether a value is finite, works elementwise on collections
*/
- isFinite(this: MathJsChain): MathJsChain
- isFinite(this: MathJsChain): MathJsChain
+ isFinite(this: MathJsChain): MathJsChain
+ isFinite(
+ this: MathJsChain>
+ ): MathJsChain>
/**
* Test whether a value is negative: smaller than zero. The function
@@ -7574,6 +7712,7 @@ export const {
cube,
divide,
dotDivide,
+ scalarDivide,
dotMultiply,
dotPow,
exp,
@@ -7595,6 +7734,7 @@ export const {
norm,
nthRoot,
nthRoots,
+ one,
pow,
round,
sign,
@@ -7604,6 +7744,7 @@ export const {
unaryMinus,
unaryPlus,
xgcd,
+ zero,
// bitwise
bitAnd,