1 |
|
2 |
|
3 | function factory (type, config, load, typed) {
|
4 | const matrix = load(require('../../type/matrix/function/matrix'))
|
5 |
|
6 | const ZERO = new type.BigNumber(0)
|
7 | const ONE = new type.BigNumber(1)
|
8 |
|
9 | /**
|
10 | * Create an array from a range.
|
11 | * By default, the range end is excluded. This can be customized by providing
|
12 | * an extra parameter `includeEnd`.
|
13 | *
|
14 | * Syntax:
|
15 | *
|
16 | * math.range(str [, includeEnd]) // Create a range from a string,
|
17 | * // where the string contains the
|
18 | * // start, optional step, and end,
|
19 | * // separated by a colon.
|
20 | * math.range(start, end [, includeEnd]) // Create a range with start and
|
21 | * // end and a step size of 1.
|
22 | * math.range(start, end, step [, includeEnd]) // Create a range with start, step,
|
23 | * // and end.
|
24 | *
|
25 | * Where:
|
26 | *
|
27 | * - `str: string`
|
28 | * A string 'start:end' or 'start:step:end'
|
29 | * - `start: {number | BigNumber}`
|
30 | * Start of the range
|
31 | * - `end: number | BigNumber`
|
32 | * End of the range, excluded by default, included when parameter includeEnd=true
|
33 | * - `step: number | BigNumber`
|
34 | * Step size. Default value is 1.
|
35 | * - `includeEnd: boolean`
|
36 | * Option to specify whether to include the end or not. False by default.
|
37 | *
|
38 | * Examples:
|
39 | *
|
40 | * math.range(2, 6) // [2, 3, 4, 5]
|
41 | * math.range(2, -3, -1) // [2, 1, 0, -1, -2]
|
42 | * math.range('2:1:6') // [2, 3, 4, 5]
|
43 | * math.range(2, 6, true) // [2, 3, 4, 5, 6]
|
44 | *
|
45 | * See also:
|
46 | *
|
47 | * ones, zeros, size, subset
|
48 | *
|
49 | * @param {*} args Parameters describing the ranges `start`, `end`, and optional `step`.
|
50 | * @return {Array | Matrix} range
|
51 | */
|
52 | const range = typed('range', {
|
53 | // TODO: simplify signatures when typed-function supports default values and optional arguments
|
54 |
|
55 | // TODO: a number or boolean should not be converted to string here
|
56 | 'string': _strRange,
|
57 | 'string, boolean': _strRange,
|
58 |
|
59 | 'number, number': function (start, end) {
|
60 | return _out(_rangeEx(start, end, 1))
|
61 | },
|
62 | 'number, number, number': function (start, end, step) {
|
63 | return _out(_rangeEx(start, end, step))
|
64 | },
|
65 | 'number, number, boolean': function (start, end, includeEnd) {
|
66 | return includeEnd
|
67 | ? _out(_rangeInc(start, end, 1))
|
68 | : _out(_rangeEx(start, end, 1))
|
69 | },
|
70 | 'number, number, number, boolean': function (start, end, step, includeEnd) {
|
71 | return includeEnd
|
72 | ? _out(_rangeInc(start, end, step))
|
73 | : _out(_rangeEx(start, end, step))
|
74 | },
|
75 |
|
76 | 'BigNumber, BigNumber': function (start, end) {
|
77 | return _out(_bigRangeEx(start, end, ONE))
|
78 | },
|
79 | 'BigNumber, BigNumber, BigNumber': function (start, end, step) {
|
80 | return _out(_bigRangeEx(start, end, step))
|
81 | },
|
82 | 'BigNumber, BigNumber, boolean': function (start, end, includeEnd) {
|
83 | return includeEnd
|
84 | ? _out(_bigRangeInc(start, end, ONE))
|
85 | : _out(_bigRangeEx(start, end, ONE))
|
86 | },
|
87 | 'BigNumber, BigNumber, BigNumber, boolean': function (start, end, step, includeEnd) {
|
88 | return includeEnd
|
89 | ? _out(_bigRangeInc(start, end, step))
|
90 | : _out(_bigRangeEx(start, end, step))
|
91 | }
|
92 |
|
93 | })
|
94 |
|
95 | range.toTex = undefined // use default template
|
96 |
|
97 | return range
|
98 |
|
99 | function _out (arr) {
|
100 | return config.matrix === 'Array' ? arr : matrix(arr)
|
101 | }
|
102 |
|
103 | function _strRange (str, includeEnd) {
|
104 | const r = _parse(str)
|
105 | if (!r) {
|
106 | throw new SyntaxError('String "' + str + '" is no valid range')
|
107 | }
|
108 |
|
109 | let fn
|
110 | if (config.number === 'BigNumber') {
|
111 | fn = includeEnd ? _bigRangeInc : _bigRangeEx
|
112 | return _out(fn(
|
113 | new type.BigNumber(r.start),
|
114 | new type.BigNumber(r.end),
|
115 | new type.BigNumber(r.step)))
|
116 | } else {
|
117 | fn = includeEnd ? _rangeInc : _rangeEx
|
118 | return _out(fn(r.start, r.end, r.step))
|
119 | }
|
120 | }
|
121 |
|
122 | /**
|
123 | * Create a range with numbers. End is excluded
|
124 | * @param {number} start
|
125 | * @param {number} end
|
126 | * @param {number} step
|
127 | * @returns {Array} range
|
128 | * @private
|
129 | */
|
130 | function _rangeEx (start, end, step) {
|
131 | const array = []
|
132 | let x = start
|
133 | if (step > 0) {
|
134 | while (x < end) {
|
135 | array.push(x)
|
136 | x += step
|
137 | }
|
138 | } else if (step < 0) {
|
139 | while (x > end) {
|
140 | array.push(x)
|
141 | x += step
|
142 | }
|
143 | }
|
144 |
|
145 | return array
|
146 | }
|
147 |
|
148 | /**
|
149 | * Create a range with numbers. End is included
|
150 | * @param {number} start
|
151 | * @param {number} end
|
152 | * @param {number} step
|
153 | * @returns {Array} range
|
154 | * @private
|
155 | */
|
156 | function _rangeInc (start, end, step) {
|
157 | const array = []
|
158 | let x = start
|
159 | if (step > 0) {
|
160 | while (x <= end) {
|
161 | array.push(x)
|
162 | x += step
|
163 | }
|
164 | } else if (step < 0) {
|
165 | while (x >= end) {
|
166 | array.push(x)
|
167 | x += step
|
168 | }
|
169 | }
|
170 |
|
171 | return array
|
172 | }
|
173 |
|
174 | /**
|
175 | * Create a range with big numbers. End is excluded
|
176 | * @param {BigNumber} start
|
177 | * @param {BigNumber} end
|
178 | * @param {BigNumber} step
|
179 | * @returns {Array} range
|
180 | * @private
|
181 | */
|
182 | function _bigRangeEx (start, end, step) {
|
183 | const array = []
|
184 | let x = start
|
185 | if (step.gt(ZERO)) {
|
186 | while (x.lt(end)) {
|
187 | array.push(x)
|
188 | x = x.plus(step)
|
189 | }
|
190 | } else if (step.lt(ZERO)) {
|
191 | while (x.gt(end)) {
|
192 | array.push(x)
|
193 | x = x.plus(step)
|
194 | }
|
195 | }
|
196 |
|
197 | return array
|
198 | }
|
199 |
|
200 | /**
|
201 | * Create a range with big numbers. End is included
|
202 | * @param {BigNumber} start
|
203 | * @param {BigNumber} end
|
204 | * @param {BigNumber} step
|
205 | * @returns {Array} range
|
206 | * @private
|
207 | */
|
208 | function _bigRangeInc (start, end, step) {
|
209 | const array = []
|
210 | let x = start
|
211 | if (step.gt(ZERO)) {
|
212 | while (x.lte(end)) {
|
213 | array.push(x)
|
214 | x = x.plus(step)
|
215 | }
|
216 | } else if (step.lt(ZERO)) {
|
217 | while (x.gte(end)) {
|
218 | array.push(x)
|
219 | x = x.plus(step)
|
220 | }
|
221 | }
|
222 |
|
223 | return array
|
224 | }
|
225 |
|
226 | /**
|
227 | * Parse a string into a range,
|
228 | * The string contains the start, optional step, and end, separated by a colon.
|
229 | * If the string does not contain a valid range, null is returned.
|
230 | * For example str='0:2:11'.
|
231 | * @param {string} str
|
232 | * @return {{start: number, end: number, step: number} | null} range Object containing properties start, end, step
|
233 | * @private
|
234 | */
|
235 | function _parse (str) {
|
236 | const args = str.split(':')
|
237 |
|
238 | // number
|
239 | const nums = args.map(function (arg) {
|
240 | // use Number and not parseFloat as Number returns NaN on invalid garbage in the string
|
241 | return Number(arg)
|
242 | })
|
243 |
|
244 | const invalid = nums.some(function (num) {
|
245 | return isNaN(num)
|
246 | })
|
247 | if (invalid) {
|
248 | return null
|
249 | }
|
250 |
|
251 | switch (nums.length) {
|
252 | case 2:
|
253 | return {
|
254 | start: nums[0],
|
255 | end: nums[1],
|
256 | step: 1
|
257 | }
|
258 |
|
259 | case 3:
|
260 | return {
|
261 | start: nums[0],
|
262 | end: nums[2],
|
263 | step: nums[1]
|
264 | }
|
265 |
|
266 | default:
|
267 | return null
|
268 | }
|
269 | }
|
270 | }
|
271 |
|
272 | exports.name = 'range'
|
273 | exports.factory = factory
|