UNPKG

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