UNPKG

7.22 kBJavaScriptView Raw
1'use strict'
2
3function 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
272exports.name = 'range'
273exports.factory = factory