1 |
|
2 |
|
3 | const clone = require('../../utils/object').clone
|
4 | const array = require('../../utils/array')
|
5 | const IndexError = require('../../error/IndexError')
|
6 | const DimensionError = require('../../error/DimensionError')
|
7 |
|
8 | function factory (type, config, load, typed) {
|
9 | const matrix = load(require('../../type/matrix/function/matrix'))
|
10 | const isInteger = load(require('../utils/isInteger'))
|
11 |
|
12 | /**
|
13 | * Concatenate two or more matrices.
|
14 | *
|
15 | * Syntax:
|
16 | *
|
17 | * math.concat(A, B, C, ...)
|
18 | * math.concat(A, B, C, ..., dim)
|
19 | *
|
20 | * Where:
|
21 | *
|
22 | * - `dim: number` is a zero-based dimension over which to concatenate the matrices.
|
23 | * By default the last dimension of the matrices.
|
24 | *
|
25 | * Examples:
|
26 | *
|
27 | * const A = [[1, 2], [5, 6]]
|
28 | * const B = [[3, 4], [7, 8]]
|
29 | *
|
30 | * math.concat(A, B) // returns [[1, 2, 3, 4], [5, 6, 7, 8]]
|
31 | * math.concat(A, B, 0) // returns [[1, 2], [5, 6], [3, 4], [7, 8]]
|
32 | * math.concat('hello', ' ', 'world') // returns 'hello world'
|
33 | *
|
34 | * See also:
|
35 | *
|
36 | * size, squeeze, subset, transpose
|
37 | *
|
38 | * @param {... Array | Matrix} args Two or more matrices
|
39 | * @return {Array | Matrix} Concatenated matrix
|
40 | */
|
41 | const concat = typed('concat', {
|
42 | // TODO: change signature to '...Array | Matrix, dim?' when supported
|
43 | '...Array | Matrix | number | BigNumber': function (args) {
|
44 | let i
|
45 | const len = args.length
|
46 | let dim = -1 // zero-based dimension
|
47 | let prevDim
|
48 | let asMatrix = false
|
49 | const matrices = [] // contains multi dimensional arrays
|
50 |
|
51 | for (i = 0; i < len; i++) {
|
52 | const arg = args[i]
|
53 |
|
54 | // test whether we need to return a Matrix (if not we return an Array)
|
55 | if (type.isMatrix(arg)) {
|
56 | asMatrix = true
|
57 | }
|
58 |
|
59 | if (type.isNumber(arg) || type.isBigNumber(arg)) {
|
60 | if (i !== len - 1) {
|
61 | throw new Error('Dimension must be specified as last argument')
|
62 | }
|
63 |
|
64 | // last argument contains the dimension on which to concatenate
|
65 | prevDim = dim
|
66 | dim = arg.valueOf() // change BigNumber to number
|
67 |
|
68 | if (!isInteger(dim)) {
|
69 | throw new TypeError('Integer number expected for dimension')
|
70 | }
|
71 |
|
72 | if (dim < 0 || (i > 0 && dim > prevDim)) {
|
73 | // TODO: would be more clear when throwing a DimensionError here
|
74 | throw new IndexError(dim, prevDim + 1)
|
75 | }
|
76 | } else {
|
77 | // this is a matrix or array
|
78 | const m = clone(arg).valueOf()
|
79 | const size = array.size(m)
|
80 | matrices[i] = m
|
81 | prevDim = dim
|
82 | dim = size.length - 1
|
83 |
|
84 | // verify whether each of the matrices has the same number of dimensions
|
85 | if (i > 0 && dim !== prevDim) {
|
86 | throw new DimensionError(prevDim + 1, dim + 1)
|
87 | }
|
88 | }
|
89 | }
|
90 |
|
91 | if (matrices.length === 0) {
|
92 | throw new SyntaxError('At least one matrix expected')
|
93 | }
|
94 |
|
95 | let res = matrices.shift()
|
96 | while (matrices.length) {
|
97 | res = _concat(res, matrices.shift(), dim, 0)
|
98 | }
|
99 |
|
100 | return asMatrix ? matrix(res) : res
|
101 | },
|
102 |
|
103 | '...string': function (args) {
|
104 | return args.join('')
|
105 | }
|
106 | })
|
107 |
|
108 | concat.toTex = undefined // use default template
|
109 |
|
110 | return concat
|
111 | }
|
112 |
|
113 | /**
|
114 | * Recursively concatenate two matrices.
|
115 | * The contents of the matrices is not cloned.
|
116 | * @param {Array} a Multi dimensional array
|
117 | * @param {Array} b Multi dimensional array
|
118 | * @param {number} concatDim The dimension on which to concatenate (zero-based)
|
119 | * @param {number} dim The current dim (zero-based)
|
120 | * @return {Array} c The concatenated matrix
|
121 | * @private
|
122 | */
|
123 | function _concat (a, b, concatDim, dim) {
|
124 | if (dim < concatDim) {
|
125 | // recurse into next dimension
|
126 | if (a.length !== b.length) {
|
127 | throw new DimensionError(a.length, b.length)
|
128 | }
|
129 |
|
130 | const c = []
|
131 | for (let i = 0; i < a.length; i++) {
|
132 | c[i] = _concat(a[i], b[i], concatDim, dim + 1)
|
133 | }
|
134 | return c
|
135 | } else {
|
136 | // concatenate this dimension
|
137 | return a.concat(b)
|
138 | }
|
139 | }
|
140 |
|
141 | exports.name = 'concat'
|
142 | exports.factory = factory
|