smath 1.16.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,3 +23,54 @@ npx smath normalize 4 0 10
23
23
  ```
24
24
 
25
25
  > Most `SMath` functions are available through `npx`, except for calculus functions which require a functional argument.
26
+
27
+ ## Data Fitting
28
+
29
+ SMath also exports the `DataFit` object which contains only 1 function, `fit()` which is used for curve fitting. All other exports are purely for defining types used within `fit()`. `fit()` uses a genetic-style algorithm to fit a curve.
30
+
31
+ ### How it works
32
+
33
+ It generates many sets of parameters to test, and keeps ones with smaller error than the previous iteration. Parameter sets with larger errors are discarded. Each subsequent iteration uses the sets of parameters with the least error and "mutates" them randomly.
34
+
35
+ > Because of these random mutations, running the same code multiple times may yield slightly different results. See [best practices](#best-practices) for mitigation tactics.
36
+
37
+ ### Complexity
38
+
39
+ The folling factors affect computation time and resources:
40
+
41
+ - Number of iterations
42
+ - Number of independent/unknown function parameters
43
+ - Number of data points
44
+
45
+ Each one alone has a linear effect on performance, but combined has an incremental effect.
46
+
47
+ `time = iterations * ( parameters + dataset )`
48
+
49
+ The dimensionality, or number of free `x` variables per data point, should not have an impact on computation time.
50
+
51
+ ### Best Practices
52
+
53
+ For production software that relies on a best curve fit for data, it's best to avoid critical operations using `fit()` for a few reasons.
54
+
55
+ 1. `fit()` uses an algorithm that generates random mutations in a set of parameters, which could yield slightly different results, even if run on the same dataset.
56
+ 1. If the number of iterations is very high (in the millions or higher), it could have a significant effect on software performance.
57
+ 1. Due to a limitation in JavaScript/TypeScript, the input function parameters cannot be optional or have default values. If they do have default values, assign them inside the function body.
58
+
59
+ To circumvent some of these issues, the following is recommended.
60
+
61
+ 1. Use `datafit` during the testing phase of application development, and use the best-fit parameters as constants in the final application.
62
+ 1. If that is not possible, `datafit` may be helpful for determining an initial guess of curve fit constants, which can be input to `fit()` during production. The number of iterations could be reduced if the initial guess is reasonably close to the desired result.
63
+ 1. Using `datafit` primarily for data visualization or rough estimation.
64
+ 1. For operations that *need* `datafit`, a suggestion would be to run multiple iterations of `fit()` itself, and using each output as the subsequent call's input. This will converge to a result more effectively but could take longer.
65
+
66
+ And here are some general good practices.
67
+
68
+ 1. Avoid overfitting your data. That means, you should never have more function unknowns than the number of data points. `fit()` allows this for the rare chance that it is needed. Typically, having more, accurate data, is better for curve fitting.
69
+ 1. Reject outliers. `fit()` does not do this. Any outliers existing in the dataset will be treated like normal data points and could negatively impact the best fit.
70
+
71
+ There are also some great arguments and use cases for this function, namely...
72
+
73
+ 1. Data analysis and visualization.
74
+ 1. Quickly iterating on different model functions for determining which best fits the data.
75
+ 1. Curve fitting for multivariate or nonlinear models.
76
+ 1. The ease of use of it all! It's just one function call with as little as 2 inputs!
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import * as SMath from './index.js';
2
+ import { SMath } from './index.js';
3
3
  const func = (process.argv[2] ?? '').toLowerCase(), nums = process.argv.slice(3).map((arg, i) => {
4
4
  const num = Number.parseFloat(arg);
5
5
  if (Number.isFinite(num)) {
@@ -24,6 +24,7 @@ if (func.includes('help')) {
24
24
  console.log(' logspace <min> <max> <n> : Generate `n` logarithmically spaced numbers between `min` and `max`');
25
25
  console.log(' factorial <n> : Compute `n!` (factorial)');
26
26
  console.log(' factors <n> : List the prime factors of `n`');
27
+ console.log(' isPrime <n> : Determine if `n` is prime');
27
28
  console.log(' round2 <n> <base> : Round `n` to a multiple of any `base`');
28
29
  console.log(' error <exp> <act> : Calculate the normaized percent error between `exp` and `act`');
29
30
  console.log(' sum <c0> [c1] ... [cn] : Compute a total of `n` numbers');
@@ -85,6 +86,10 @@ switch (func) {
85
86
  console.log(SMath.factors(nums[0]));
86
87
  break;
87
88
  }
89
+ case ('isPrime'): {
90
+ console.log(SMath.isPrime(nums[0]));
91
+ break;
92
+ }
88
93
  case ('round2'): {
89
94
  console.log(SMath.round2(nums[0], nums[1]));
90
95
  break;
@@ -0,0 +1,2 @@
1
+ export * from './lib.js';
2
+ export * from './types.js';
@@ -0,0 +1,72 @@
1
+ import { SMath } from '../index.js';
2
+ /**
3
+ * Minimize the sum of squared errors to fit a set of data
4
+ * points to a curve with a set of unknown parameters.
5
+ * @param f The model function for curve fitting.
6
+ * @param data The entire dataset, as an array of points.
7
+ * @param params_initial The initial guess for function
8
+ * parameters, which defaults to an array filled with zeroes.
9
+ * @param iterations The number of parameter sets to generate.
10
+ * @param maxDeviation The relative standard parameter deviation.
11
+ * This is a number [0.0-1.0] and affects the standard deviation
12
+ * on the first iteration. Every subsequent iteration has a
13
+ * decayed standard deviation until the final iteration.
14
+ * @returns The set of parameters and error for the best fit.
15
+ * @example
16
+ * // Define model function
17
+ * function f(x: number, a2: number = -0.5, a1: number = 3.9, a0: number = -1.2): number {
18
+ * return a2 * x ** 2 + a1 * x + a0;
19
+ * }
20
+ * // Construct a data set
21
+ * const data: Datum<number>[] = [0, 2, 4].map(x => ({ x: x, y: f(x) }));
22
+ * // Compute best-fit summary
23
+ * const summary = fit(f, data);
24
+ */
25
+ export function fit(f, data, params_initial = [], iterations = 1e3, maxDeviation = 1) {
26
+ const N_params = f.length - 1;
27
+ if (params_initial.length === 0) {
28
+ params_initial.length = N_params;
29
+ params_initial.fill(0);
30
+ }
31
+ if (params_initial.length !== N_params) {
32
+ throw new Error('The initial guess should contain ' + N_params + ' parameters.');
33
+ }
34
+ if (maxDeviation <= 0) {
35
+ throw new Error('Standard deviation should be a positive value.');
36
+ }
37
+ let params = params_initial, error = err(f, params, data);
38
+ for (let i = 0; i < iterations; i++) {
39
+ const params_i = mutate(params, SMath.translate(i, 0, iterations, maxDeviation, 0)), error_i = err(f, params_i, data);
40
+ if (error_i < error) {
41
+ params = params_i;
42
+ error = error_i;
43
+ }
44
+ }
45
+ return {
46
+ f: (x) => f(x, ...params),
47
+ params: params,
48
+ error: error,
49
+ errorAvgAbs: Math.sqrt(error / data.length),
50
+ };
51
+ }
52
+ /**
53
+ * Calculate the sum of squared errors for a set of function parameters.
54
+ * @param f The model function for curve fitting.
55
+ * @param params The array of parameters to check.
56
+ * @param data The entire dataset, as an array of points.
57
+ * @returns The sum of squared errors.
58
+ */
59
+ function err(f, params, data) {
60
+ let sum = 0;
61
+ data.forEach(point => sum += (point.y - f(point.x, ...params)) ** 2);
62
+ return sum;
63
+ }
64
+ /**
65
+ * Randomly mutate the set of function parameters by some standard deviation.
66
+ * @param params The set of function parameters to mutate.
67
+ * @param deviation The standard relative amount to deviate in any direction.
68
+ * @returns A mutated set of parameters.
69
+ */
70
+ function mutate(params, deviation) {
71
+ return params.map(c => SMath.rnorm(c, deviation * Math.max(1, Math.abs(c))));
72
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js CHANGED
@@ -1,513 +1,4 @@
1
- /**
2
- * @packageDocumentation
3
- * Small math function library
4
- *
5
- * ![NPM Downloads](https://img.shields.io/npm/d18m/smath)
6
- * ![NPM Last Update](https://img.shields.io/npm/last-update/smath)
7
- */
8
- /**
9
- * Check if two numbers are approximately equal with a maximum abolute error.
10
- * @param a Any number
11
- * @param b Any number
12
- * @param epsilon Maximum absolute error
13
- * @returns True if `a` is approximately `b`
14
- * @example
15
- * const b1 = SMath.approx(1 / 3, 0.33, 1e-6), // false
16
- * b2 = SMath.approx(1 / 3, 0.33, 1e-2); // true
17
- */
18
- export function approx(a, b, epsilon = 1e-6) {
19
- return a - b < epsilon && b - a < epsilon;
20
- }
21
- /**
22
- * Clamp a number within a range.
23
- * @param n The number to clamp
24
- * @param min The minimum value of the range
25
- * @param max The maximum value of the range
26
- * @returns A clamped number
27
- * @example
28
- * const n1 = SMath.clamp(5, 0, 10), // 5
29
- * n2 = SMath.clamp(-2, 0, 10); // 0
30
- */
31
- export function clamp(n, min, max) {
32
- if (n < min) {
33
- return min;
34
- }
35
- if (n > max) {
36
- return max;
37
- }
38
- return n;
39
- }
40
- /**
41
- * Normalize the number `n` from the range `min, max` to the range `0, 1`
42
- * @param n The number to normalize
43
- * @param min The minimum value in the range
44
- * @param max The maximum value in the range
45
- * @returns A normalized value
46
- * @example
47
- * const y = SMath.normalize(18, 9, 99); // 0.1
48
- */
49
- export function normalize(n, min, max) {
50
- if (min === max) {
51
- return 0;
52
- }
53
- return (n - min) / (max - min);
54
- }
55
- /**
56
- * Expand a normalized number `n` to the range `min, max`
57
- * @param n A normalized number
58
- * @param min The minimum value in the range
59
- * @param max The maximum value in the range
60
- * @returns A value within the number range
61
- * @example
62
- * const y = SMath.expand(0.25, 4, 6); // 4.5
63
- */
64
- export function expand(n, min, max) {
65
- return (max - min) * n + min;
66
- }
67
- /**
68
- * Translate a number `n` from the range `min1, max1` to the range `min2, max2`
69
- * @param n The number to translate
70
- * @param min1 The minimum value from the initial range
71
- * @param max1 The maximum value from the initial range
72
- * @param min2 The minimum value for the final range
73
- * @param max2 The maximum value for the final range
74
- * @returns A translated number in the final range
75
- * @example
76
- * const C = 20,
77
- * F = SMath.translate(C, 0, 100, 32, 212); // 68
78
- */
79
- export function translate(n, min1, max1, min2, max2) {
80
- return expand(normalize(n, min1, max1), min2, max2);
81
- }
82
- /**
83
- * Generate an array of linearly spaced numbers.
84
- * @param min The initial value of the linear space
85
- * @param max The final value of the linear space
86
- * @param count The number of values in the space
87
- * @returns The linear space as an array of numbers
88
- * @example
89
- * const space = SMath.linspace(1, 5, 6);
90
- * // [ 1, 1.8, 2.6, 3.4, 4.2, 5 ]
91
- */
92
- export function linspace(min, max, count) {
93
- const space = [];
94
- for (let i = 0; i < count; i++) {
95
- space[i] = translate(i, 0, count - 1, min, max);
96
- }
97
- return space;
98
- }
99
- /**
100
- * Generate an array of logarithmically spaced numbers.
101
- * @param min The initial magnitude of the space
102
- * @param max The final magnitude of the space
103
- * @param count The number of values in the space
104
- * @returns The logarithmic space as an array of numbers
105
- * @example
106
- * const space = SMath.logspace(0, 2, 5);
107
- * // [ 1, 3.2, 10, 31.6, 100 ]
108
- */
109
- export function logspace(min, max, count) {
110
- return linspace(min, max, count).map(n => 10 ** n);
111
- }
112
- /**
113
- * Compute the factorial of `n`.
114
- * @param n Any positive integer
115
- * @returns `n!`
116
- * @example
117
- * const y = SMath.factorial(5); // 120
118
- */
119
- export function factorial(n) {
120
- if (n < 0 || (n | 0) !== n) {
121
- throw new Error('Input must be a positive integer.');
122
- }
123
- else if (n === 0) {
124
- return 1;
125
- }
126
- else if (n <= 2) {
127
- return n;
128
- }
129
- else {
130
- return n * factorial(n - 1);
131
- }
132
- }
133
- /**
134
- * Factorize `n` into its prime factors.
135
- * @param n Any positive integer
136
- * @returns The array of prime factors
137
- * @example
138
- * const y = SMath.factors(12); // [ 2, 2, 3 ]
139
- */
140
- export function factors(n) {
141
- if (n < 0 || (n | 0) !== n) {
142
- throw new Error('Input must be a positive integer!');
143
- }
144
- if (n <= 3) {
145
- return [n];
146
- }
147
- const f = [];
148
- let i = 2;
149
- while (n > 1 && i <= n) {
150
- if ((n / i) === ((n / i) | 0)) {
151
- n /= i;
152
- f.push(i);
153
- }
154
- else {
155
- i++;
156
- }
157
- }
158
- return f;
159
- }
160
- /**
161
- * Round a number to the nearest multiple of an arbitrary
162
- * base. Does not round when the base is set to zero.
163
- * @param n Any number to round
164
- * @param base Any base to round to
165
- * @returns `n` rounded to the nearest multiple of `base`
166
- * @example
167
- * const y = SMath.round2(Math.PI, 0.2); // 3.2
168
- */
169
- export function round2(n, base) {
170
- const rounded = base ? base * Math.round(n / base) : n;
171
- const precision = 10; // Removes precision errors
172
- return parseFloat(rounded.toFixed(precision));
173
- }
174
- /**
175
- * Calculate the relative normalized error or deviation from any
176
- * value to an accepted value. An error of 0 indicates that the
177
- * two values are identical. An error of -0.1 indicates that the
178
- * experimental value is 10% smaller than (90% of) the accepted
179
- * value. An error of 1.0 indicates that the experimental value
180
- * is 100% greater (or twice the size) of the accepted value.
181
- * @param experimental The value observed or produced by a test
182
- * @param actual The accepted or theoretical value
183
- * @returns The relative (normalized) error
184
- * @example
185
- * const e = SMath.error(22.5, 25); // -0.1
186
- */
187
- export function error(experimental, actual) {
188
- if (experimental === 0 && actual === 0) {
189
- return 0;
190
- }
191
- else {
192
- return (experimental - actual) / actual;
193
- }
194
- }
195
- /**
196
- * Add up all the inputs.
197
- * If none are present, returns 0.
198
- * @param data An array of numeric inputs
199
- * @returns The sum total
200
- * @example
201
- * const y = SMath.sum([1, 2, 3]); // 6
202
- */
203
- export function sum(data) {
204
- return data.reduce((a, b) => a + b, 0);
205
- }
206
- /**
207
- * Multiply all the inputs.
208
- * If none are present, returns 1.
209
- * @param data An array of numeric inputs
210
- * @returns The product
211
- * @example
212
- * const y = SMath.prod([2, 2, 3, 5]); // 60
213
- */
214
- export function prod(data) {
215
- return data.reduce((a, b) => a * b, 1);
216
- }
217
- /**
218
- * Compute the average, or mean, of a set of numbers.
219
- * @param data An array of numeric inputs
220
- * @returns The average, or mean
221
- * @example
222
- * const y = SMath.avg([1, 2, 4, 4]); // 2.75
223
- */
224
- export function avg(data) {
225
- return sum(data) / data.length;
226
- }
227
- /**
228
- * Compute the median of a set of numbers.
229
- * @param data An array of numeric inputs
230
- * @returns The median of the dataset
231
- * @example
232
- * const y = SMath.median([2, 5, 3, 1]); // 2.5
233
- */
234
- export function median(data) {
235
- data.sort((a, b) => a - b);
236
- if (data.length % 2) {
237
- return data[(data.length - 1) / 2];
238
- }
239
- return avg([data[data.length / 2 - 1], data[data.length / 2]]);
240
- }
241
- /**
242
- * Compute the variance of a **complete population**.
243
- * @param data An array of numeric inputs
244
- * @returns The population variance
245
- * @example
246
- * const y = SMath.varp([1, 2, 4, 4]); // 1.6875
247
- */
248
- export function varp(data) {
249
- const mean = avg(data), squares = data.map(x => (x - mean) ** 2);
250
- return sum(squares) / data.length;
251
- }
252
- /**
253
- * Compute the variance of a **sample**.
254
- * @param data An array of numeric inputs
255
- * @returns The sample variance
256
- * @example
257
- * const y = SMath.vars([1, 2, 4, 4]); // 2.25
258
- */
259
- export function vars(data) {
260
- const mean = avg(data), squares = data.map(x => (x - mean) ** 2);
261
- return sum(squares) / (data.length - 1);
262
- }
263
- /**
264
- * Compute the standard deviation of a **complete population**.
265
- * @param data An array of numeric inputs
266
- * @returns The population standard deviation
267
- * @example
268
- * const y = SMath.stdevp([1, 2, 3, 4]); // 1.118...
269
- */
270
- export function stdevp(data) {
271
- return Math.sqrt(varp(data));
272
- }
273
- /**
274
- * Compute the standard deviation of a **sample**.
275
- * @param data An array of numeric inputs
276
- * @returns The sample standard deviation
277
- * @example
278
- * const y = SMath.stdevs([1, 2, 3, 4]); // 1.29...
279
- */
280
- export function stdevs(data) {
281
- return Math.sqrt(vars(data));
282
- }
283
- /**
284
- * Generate a uniformly-distributed floating-point number within the range.
285
- * @param min The minimum bound
286
- * @param max The maximum bound
287
- * @returns A random float within the range
288
- * @example
289
- * const y = SMath.runif(-2, 2); // 0.376...
290
- */
291
- export function runif(min, max) {
292
- return expand(Math.random(), min, max);
293
- }
294
- /**
295
- * Generate a uniformly-distributed integer within the range.
296
- * @param min The minimum bound (inclusive)
297
- * @param max The maximum bound (inclusive)
298
- * @returns A random integer within the range
299
- * @example
300
- * const y = SMath.rint(-4, 3); // -4
301
- */
302
- export function rint(min, max) {
303
- min |= 0;
304
- max |= 0;
305
- if (min < 0) {
306
- min--;
307
- }
308
- if (max > 0) {
309
- max++;
310
- }
311
- return clamp(runif(min, max), min, max) | 0; // `| 0` pulls toward 0
312
- }
313
- /**
314
- * Generate a normally-distributed floating-point number.
315
- * @param mean The mean of the population distribution
316
- * @param stdev The standard deviation of the population
317
- * @returns A random float
318
- * @example
319
- * const y = SMath.rnorm(2, 3); // 1.627...
320
- */
321
- export function rnorm(mean = 0, stdev = 1) {
322
- return mean + stdev * Math.sqrt(-2 * Math.log(Math.random())) * Math.cos(2 * Math.PI * Math.random());
323
- }
324
- /**
325
- * Generate a population of normally-distributed floating-point numbers.
326
- * @param count The number of values to generate
327
- * @param mean The mean of the population distribution
328
- * @param stdev The standard deviation of the population
329
- * @returns A population of random floats
330
- * @example
331
- * const dataset = SMath.rdist(3); // [ 1.051..., -0.779..., -2.254... ]
332
- */
333
- export function rdist(count, mean = 0, stdev = 1) {
334
- const distribution = [];
335
- for (let i = 0; i < count; i++) {
336
- distribution[i] = rnorm(mean, stdev);
337
- }
338
- return distribution;
339
- }
340
- /**
341
- * Randomize an array of arbitrary elements.
342
- * @param stack An array of arbitrary elements
343
- * @returns The `stack` array in a random order
344
- * @example
345
- * const shuffled = SMath.shuffle(['a', 'b', 'c']); // [ 'c', 'a', 'b' ]
346
- */
347
- export function shuffle(stack) {
348
- const rawData = [];
349
- for (const item of stack) {
350
- rawData.push({ index: Math.random(), value: item });
351
- }
352
- return rawData.sort((a, b) => a.index - b.index).map(a => a.value);
353
- }
354
- /**
355
- * Select a single item from an array at random with uniform weights.
356
- * @param stack An array of arbirary item
357
- * @returns A single randomly selected item
358
- * @example
359
- * const selected = SMath.selectRandom([10, 20, 30, 40]); // 30
360
- */
361
- export function selectRandom(stack) {
362
- return stack[rint(0, stack.length - 1)];
363
- }
364
- /**
365
- * Select a single index in an array at random with different weights.
366
- * @param weights The weights for each item
367
- * @returns The 0-based index of the randomly selected item
368
- * @example
369
- * const index = SMath.selectRandomWeighted([3.5, 4, 1]); // 1
370
- */
371
- export function selectRandomWeighted(weights) {
372
- const startWeights = [];
373
- let accumulation = 0;
374
- for (const weight of weights) {
375
- accumulation += clamp(0, weight, Infinity);
376
- startWeights.push(accumulation);
377
- }
378
- const random = runif(0, accumulation);
379
- return startWeights.findIndex(weight => random < weight);
380
- }
381
- /**
382
- * Take the limit of a function. A return value of `NaN` indicates
383
- * that no limit exists either due to a discontinuity or imaginary value.
384
- * @param f Function `f(x)`
385
- * @param x The x-value where to take the limit
386
- * @param h The approach distance
387
- * @param discontinuity_cutoff The discontinuity cutoff
388
- * @returns `lim(f(x->x))`
389
- * @example
390
- * const y = SMath.lim(Math.log, 0); // -Infinity
391
- */
392
- export function lim(f, x, h = 1e-6, discontinuity_cutoff = 1e-3) {
393
- const center = f(x), left1 = f(x - h), left2 = f(x - h / 2), right1 = f(x + h), right2 = f(x + h / 2);
394
- let left, right;
395
- if (Number.isFinite(center)) {
396
- return center;
397
- }
398
- // Check the limit approaching from the left
399
- if (Number.isFinite(left1) && Number.isFinite(left2)) {
400
- if (approx(left1, left2, discontinuity_cutoff)) {
401
- left = left2; // Converges
402
- }
403
- else if (left1 > 0 && left2 > left1) {
404
- left = Infinity; // Diverges to +inf
405
- }
406
- else if (left1 < 0 && left2 < left1) {
407
- left = -Infinity; // Diverges to -inf
408
- }
409
- else {
410
- left = NaN; // Diverges
411
- }
412
- }
413
- else {
414
- left = NaN; // Discontinuous
415
- }
416
- // Check the limit approaching from the right
417
- if (Number.isFinite(right1) && Number.isFinite(right2)) {
418
- if (approx(right1, right2, discontinuity_cutoff)) {
419
- right = right2; // Converges
420
- }
421
- else if (right1 > 0 && right2 > right1) {
422
- right = Infinity; // Diverges to +inf
423
- }
424
- else if (right1 < 0 && right2 < right1) {
425
- right = -Infinity; // Diverges to -inf
426
- }
427
- else {
428
- right = NaN; // Diverges
429
- }
430
- }
431
- else {
432
- right = NaN; // Discontinuous
433
- }
434
- // Check if limits match or are close
435
- if (left === right) { // Handles +/-Infinity case
436
- return left;
437
- }
438
- else if (Number.isNaN(left) && Number.isNaN(right)) {
439
- return center;
440
- }
441
- else if (!Number.isNaN(left) && Number.isNaN(right)) {
442
- return left;
443
- }
444
- else if (Number.isNaN(left) && !Number.isNaN(right)) {
445
- return right;
446
- }
447
- else if (approx(left, right, discontinuity_cutoff)) {
448
- return avg([left, right]);
449
- }
450
- else {
451
- return NaN;
452
- }
453
- }
454
- /**
455
- * Take the derivative of a function.
456
- * @param f Function `f(x)`
457
- * @param x The x-value where to evaluate the derivative
458
- * @param epsilon Small step value
459
- * @returns `f'(x)`
460
- * @example
461
- * const y = SMath.differentiate(x => 3 * x ** 2, 2); // 12
462
- */
463
- export function differentiate(f, x, epsilon = 1e-6) {
464
- return lim(h => (f(x + h) - f(x - h)) / (2 * h), 0, epsilon);
465
- }
466
- /**
467
- * Compute the definite integral of a function.
468
- * @param f Function `f(x)`
469
- * @param a The miminum integral bound
470
- * @param b The maximum integral bound
471
- * @param Ndx The number of rectangles to compute
472
- * @returns `F(b)-F(a)`
473
- * @example
474
- * const y = SMath.integrate(x => 3 * x ** 2, 1, 2); // 7
475
- */
476
- export function integrate(f, a, b, Ndx = 1e6) {
477
- return ((b - a) / Ndx) * sum(linspace(a, b, Ndx).map(x => f(x)));
478
- }
479
- /**
480
- * Convert an arbitrary decimal number into a simplified fraction (or ratio).
481
- * See `mixed()` for instructions on how to break out the whole number part.
482
- * @param n The decimal number to convert
483
- * @param epsilon Maximum absolute error
484
- * @returns An object containing the fraction's numerator and denominator
485
- * @example
486
- * const frac = SMath.rat(0.625); // { num: 5, den: 8 }
487
- */
488
- export function rat(n, epsilon = 1e-6) {
489
- let num = 0, den = 1;
490
- const sign = n < 0 ? -1 : 1;
491
- while (!approx(sign * n, num / den, epsilon)) {
492
- if (sign * n > num / den) {
493
- num++;
494
- }
495
- else {
496
- den++;
497
- }
498
- }
499
- return { num: sign * num, den: den };
500
- }
501
- /**
502
- * Convert an arbitrary decimal number into a simplified fraction, after
503
- * breaking out the whole number part first. See `rat()` for keeping the
504
- * number as a ratio without separating the whole number part.
505
- * @param n A decimal number to convert
506
- * @param epsilon Maximum absolute error
507
- * @returns An object containing the whole part and fraction numerator and denominator
508
- * @example
509
- * const frac = SMath.mixed(-8 / 6); // { whole: -1, num: 1, den: 3 }
510
- */
511
- export function mixed(n, epsilon = 1e-6) {
512
- return { whole: n | 0, ...rat(n < -1 ? (n | 0) - n : n - (n | 0), epsilon) };
513
- }
1
+ import * as SMath_1 from './smath.js';
2
+ export { SMath_1 as SMath };
3
+ import * as DataFit_1 from './datafit/index.js';
4
+ export { DataFit_1 as DataFit };