@iyulab/u-numflow 0.13.1 → 0.14.1

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
@@ -24,6 +24,7 @@ u-numflow provides foundational mathematical, statistical, and probabilistic bui
24
24
  | `random` | Seeded RNG, Fisher-Yates shuffle, weighted sampling, random subset selection |
25
25
  | `distributions::Sample` | Random variates from every continuous distribution — `sample(&mut rng)` and reproducible `sample_n(n, seed)`; inversion where the quantile is direct, Marsaglia–Tsang for the gamma family (Gamma, χ², Beta, PERT); uniforms from the open interval (0, 1), so no variate is infinite |
26
26
  | `collections` | Specialized data structures: Union-Find with path compression and union-by-rank; `IntervalSet` — unions of half-open intervals with union, intersection, difference, clip and a measure that counts overlapping input once |
27
+ | `units` | Units of measure: dimensional analysis over the seven SI base quantities, UCUM-syntax unit expressions (`kW.h`, `m/s2`, `J/(kg.K)`), conversion of readings (with a scale's offset — 20 °C is 293.15 K) and of differences (without — a rise of 20 °C is a rise of 20 K); the SI built in, every other unit defined by the caller |
27
28
 
28
29
  ## Design Philosophy
29
30
 
@@ -35,7 +36,7 @@ u-numflow provides foundational mathematical, statistical, and probabilistic bui
35
36
 
36
37
  ```toml
37
38
  [dependencies]
38
- u-numflow = "0.13"
39
+ u-numflow = "0.14"
39
40
  ```
40
41
 
41
42
  ```rust
@@ -86,6 +87,31 @@ let up = week.difference(&planned.union(&unplanned));
86
87
  assert_eq!(up.measure(), 138.0);
87
88
  ```
88
89
 
90
+ ```rust
91
+ // Units: the SI is built in; a unit whose size varies by convention is yours to define
92
+ use u_numflow::units::Units;
93
+ let mut units = Units::si();
94
+ units.define("cal_IT", 4.1868, "J", 0.0).unwrap(); // the international calorie
95
+ units.define("toe", 1e7, "kcal_IT", 0.0).unwrap(); // tonne of oil equivalent
96
+ assert!((units.convert(1.0, "toe", "GJ").unwrap() - 41.868).abs() < 1e-9);
97
+ assert!((units.convert(20.0, "Cel", "K").unwrap() - 293.15).abs() < 1e-9); // a reading
98
+ assert_eq!(units.convert_difference(20.0, "Cel", "K").unwrap(), 20.0); // a change
99
+ assert!(units.convert(1.0, "m3", "kg").is_err()); // volume is not mass
100
+ ```
101
+
102
+ Unit expressions follow UCUM's case-sensitive syntax: an atom with an optional
103
+ decimal prefix (`km`, `kW`, `uA` — `u` is micro), an integer exponent after it
104
+ (`m3`, `s-2`), `.` for a product and `/` for a quotient read left to right
105
+ (`kg/m.s` is `(kg/m)·s`), parentheses, integer factors (`1000.m`), and `{…}`
106
+ annotations, which have no effect (`m3{gas}`). Built in: the SI base units
107
+ (`m`, `g`, `s`, `A`, `K`, `mol`, `cd`), the 22 derived units with special names
108
+ (`N`, `Pa`, `J`, `W`, `Cel`, …), `min`, `h`, `d`, `L`/`l`, `t`, and the prefixes
109
+ `Q` … `q`. No unit table beyond the SI is embedded: the calorie (three
110
+ definitions, up to 0.07 % apart), the tonne of oil equivalent, imperial units
111
+ and the normal cubic metre are the caller's `define` calls, so the crate never
112
+ picks one convention silently. A scale with an offset (`Cel`) stands alone — a
113
+ prefix, exponent or product on it is refused.
114
+
89
115
  `IntervalSet<T>` works over `f32`, `f64` and every primitive integer type. Its
90
116
  measure has the type of a distance — the unsigned type of the same width for
91
117
  integers (as `i64::abs_diff` returns `u64`), so it cannot overflow. A reversed
@@ -114,8 +140,8 @@ MIT License — see [LICENSE](LICENSE).
114
140
  dotnet add package UNumflow
115
141
  ```
116
142
 
117
- The distributions (CDF, quantile, moments, seeded sampling) and interval sets,
118
- backed by this crate through its C ABI (the `ffi` feature) — see
143
+ The distributions (CDF, quantile, moments, seeded sampling), sample statistics
144
+ (median, R-7 quantiles), interval sets and units, backed by this crate through its C ABI (the `ffi` feature) — see
119
145
  [bindings/csharp/README.md](bindings/csharp/README.md). Refusals carry the same
120
146
  `code` and fields as the WebAssembly binding's.
121
147
 
@@ -137,7 +163,7 @@ file with an ES module import, which browsers refuse (`application/wasm` is not
137
163
  module script type), so `<script type="module">` from a CDN fails, and CDN
138
164
  re-bundling services fail on the same import. Use a bundler or Node.
139
165
 
140
- Exported functions: `mean`, `std_dev`, `variance`, `normal_cdf`, `normal_sf` (upper tail `P(Z > x)`, computed directly so tail probabilities keep ~15 significant digits), `box_cox`, `estimate_lambda` (returns `{ lambda, at_bound }` — `at_bound` is `true` when the likelihood was still rising at an end of the search range, so `lambda` is that limit), and
166
+ Exported functions: `mean`, `std_dev`, `variance`, `median`, `quantiles(data, probabilities) -> Float64Array` (sample quantiles in the order asked, R-7 linear interpolation — the default of R, NumPy and Excel — with one sort for all of them), `normal_cdf`, `normal_sf` (upper tail `P(Z > x)`, computed directly so tail probabilities keep ~15 significant digits), `box_cox`, `estimate_lambda` (returns `{ lambda, at_bound }` — `at_bound` is `true` when the likelihood was still rising at an end of the search range, so `lambda` is that limit), and
141
167
  `rfft(data) -> Float64Array` — the DFT of a real sequence of any length, interleaved as
142
168
  `[re0, im0, re1, im1, …]` (bins `k` and `n − k` are conjugates, so `0..=n/2` describes the spectrum).
143
169
 
@@ -146,6 +172,8 @@ element that is not a number (`null`, `undefined`, a string) throws `malformed_i
146
172
  and a NaN or ±Infinity throws `value_not_finite`, each with the element's `index` —
147
173
  `mean([1, null, 3])` throws rather than averaging the `null` as 0. `mean` of no values
148
174
  throws `empty_input`; `std_dev` and `variance` of fewer than 2 throw `insufficient_data`.
175
+ `median` and `quantiles` of no values throw `empty_input`; a probability outside `[0, 1]`
176
+ throws `parameter_out_of_range` with `parameter` `probabilities[i]`, `min` 0 and `max` 1.
149
177
 
150
178
  Distribution functions for critical values and p-values — each returns a `number` and
151
179
  **throws** (an `Error` naming the argument — see *Errors* below) when an argument is outside the domain,
@@ -210,6 +238,35 @@ const up = interval_difference([[0, 168]], down); // [[30, 168]]
210
238
  console.log(interval_measure(up)); // 138
211
239
  ```
212
240
 
241
+ Units of measure — `from`, `to` and `unit` are unit expressions (UCUM syntax, see the
242
+ Rust section above) over the SI and the optional `definitions`, an array of
243
+ `UnitDefinitionSpec` `{ symbol, unit, factor?, offset? }` (`symbol` = `factor` × `unit`,
244
+ plus `offset` SI units at its zero; `factor` defaults to 1, `offset` to 0; a definition
245
+ may use the ones before it):
246
+
247
+ | Function | Returns |
248
+ |---|---|
249
+ | `unit_convert(value, from, to, definitions?)` | A reading converted — 20 `Cel` is 293.15 `K` |
250
+ | `unit_convert_difference(value, from, to, definitions?)` | An amount of change converted — the offset cancels: a rise of 20 `Cel` is a rise of 20 `K` |
251
+ | `unit_info(unit, definitions?)` | `UnitInfo` `{ dimension, exponents, scale, offset }` — the dimension in SI base units (`"kg.m2.s-2"`; exponents of m, kg, s, A, K, mol, cd) and `value_in_si = value · scale + offset`. Two units convert into each other exactly when their dimensions are equal |
252
+
253
+ ```js
254
+ const { unit_convert, unit_convert_difference, unit_info } = require("@iyulab/u-numflow");
255
+ const energy = [
256
+ { symbol: "cal_IT", unit: "J", factor: 4.1868 },
257
+ { symbol: "toe", unit: "kcal_IT", factor: 1e7 },
258
+ ];
259
+ console.log(unit_convert(1, "Gcal_IT", "GJ", energy)); // 4.1868
260
+ console.log(unit_convert(20, "Cel", "K")); // 293.15
261
+ console.log(unit_convert_difference(20, "Cel", "K")); // 20
262
+ console.log(unit_info("toe", energy).dimension); // "kg.m2.s-2"
263
+ try {
264
+ unit_convert(1, "m3", "kg");
265
+ } catch (err) {
266
+ console.log(err.code, err.parameter, err.expected, err.got); // dimension_mismatch to m3 kg
267
+ }
268
+ ```
269
+
213
270
  `codes()` returns every refusal `code` the functions throw, with the fields that
214
271
  can come with it — `{ refusals: [{ code, fields }], violations: [] }` — built from
215
272
  the table the refusals come from; the table below explains each. The C library
@@ -231,11 +288,15 @@ try {
231
288
 
232
289
  | `code` | Fields | Meaning |
233
290
  |---|---|---|
234
- | `parameter_out_of_range` | `parameter`, `min`, `max`, `got`; `exclusive_min` / `exclusive_max` on an open end | `p` not strictly inside (0, 1) (`exclusive_min` 0, `exclusive_max` 1), or a degrees of freedom that is not a finite number `> 0` (`exclusive_min` 0); a distribution parameter that must be `> 0` (`exclusive_min` 0; `parameter` is its path, e.g. `distribution.sigma`), or that falls outside the range the others leave it: `distribution.max` not above `min` (`exclusive_min` = `min`), `distribution.mode` outside `[min, max]` (`min`, `max`); `estimate_lambda`'s `lambda_min` not below `lambda_max` (`exclusive_max` = `lambda_max`); a whole `n` or `seed` outside its range, a negative one included (`min` 0, `max`) |
291
+ | `parameter_out_of_range` | `parameter`, `min`, `max`, `got`; `exclusive_min` / `exclusive_max` on an open end | `p` not strictly inside (0, 1) (`exclusive_min` 0, `exclusive_max` 1), or a degrees of freedom that is not a finite number `> 0` (`exclusive_min` 0); a distribution parameter that must be `> 0` (`exclusive_min` 0; `parameter` is its path, e.g. `distribution.sigma`), or that falls outside the range the others leave it: `distribution.max` not above `min` (`exclusive_min` = `min`), `distribution.mode` outside `[min, max]` (`min`, `max`); `estimate_lambda`'s `lambda_min` not below `lambda_max` (`exclusive_max` = `lambda_max`); a whole `n` or `seed` outside its range, a negative one included (`min` 0, `max`); a unit definition's `factor` not `> 0` (`parameter` `definitions[i].factor`, `exclusive_min` 0) |
235
292
  | `unknown_option` | `parameter`, `got`, `expected` | A `distribution.kind` the crate does not have |
236
- | `malformed_input` | `parameter`, `index` (or absent) | A `data` argument that is not an array or `Float64Array`, or an element that is not a number; a `distribution` that is not an object with a string `kind`, lacks a parameter or has one its kind does not take; an interval row that is not two numbers (`parameter` is its path, e.g. `a[2]`); an `n` or `seed` with a fraction ("must be a whole number") |
293
+ | `malformed_input` | `parameter`, `index` (or absent) | A `data` argument that is not an array or `Float64Array`, or an element that is not a number; a `distribution` that is not an object with a string `kind`, lacks a parameter or has one its kind does not take; an interval row that is not two numbers (`parameter` is its path, e.g. `a[2]`); an `n` or `seed` with a fraction ("must be a whole number"); a unit expression that does not parse (`from`, `to`, `unit`, `definitions[i].unit`), a definition symbol that is empty, starts with a digit or holds an operator character (`definitions[i].symbol`), or a definition of the wrong shape (`definitions[i]`) |
294
+ | `unknown_unit` | `parameter`, `got` | A unit symbol that is neither built in nor defined, nor a prefix on one — `got` is the symbol (`hr` in `kW.hr`) |
295
+ | `dimension_mismatch` | `parameter`, `expected`, `got` | `unit_convert` between units of different dimensions — `parameter` is `to`, `expected` the dimension of `from` and `got` that of `to`, both in SI base units (`m3`, `kg`) |
296
+ | `invalid_option` | `parameter` | A scale with an offset (`Cel`, or a defined one) given a prefix, an exponent or a product — it has a meaning only alone |
297
+ | `duplicate_id` | `parameter`, `id`, `index` | A unit definition whose `symbol` is already a unit (built in or defined before) — `index` is the definition's position |
237
298
  | `reversed_interval` | `parameter`, `index` | An interval row with start > end — refused, not swapped |
238
- | `value_not_finite` | `parameter`, `index` for an array element | A NaN argument, an infinite `n`, `seed`, `lambda_min` or `lambda_max`, or a NaN or infinity in any `data` array or interval row (`parameter` is the row's path) |
299
+ | `value_not_finite` | `parameter`, `index` for an array element | A NaN argument, an infinite `n`, `seed`, `lambda_min` or `lambda_max`, or a NaN or infinity in any `data` array or interval row (`parameter` is the row's path); a NaN or infinite `value` to convert or definition `factor` / `offset` |
239
300
  | `empty_input` | `parameter` | `mean` of no values |
240
301
  | `non_positive_data` | `parameter`, `index`, `got` | Box-Cox data with a value `≤ 0` — `index` is the first such value |
241
302
  | `insufficient_data` | `parameter`, `min`, `got` | Fewer values than the function needs (`std_dev`/`variance` 2, Box-Cox 2) |
@@ -362,6 +362,23 @@ function mean(data) {
362
362
  }
363
363
  exports.mean = mean;
364
364
 
365
+ /**
366
+ * Median of the sample `data` (even length: the mean of the middle two).
367
+ *
368
+ * # Errors
369
+ * Throws `empty_input` when `data` is empty.
370
+ * @param {number[] | Float64Array} data
371
+ * @returns {number}
372
+ */
373
+ function median(data) {
374
+ const ret = wasm.median(data);
375
+ if (ret[2]) {
376
+ throw takeFromExternrefTable0(ret[1]);
377
+ }
378
+ return ret[0];
379
+ }
380
+ exports.median = median;
381
+
365
382
  /**
366
383
  * Standard normal CDF Φ(x) = P(Z ≤ x) for Z ~ N(0, 1).
367
384
  *
@@ -393,6 +410,30 @@ function normal_sf(x) {
393
410
  }
394
411
  exports.normal_sf = normal_sf;
395
412
 
413
+ /**
414
+ * Sample quantiles of `data` at each of `probabilities`, in the order asked
415
+ * — R-7 linear interpolation (Hyndman & Fan 1996; the default of R, NumPy
416
+ * and Excel), sorting the sample once.
417
+ *
418
+ * # Errors
419
+ * Throws `empty_input` when `data` is empty, and `parameter_out_of_range`
420
+ * (`parameter` `probabilities[i]`, `min` 0, `max` 1) for a probability
421
+ * outside `[0, 1]`.
422
+ * @param {number[] | Float64Array} data
423
+ * @param {number[] | Float64Array} probabilities
424
+ * @returns {Float64Array}
425
+ */
426
+ function quantiles(data, probabilities) {
427
+ const ret = wasm.quantiles(data, probabilities);
428
+ if (ret[3]) {
429
+ throw takeFromExternrefTable0(ret[2]);
430
+ }
431
+ var v1 = getArrayF64FromWasm0(ret[0], ret[1]).slice();
432
+ wasm.__wbindgen_free(ret[0], ret[1] * 8, 8);
433
+ return v1;
434
+ }
435
+ exports.quantiles = quantiles;
436
+
396
437
  /**
397
438
  * Forward DFT of a real sequence of any length.
398
439
  *
@@ -472,6 +513,89 @@ function t_distribution_quantile(p, df) {
472
513
  }
473
514
  exports.t_distribution_quantile = t_distribution_quantile;
474
515
 
516
+ /**
517
+ * A reading `value` in the unit `from`, in the unit `to`: 20 `Cel` is
518
+ * 293.15 `K`. Units are UCUM expressions (`kW.h`, `m/s2`, `J/(kg.K)`) over
519
+ * the SI and the `definitions` given — e.g.
520
+ * `[{ symbol: "cal_IT", unit: "J", factor: 4.1868 }]`.
521
+ *
522
+ * # Errors
523
+ * `value_not_finite` (`parameter` `value`); `malformed_input` for an
524
+ * expression that does not parse or a definition of the wrong shape;
525
+ * `unknown_unit` (`parameter`, `got` = the symbol); `invalid_option` for a
526
+ * scale such as `Cel` with a prefix, exponent or product;
527
+ * `dimension_mismatch` (`parameter` `to`, `expected` = the dimension of
528
+ * `from`, `got` = that of `to`, both in SI base units); for a definition,
529
+ * `duplicate_id` (`id`, `index`) for a symbol already a unit and
530
+ * `parameter_out_of_range` for a `factor` not `> 0`, each at
531
+ * `definitions[i].…`.
532
+ * @param {number} value
533
+ * @param {string} from
534
+ * @param {string} to
535
+ * @param {UnitDefinitionSpec[] | null} [definitions]
536
+ * @returns {number}
537
+ */
538
+ function unit_convert(value, from, to, definitions) {
539
+ const ptr0 = passStringToWasm0(from, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
540
+ const len0 = WASM_VECTOR_LEN;
541
+ const ptr1 = passStringToWasm0(to, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
542
+ const len1 = WASM_VECTOR_LEN;
543
+ const ret = wasm.unit_convert(value, ptr0, len0, ptr1, len1, isLikeNone(definitions) ? 0 : addToExternrefTable0(definitions));
544
+ if (ret[2]) {
545
+ throw takeFromExternrefTable0(ret[1]);
546
+ }
547
+ return ret[0];
548
+ }
549
+ exports.unit_convert = unit_convert;
550
+
551
+ /**
552
+ * An amount of change `value` in `from`, in `to`: the offset of a scale
553
+ * cancels, so a rise of 20 `Cel` is a rise of 20 `K`.
554
+ *
555
+ * # Errors
556
+ * As [`unit_convert`].
557
+ * @param {number} value
558
+ * @param {string} from
559
+ * @param {string} to
560
+ * @param {UnitDefinitionSpec[] | null} [definitions]
561
+ * @returns {number}
562
+ */
563
+ function unit_convert_difference(value, from, to, definitions) {
564
+ const ptr0 = passStringToWasm0(from, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
565
+ const len0 = WASM_VECTOR_LEN;
566
+ const ptr1 = passStringToWasm0(to, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
567
+ const len1 = WASM_VECTOR_LEN;
568
+ const ret = wasm.unit_convert_difference(value, ptr0, len0, ptr1, len1, isLikeNone(definitions) ? 0 : addToExternrefTable0(definitions));
569
+ if (ret[2]) {
570
+ throw takeFromExternrefTable0(ret[1]);
571
+ }
572
+ return ret[0];
573
+ }
574
+ exports.unit_convert_difference = unit_convert_difference;
575
+
576
+ /**
577
+ * What the unit expression `unit` stands for: its dimension in SI base units
578
+ * (`"kg.m2.s-2"`, with the exponents of m, kg, s, A, K, mol, cd), and
579
+ * `scale` / `offset` with `value_in_si = value · scale + offset`. Two units
580
+ * convert into each other exactly when their dimensions are equal.
581
+ *
582
+ * # Errors
583
+ * As [`unit_convert`] for `unit` (at `parameter` `unit`) and `definitions`.
584
+ * @param {string} unit
585
+ * @param {UnitDefinitionSpec[] | null} [definitions]
586
+ * @returns {UnitInfo}
587
+ */
588
+ function unit_info(unit, definitions) {
589
+ const ptr0 = passStringToWasm0(unit, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
590
+ const len0 = WASM_VECTOR_LEN;
591
+ const ret = wasm.unit_info(ptr0, len0, isLikeNone(definitions) ? 0 : addToExternrefTable0(definitions));
592
+ if (ret[2]) {
593
+ throw takeFromExternrefTable0(ret[1]);
594
+ }
595
+ return takeFromExternrefTable0(ret[0]);
596
+ }
597
+ exports.unit_info = unit_info;
598
+
475
599
  /**
476
600
  * Sample variance of `data` (Bessel-corrected, denominator n−1).
477
601
  *
@@ -541,6 +665,10 @@ function __wbg_get_imports() {
541
665
  const ret = typeof(val) === 'object' && val !== null;
542
666
  return ret;
543
667
  },
668
+ __wbg___wbindgen_is_undefined_8865fb403f8fe9d8: function(arg0) {
669
+ const ret = arg0 === undefined;
670
+ return ret;
671
+ },
544
672
  __wbg___wbindgen_jsval_eq_02babf21faa37971: function(arg0, arg1) {
545
673
  const ret = arg0 === arg1;
546
674
  return ret;
@@ -598,6 +726,10 @@ function __wbg_get_imports() {
598
726
  const ret = arg0[arg1 >>> 0];
599
727
  return ret;
600
728
  },
729
+ __wbg_get_with_ref_key_6412cf3094599694: function(arg0, arg1) {
730
+ const ret = arg0[arg1];
731
+ return ret;
732
+ },
601
733
  __wbg_instanceof_ArrayBuffer_a99f175873e5d9b8: function(arg0) {
602
734
  let result;
603
735
  try {
@@ -14,6 +14,29 @@ export interface RefusalCode {
14
14
  fields: string[];
15
15
  }
16
16
 
17
+ /**
18
+ * A unit the caller defines: `symbol` = `factor` × `unit` (+ `offset` SI
19
+ * units at its zero, for a scale such as a Fahrenheit degree).
20
+ */
21
+ export interface UnitDefinitionSpec {
22
+ /**
23
+ * The new symbol, e.g. `cal_IT`.
24
+ */
25
+ symbol: string;
26
+ /**
27
+ * What it is a multiple of, as a unit expression, e.g. `J`.
28
+ */
29
+ unit: string;
30
+ /**
31
+ * How many `unit` one `symbol` is (default 1, `> 0`).
32
+ */
33
+ factor?: number;
34
+ /**
35
+ * SI value at the symbol's zero (default 0).
36
+ */
37
+ offset?: number;
38
+ }
39
+
17
40
  /**
18
41
  * A violation a checker reports. This package has no checker; the type is
19
42
  * here so every engine's [`Codes`] has one shape.
@@ -40,6 +63,28 @@ export interface LambdaEstimateDto {
40
63
  at_bound: boolean;
41
64
  }
42
65
 
66
+ /**
67
+ * What a unit expression stands for.
68
+ */
69
+ export interface UnitInfo {
70
+ /**
71
+ * The dimension in SI base units, e.g. `kg.m2.s-2` (`1` for a pure number).
72
+ */
73
+ dimension: string;
74
+ /**
75
+ * Exponents of m, kg, s, A, K, mol, cd.
76
+ */
77
+ exponents: [number, number, number, number, number, number, number];
78
+ /**
79
+ * Size of one unit in coherent SI units.
80
+ */
81
+ scale: number;
82
+ /**
83
+ * SI value of the unit's zero (0 except for scales such as `Cel`).
84
+ */
85
+ offset: number;
86
+ }
87
+
43
88
  /**
44
89
  * What this package can answer with besides a result: every refusal
45
90
  * `code`, and no violations -- it has no checker.
@@ -231,6 +276,14 @@ export function inverse_normal_cdf(p: number): number;
231
276
  */
232
277
  export function mean(data: number[] | Float64Array): number;
233
278
 
279
+ /**
280
+ * Median of the sample `data` (even length: the mean of the middle two).
281
+ *
282
+ * # Errors
283
+ * Throws `empty_input` when `data` is empty.
284
+ */
285
+ export function median(data: number[] | Float64Array): number;
286
+
234
287
  /**
235
288
  * Standard normal CDF Φ(x) = P(Z ≤ x) for Z ~ N(0, 1).
236
289
  *
@@ -250,6 +303,18 @@ export function normal_cdf(x: number): number;
250
303
  */
251
304
  export function normal_sf(x: number): number;
252
305
 
306
+ /**
307
+ * Sample quantiles of `data` at each of `probabilities`, in the order asked
308
+ * — R-7 linear interpolation (Hyndman & Fan 1996; the default of R, NumPy
309
+ * and Excel), sorting the sample once.
310
+ *
311
+ * # Errors
312
+ * Throws `empty_input` when `data` is empty, and `parameter_out_of_range`
313
+ * (`parameter` `probabilities[i]`, `min` 0, `max` 1) for a probability
314
+ * outside `[0, 1]`.
315
+ */
316
+ export function quantiles(data: number[] | Float64Array, probabilities: number[] | Float64Array): Float64Array;
317
+
253
318
  /**
254
319
  * Forward DFT of a real sequence of any length.
255
320
  *
@@ -289,6 +354,45 @@ export function t_distribution_cdf(t: number, df: number): number;
289
354
  */
290
355
  export function t_distribution_quantile(p: number, df: number): number;
291
356
 
357
+ /**
358
+ * A reading `value` in the unit `from`, in the unit `to`: 20 `Cel` is
359
+ * 293.15 `K`. Units are UCUM expressions (`kW.h`, `m/s2`, `J/(kg.K)`) over
360
+ * the SI and the `definitions` given — e.g.
361
+ * `[{ symbol: "cal_IT", unit: "J", factor: 4.1868 }]`.
362
+ *
363
+ * # Errors
364
+ * `value_not_finite` (`parameter` `value`); `malformed_input` for an
365
+ * expression that does not parse or a definition of the wrong shape;
366
+ * `unknown_unit` (`parameter`, `got` = the symbol); `invalid_option` for a
367
+ * scale such as `Cel` with a prefix, exponent or product;
368
+ * `dimension_mismatch` (`parameter` `to`, `expected` = the dimension of
369
+ * `from`, `got` = that of `to`, both in SI base units); for a definition,
370
+ * `duplicate_id` (`id`, `index`) for a symbol already a unit and
371
+ * `parameter_out_of_range` for a `factor` not `> 0`, each at
372
+ * `definitions[i].…`.
373
+ */
374
+ export function unit_convert(value: number, from: string, to: string, definitions?: UnitDefinitionSpec[] | null): number;
375
+
376
+ /**
377
+ * An amount of change `value` in `from`, in `to`: the offset of a scale
378
+ * cancels, so a rise of 20 `Cel` is a rise of 20 `K`.
379
+ *
380
+ * # Errors
381
+ * As [`unit_convert`].
382
+ */
383
+ export function unit_convert_difference(value: number, from: string, to: string, definitions?: UnitDefinitionSpec[] | null): number;
384
+
385
+ /**
386
+ * What the unit expression `unit` stands for: its dimension in SI base units
387
+ * (`"kg.m2.s-2"`, with the exponents of m, kg, s, A, K, mol, cd), and
388
+ * `scale` / `offset` with `value_in_si = value · scale + offset`. Two units
389
+ * convert into each other exactly when their dimensions are equal.
390
+ *
391
+ * # Errors
392
+ * As [`unit_convert`] for `unit` (at `parameter` `unit`) and `definitions`.
393
+ */
394
+ export function unit_info(unit: string, definitions?: UnitDefinitionSpec[] | null): UnitInfo;
395
+
292
396
  /**
293
397
  * Sample variance of `data` (Bessel-corrected, denominator n−1).
294
398
  *
Binary file
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "iyulab"
6
6
  ],
7
7
  "description": "Foundational mathematical primitives: statistics, probability distributions, random sampling, and collections.",
8
- "version": "0.13.1",
8
+ "version": "0.14.1",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",
package/u_numflow.d.ts CHANGED
@@ -14,6 +14,29 @@ export interface RefusalCode {
14
14
  fields: string[];
15
15
  }
16
16
 
17
+ /**
18
+ * A unit the caller defines: `symbol` = `factor` × `unit` (+ `offset` SI
19
+ * units at its zero, for a scale such as a Fahrenheit degree).
20
+ */
21
+ export interface UnitDefinitionSpec {
22
+ /**
23
+ * The new symbol, e.g. `cal_IT`.
24
+ */
25
+ symbol: string;
26
+ /**
27
+ * What it is a multiple of, as a unit expression, e.g. `J`.
28
+ */
29
+ unit: string;
30
+ /**
31
+ * How many `unit` one `symbol` is (default 1, `> 0`).
32
+ */
33
+ factor?: number;
34
+ /**
35
+ * SI value at the symbol's zero (default 0).
36
+ */
37
+ offset?: number;
38
+ }
39
+
17
40
  /**
18
41
  * A violation a checker reports. This package has no checker; the type is
19
42
  * here so every engine's [`Codes`] has one shape.
@@ -40,6 +63,28 @@ export interface LambdaEstimateDto {
40
63
  at_bound: boolean;
41
64
  }
42
65
 
66
+ /**
67
+ * What a unit expression stands for.
68
+ */
69
+ export interface UnitInfo {
70
+ /**
71
+ * The dimension in SI base units, e.g. `kg.m2.s-2` (`1` for a pure number).
72
+ */
73
+ dimension: string;
74
+ /**
75
+ * Exponents of m, kg, s, A, K, mol, cd.
76
+ */
77
+ exponents: [number, number, number, number, number, number, number];
78
+ /**
79
+ * Size of one unit in coherent SI units.
80
+ */
81
+ scale: number;
82
+ /**
83
+ * SI value of the unit's zero (0 except for scales such as `Cel`).
84
+ */
85
+ offset: number;
86
+ }
87
+
43
88
  /**
44
89
  * What this package can answer with besides a result: every refusal
45
90
  * `code`, and no violations -- it has no checker.
@@ -231,6 +276,14 @@ export function inverse_normal_cdf(p: number): number;
231
276
  */
232
277
  export function mean(data: number[] | Float64Array): number;
233
278
 
279
+ /**
280
+ * Median of the sample `data` (even length: the mean of the middle two).
281
+ *
282
+ * # Errors
283
+ * Throws `empty_input` when `data` is empty.
284
+ */
285
+ export function median(data: number[] | Float64Array): number;
286
+
234
287
  /**
235
288
  * Standard normal CDF Φ(x) = P(Z ≤ x) for Z ~ N(0, 1).
236
289
  *
@@ -250,6 +303,18 @@ export function normal_cdf(x: number): number;
250
303
  */
251
304
  export function normal_sf(x: number): number;
252
305
 
306
+ /**
307
+ * Sample quantiles of `data` at each of `probabilities`, in the order asked
308
+ * — R-7 linear interpolation (Hyndman & Fan 1996; the default of R, NumPy
309
+ * and Excel), sorting the sample once.
310
+ *
311
+ * # Errors
312
+ * Throws `empty_input` when `data` is empty, and `parameter_out_of_range`
313
+ * (`parameter` `probabilities[i]`, `min` 0, `max` 1) for a probability
314
+ * outside `[0, 1]`.
315
+ */
316
+ export function quantiles(data: number[] | Float64Array, probabilities: number[] | Float64Array): Float64Array;
317
+
253
318
  /**
254
319
  * Forward DFT of a real sequence of any length.
255
320
  *
@@ -289,6 +354,45 @@ export function t_distribution_cdf(t: number, df: number): number;
289
354
  */
290
355
  export function t_distribution_quantile(p: number, df: number): number;
291
356
 
357
+ /**
358
+ * A reading `value` in the unit `from`, in the unit `to`: 20 `Cel` is
359
+ * 293.15 `K`. Units are UCUM expressions (`kW.h`, `m/s2`, `J/(kg.K)`) over
360
+ * the SI and the `definitions` given — e.g.
361
+ * `[{ symbol: "cal_IT", unit: "J", factor: 4.1868 }]`.
362
+ *
363
+ * # Errors
364
+ * `value_not_finite` (`parameter` `value`); `malformed_input` for an
365
+ * expression that does not parse or a definition of the wrong shape;
366
+ * `unknown_unit` (`parameter`, `got` = the symbol); `invalid_option` for a
367
+ * scale such as `Cel` with a prefix, exponent or product;
368
+ * `dimension_mismatch` (`parameter` `to`, `expected` = the dimension of
369
+ * `from`, `got` = that of `to`, both in SI base units); for a definition,
370
+ * `duplicate_id` (`id`, `index`) for a symbol already a unit and
371
+ * `parameter_out_of_range` for a `factor` not `> 0`, each at
372
+ * `definitions[i].…`.
373
+ */
374
+ export function unit_convert(value: number, from: string, to: string, definitions?: UnitDefinitionSpec[] | null): number;
375
+
376
+ /**
377
+ * An amount of change `value` in `from`, in `to`: the offset of a scale
378
+ * cancels, so a rise of 20 `Cel` is a rise of 20 `K`.
379
+ *
380
+ * # Errors
381
+ * As [`unit_convert`].
382
+ */
383
+ export function unit_convert_difference(value: number, from: string, to: string, definitions?: UnitDefinitionSpec[] | null): number;
384
+
385
+ /**
386
+ * What the unit expression `unit` stands for: its dimension in SI base units
387
+ * (`"kg.m2.s-2"`, with the exponents of m, kg, s, A, K, mol, cd), and
388
+ * `scale` / `offset` with `value_in_si = value · scale + offset`. Two units
389
+ * convert into each other exactly when their dimensions are equal.
390
+ *
391
+ * # Errors
392
+ * As [`unit_convert`] for `unit` (at `parameter` `unit`) and `definitions`.
393
+ */
394
+ export function unit_info(unit: string, definitions?: UnitDefinitionSpec[] | null): UnitInfo;
395
+
292
396
  /**
293
397
  * Sample variance of `data` (Bessel-corrected, denominator n−1).
294
398
  *
package/u_numflow.js CHANGED
@@ -5,5 +5,5 @@ import { __wbg_set_wasm } from "./u_numflow_bg.js";
5
5
  __wbg_set_wasm(wasm);
6
6
  wasm.__wbindgen_start();
7
7
  export {
8
- box_cox, chi_squared_cdf, chi_squared_quantile, codes, distribution_cdf, distribution_moments, distribution_quantile, distribution_sample, estimate_lambda, f_distribution_cdf, f_distribution_quantile, interval_difference, interval_intersection, interval_measure, interval_normalize, interval_union, inverse_normal_cdf, mean, normal_cdf, normal_sf, rfft, std_dev, t_distribution_cdf, t_distribution_quantile, variance
8
+ box_cox, chi_squared_cdf, chi_squared_quantile, codes, distribution_cdf, distribution_moments, distribution_quantile, distribution_sample, estimate_lambda, f_distribution_cdf, f_distribution_quantile, interval_difference, interval_intersection, interval_measure, interval_normalize, interval_union, inverse_normal_cdf, mean, median, normal_cdf, normal_sf, quantiles, rfft, std_dev, t_distribution_cdf, t_distribution_quantile, unit_convert, unit_convert_difference, unit_info, variance
9
9
  } from "./u_numflow_bg.js";
package/u_numflow_bg.js CHANGED
@@ -342,6 +342,22 @@ export function mean(data) {
342
342
  return ret[0];
343
343
  }
344
344
 
345
+ /**
346
+ * Median of the sample `data` (even length: the mean of the middle two).
347
+ *
348
+ * # Errors
349
+ * Throws `empty_input` when `data` is empty.
350
+ * @param {number[] | Float64Array} data
351
+ * @returns {number}
352
+ */
353
+ export function median(data) {
354
+ const ret = wasm.median(data);
355
+ if (ret[2]) {
356
+ throw takeFromExternrefTable0(ret[1]);
357
+ }
358
+ return ret[0];
359
+ }
360
+
345
361
  /**
346
362
  * Standard normal CDF Φ(x) = P(Z ≤ x) for Z ~ N(0, 1).
347
363
  *
@@ -371,6 +387,29 @@ export function normal_sf(x) {
371
387
  return ret;
372
388
  }
373
389
 
390
+ /**
391
+ * Sample quantiles of `data` at each of `probabilities`, in the order asked
392
+ * — R-7 linear interpolation (Hyndman & Fan 1996; the default of R, NumPy
393
+ * and Excel), sorting the sample once.
394
+ *
395
+ * # Errors
396
+ * Throws `empty_input` when `data` is empty, and `parameter_out_of_range`
397
+ * (`parameter` `probabilities[i]`, `min` 0, `max` 1) for a probability
398
+ * outside `[0, 1]`.
399
+ * @param {number[] | Float64Array} data
400
+ * @param {number[] | Float64Array} probabilities
401
+ * @returns {Float64Array}
402
+ */
403
+ export function quantiles(data, probabilities) {
404
+ const ret = wasm.quantiles(data, probabilities);
405
+ if (ret[3]) {
406
+ throw takeFromExternrefTable0(ret[2]);
407
+ }
408
+ var v1 = getArrayF64FromWasm0(ret[0], ret[1]).slice();
409
+ wasm.__wbindgen_free(ret[0], ret[1] * 8, 8);
410
+ return v1;
411
+ }
412
+
374
413
  /**
375
414
  * Forward DFT of a real sequence of any length.
376
415
  *
@@ -446,6 +485,86 @@ export function t_distribution_quantile(p, df) {
446
485
  return ret[0];
447
486
  }
448
487
 
488
+ /**
489
+ * A reading `value` in the unit `from`, in the unit `to`: 20 `Cel` is
490
+ * 293.15 `K`. Units are UCUM expressions (`kW.h`, `m/s2`, `J/(kg.K)`) over
491
+ * the SI and the `definitions` given — e.g.
492
+ * `[{ symbol: "cal_IT", unit: "J", factor: 4.1868 }]`.
493
+ *
494
+ * # Errors
495
+ * `value_not_finite` (`parameter` `value`); `malformed_input` for an
496
+ * expression that does not parse or a definition of the wrong shape;
497
+ * `unknown_unit` (`parameter`, `got` = the symbol); `invalid_option` for a
498
+ * scale such as `Cel` with a prefix, exponent or product;
499
+ * `dimension_mismatch` (`parameter` `to`, `expected` = the dimension of
500
+ * `from`, `got` = that of `to`, both in SI base units); for a definition,
501
+ * `duplicate_id` (`id`, `index`) for a symbol already a unit and
502
+ * `parameter_out_of_range` for a `factor` not `> 0`, each at
503
+ * `definitions[i].…`.
504
+ * @param {number} value
505
+ * @param {string} from
506
+ * @param {string} to
507
+ * @param {UnitDefinitionSpec[] | null} [definitions]
508
+ * @returns {number}
509
+ */
510
+ export function unit_convert(value, from, to, definitions) {
511
+ const ptr0 = passStringToWasm0(from, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
512
+ const len0 = WASM_VECTOR_LEN;
513
+ const ptr1 = passStringToWasm0(to, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
514
+ const len1 = WASM_VECTOR_LEN;
515
+ const ret = wasm.unit_convert(value, ptr0, len0, ptr1, len1, isLikeNone(definitions) ? 0 : addToExternrefTable0(definitions));
516
+ if (ret[2]) {
517
+ throw takeFromExternrefTable0(ret[1]);
518
+ }
519
+ return ret[0];
520
+ }
521
+
522
+ /**
523
+ * An amount of change `value` in `from`, in `to`: the offset of a scale
524
+ * cancels, so a rise of 20 `Cel` is a rise of 20 `K`.
525
+ *
526
+ * # Errors
527
+ * As [`unit_convert`].
528
+ * @param {number} value
529
+ * @param {string} from
530
+ * @param {string} to
531
+ * @param {UnitDefinitionSpec[] | null} [definitions]
532
+ * @returns {number}
533
+ */
534
+ export function unit_convert_difference(value, from, to, definitions) {
535
+ const ptr0 = passStringToWasm0(from, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
536
+ const len0 = WASM_VECTOR_LEN;
537
+ const ptr1 = passStringToWasm0(to, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
538
+ const len1 = WASM_VECTOR_LEN;
539
+ const ret = wasm.unit_convert_difference(value, ptr0, len0, ptr1, len1, isLikeNone(definitions) ? 0 : addToExternrefTable0(definitions));
540
+ if (ret[2]) {
541
+ throw takeFromExternrefTable0(ret[1]);
542
+ }
543
+ return ret[0];
544
+ }
545
+
546
+ /**
547
+ * What the unit expression `unit` stands for: its dimension in SI base units
548
+ * (`"kg.m2.s-2"`, with the exponents of m, kg, s, A, K, mol, cd), and
549
+ * `scale` / `offset` with `value_in_si = value · scale + offset`. Two units
550
+ * convert into each other exactly when their dimensions are equal.
551
+ *
552
+ * # Errors
553
+ * As [`unit_convert`] for `unit` (at `parameter` `unit`) and `definitions`.
554
+ * @param {string} unit
555
+ * @param {UnitDefinitionSpec[] | null} [definitions]
556
+ * @returns {UnitInfo}
557
+ */
558
+ export function unit_info(unit, definitions) {
559
+ const ptr0 = passStringToWasm0(unit, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
560
+ const len0 = WASM_VECTOR_LEN;
561
+ const ret = wasm.unit_info(ptr0, len0, isLikeNone(definitions) ? 0 : addToExternrefTable0(definitions));
562
+ if (ret[2]) {
563
+ throw takeFromExternrefTable0(ret[1]);
564
+ }
565
+ return takeFromExternrefTable0(ret[0]);
566
+ }
567
+
449
568
  /**
450
569
  * Sample variance of `data` (Bessel-corrected, denominator n−1).
451
570
  *
@@ -511,6 +630,10 @@ export function __wbg___wbindgen_is_object_3c45d4f2dde4e749(arg0) {
511
630
  const ret = typeof(val) === 'object' && val !== null;
512
631
  return ret;
513
632
  }
633
+ export function __wbg___wbindgen_is_undefined_8865fb403f8fe9d8(arg0) {
634
+ const ret = arg0 === undefined;
635
+ return ret;
636
+ }
514
637
  export function __wbg___wbindgen_jsval_eq_02babf21faa37971(arg0, arg1) {
515
638
  const ret = arg0 === arg1;
516
639
  return ret;
@@ -568,6 +691,10 @@ export function __wbg_get_unchecked_288889d017702237(arg0, arg1) {
568
691
  const ret = arg0[arg1 >>> 0];
569
692
  return ret;
570
693
  }
694
+ export function __wbg_get_with_ref_key_6412cf3094599694(arg0, arg1) {
695
+ const ret = arg0[arg1];
696
+ return ret;
697
+ }
571
698
  export function __wbg_instanceof_ArrayBuffer_a99f175873e5d9b8(arg0) {
572
699
  let result;
573
700
  try {
package/u_numflow_bg.wasm CHANGED
Binary file