UNPKG

4.19 kBJavaScriptView Raw
1'use strict'
2
3const clone = require('../../utils/object').clone
4const array = require('../../utils/array')
5const IndexError = require('../../error/IndexError')
6const DimensionError = require('../../error/DimensionError')
7
8function 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 */
123function _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
141exports.name = 'concat'
142exports.factory = factory