@danielsimonjr/mathts-core 0.1.2 → 0.1.3
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 +12 -10
- package/dist/index.d.ts +340 -2
- package/dist/index.js +812 -44
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -757,10 +757,7 @@ var Fraction = class _Fraction {
|
|
|
757
757
|
*/
|
|
758
758
|
multiply(other) {
|
|
759
759
|
if (other instanceof _Fraction) {
|
|
760
|
-
return new _Fraction(
|
|
761
|
-
this.numerator * other.numerator,
|
|
762
|
-
this.denominator * other.denominator
|
|
763
|
-
);
|
|
760
|
+
return new _Fraction(this.numerator * other.numerator, this.denominator * other.denominator);
|
|
764
761
|
}
|
|
765
762
|
return this.multiply(_Fraction.fromNumber(Number(other.valueOf())));
|
|
766
763
|
}
|
|
@@ -772,10 +769,7 @@ var Fraction = class _Fraction {
|
|
|
772
769
|
if (other.numerator === 0n) {
|
|
773
770
|
throw new Error("Division by zero");
|
|
774
771
|
}
|
|
775
|
-
return new _Fraction(
|
|
776
|
-
this.numerator * other.denominator,
|
|
777
|
-
this.denominator * other.numerator
|
|
778
|
-
);
|
|
772
|
+
return new _Fraction(this.numerator * other.denominator, this.denominator * other.numerator);
|
|
779
773
|
}
|
|
780
774
|
return this.divide(_Fraction.fromNumber(Number(other.valueOf())));
|
|
781
775
|
}
|
|
@@ -789,10 +783,7 @@ var Fraction = class _Fraction {
|
|
|
789
783
|
* Absolute value: |a/b|
|
|
790
784
|
*/
|
|
791
785
|
abs() {
|
|
792
|
-
return new _Fraction(
|
|
793
|
-
this.numerator < 0n ? -this.numerator : this.numerator,
|
|
794
|
-
this.denominator
|
|
795
|
-
);
|
|
786
|
+
return new _Fraction(this.numerator < 0n ? -this.numerator : this.numerator, this.denominator);
|
|
796
787
|
}
|
|
797
788
|
/**
|
|
798
789
|
* Reciprocal: b/a
|
|
@@ -1019,10 +1010,7 @@ var Fraction = class _Fraction {
|
|
|
1019
1010
|
* Used in Stern-Brocot tree and Farey sequences
|
|
1020
1011
|
*/
|
|
1021
1012
|
mediant(other) {
|
|
1022
|
-
return new _Fraction(
|
|
1023
|
-
this.numerator + other.numerator,
|
|
1024
|
-
this.denominator + other.denominator
|
|
1025
|
-
);
|
|
1013
|
+
return new _Fraction(this.numerator + other.numerator, this.denominator + other.denominator);
|
|
1026
1014
|
}
|
|
1027
1015
|
};
|
|
1028
1016
|
var FRACTION_ZERO = new Fraction(0n, 1n);
|
|
@@ -1045,6 +1033,12 @@ var defaultConfig = {
|
|
|
1045
1033
|
var globalConfig = { ...defaultConfig };
|
|
1046
1034
|
var BigNumber = class _BigNumber {
|
|
1047
1035
|
type = "BigNumber";
|
|
1036
|
+
/**
|
|
1037
|
+
* Duck-typing marker for Decimal.js / mathjs formatter compatibility.
|
|
1038
|
+
* Allows external code that checks `obj.isBigNumber === true` to identify
|
|
1039
|
+
* this class without an `instanceof` check.
|
|
1040
|
+
*/
|
|
1041
|
+
isBigNumber = true;
|
|
1048
1042
|
// Internal representation: sign * coefficient * 10^exponent
|
|
1049
1043
|
_sign;
|
|
1050
1044
|
_coefficient;
|
|
@@ -1218,6 +1212,14 @@ var BigNumber = class _BigNumber {
|
|
|
1218
1212
|
const str = this.toString();
|
|
1219
1213
|
return parseFloat(str);
|
|
1220
1214
|
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Convert to a JS number.
|
|
1217
|
+
* Alias for `valueOf()` — provided for Decimal.js / mathjs formatter
|
|
1218
|
+
* duck-typing compatibility.
|
|
1219
|
+
*/
|
|
1220
|
+
toNumber() {
|
|
1221
|
+
return this.valueOf();
|
|
1222
|
+
}
|
|
1221
1223
|
toString() {
|
|
1222
1224
|
if (this._isNaN) return "NaN";
|
|
1223
1225
|
if (this._isInfinite) return this._sign === 1 ? "Infinity" : "-Infinity";
|
|
@@ -1240,35 +1242,55 @@ var BigNumber = class _BigNumber {
|
|
|
1240
1242
|
};
|
|
1241
1243
|
}
|
|
1242
1244
|
/**
|
|
1243
|
-
* Convert to fixed-point notation
|
|
1245
|
+
* Convert to fixed-point notation.
|
|
1246
|
+
*
|
|
1247
|
+
* When called with no argument (or `undefined`) returns the full fixed-point
|
|
1248
|
+
* string representation without rounding — matching Decimal.js semantics where
|
|
1249
|
+
* `toFixed()` is equivalent to a lossless fixed-point stringify.
|
|
1250
|
+
* When called with a non-negative integer `decimalPlaces`, rounds and formats
|
|
1251
|
+
* to exactly that many digits after the decimal point.
|
|
1244
1252
|
*/
|
|
1245
|
-
toFixed(decimalPlaces
|
|
1253
|
+
toFixed(decimalPlaces) {
|
|
1254
|
+
if (decimalPlaces === void 0) {
|
|
1255
|
+
return this.toFixedInternal(void 0);
|
|
1256
|
+
}
|
|
1246
1257
|
return this.round(decimalPlaces).toFixedInternal(decimalPlaces);
|
|
1247
1258
|
}
|
|
1248
1259
|
toFixedInternal(decimalPlaces) {
|
|
1249
1260
|
if (this._isNaN) return "NaN";
|
|
1250
1261
|
if (this._isInfinite) return this._sign === 1 ? "Infinity" : "-Infinity";
|
|
1251
|
-
|
|
1262
|
+
const dp = decimalPlaces ?? -1;
|
|
1263
|
+
const allDigits = dp < 0;
|
|
1264
|
+
if (this._sign === 0) return allDigits ? "0" : dp > 0 ? "0." + "0".repeat(dp) : "0";
|
|
1252
1265
|
const sign = this._sign === -1 ? "-" : "";
|
|
1253
1266
|
const digits = this._coefficient.toString();
|
|
1254
1267
|
if (this._exponent >= 0) {
|
|
1255
1268
|
const intPart = digits + "0".repeat(this._exponent);
|
|
1256
|
-
return sign + intPart
|
|
1269
|
+
if (allDigits) return sign + intPart;
|
|
1270
|
+
return sign + intPart + (dp > 0 ? "." + "0".repeat(dp) : "");
|
|
1257
1271
|
} else if (-this._exponent <= digits.length) {
|
|
1258
1272
|
const insertPos = digits.length + this._exponent;
|
|
1259
1273
|
const intPart = digits.slice(0, insertPos) || "0";
|
|
1260
|
-
|
|
1261
|
-
if (
|
|
1262
|
-
decPart
|
|
1274
|
+
const decPart = digits.slice(insertPos);
|
|
1275
|
+
if (allDigits) {
|
|
1276
|
+
return sign + intPart + (decPart.length > 0 ? "." + decPart : "");
|
|
1263
1277
|
}
|
|
1264
|
-
|
|
1278
|
+
let paddedDec = decPart;
|
|
1279
|
+
if (paddedDec.length < dp) {
|
|
1280
|
+
paddedDec += "0".repeat(dp - paddedDec.length);
|
|
1281
|
+
}
|
|
1282
|
+
return sign + intPart + (dp > 0 ? "." + paddedDec.slice(0, dp) : "");
|
|
1265
1283
|
} else {
|
|
1266
1284
|
const leadingZeros = -this._exponent - digits.length;
|
|
1267
|
-
|
|
1268
|
-
if (
|
|
1269
|
-
|
|
1285
|
+
const decPart = "0".repeat(leadingZeros) + digits;
|
|
1286
|
+
if (allDigits) {
|
|
1287
|
+
return sign + "0." + decPart;
|
|
1288
|
+
}
|
|
1289
|
+
let paddedDec = decPart;
|
|
1290
|
+
if (paddedDec.length < dp) {
|
|
1291
|
+
paddedDec += "0".repeat(dp - paddedDec.length);
|
|
1270
1292
|
}
|
|
1271
|
-
return sign + "0" + (
|
|
1293
|
+
return sign + "0" + (dp > 0 ? "." + paddedDec.slice(0, dp) : "");
|
|
1272
1294
|
}
|
|
1273
1295
|
}
|
|
1274
1296
|
/**
|
|
@@ -1373,6 +1395,12 @@ var BigNumber = class _BigNumber {
|
|
|
1373
1395
|
const b = this.ensureBigNumber(other);
|
|
1374
1396
|
return this.add(b.negate());
|
|
1375
1397
|
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Alias for subtract — mathjs / Decimal.js compatibility.
|
|
1400
|
+
*/
|
|
1401
|
+
sub(other) {
|
|
1402
|
+
return this.subtract(other);
|
|
1403
|
+
}
|
|
1376
1404
|
/**
|
|
1377
1405
|
* Multiplication
|
|
1378
1406
|
*/
|
|
@@ -1390,6 +1418,12 @@ var BigNumber = class _BigNumber {
|
|
|
1390
1418
|
const exponent = this._exponent + b._exponent;
|
|
1391
1419
|
return this.normalize(sign, coefficient, exponent);
|
|
1392
1420
|
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Alias for multiply — mathjs / Decimal.js compatibility.
|
|
1423
|
+
*/
|
|
1424
|
+
mul(other) {
|
|
1425
|
+
return this.multiply(other);
|
|
1426
|
+
}
|
|
1393
1427
|
/**
|
|
1394
1428
|
* Division
|
|
1395
1429
|
*/
|
|
@@ -1501,6 +1535,14 @@ var BigNumber = class _BigNumber {
|
|
|
1501
1535
|
compare(other) {
|
|
1502
1536
|
return this.compareTo(other);
|
|
1503
1537
|
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Returns true iff this > other.
|
|
1540
|
+
* Accepts the same argument types as `add`/`multiply` (BigNumber, number, or string).
|
|
1541
|
+
* Named `gt` for Decimal.js / mathjs formatter compatibility.
|
|
1542
|
+
*/
|
|
1543
|
+
gt(other) {
|
|
1544
|
+
return this.greaterThan(this.ensureBigNumber(other));
|
|
1545
|
+
}
|
|
1504
1546
|
// ============================================================
|
|
1505
1547
|
// Rounding Methods
|
|
1506
1548
|
// ============================================================
|
|
@@ -1579,6 +1621,36 @@ var BigNumber = class _BigNumber {
|
|
|
1579
1621
|
trunc() {
|
|
1580
1622
|
return this.round(0, "down");
|
|
1581
1623
|
}
|
|
1624
|
+
/**
|
|
1625
|
+
* Round to `n` significant decimal digits and return a new BigNumber.
|
|
1626
|
+
* Delegates to `roundToPrecision`, using the global rounding mode unless
|
|
1627
|
+
* an explicit `roundingMode` is supplied.
|
|
1628
|
+
*
|
|
1629
|
+
* Named `toSignificantDigits` for Decimal.js / mathjs formatter compatibility.
|
|
1630
|
+
* When `n` is undefined the value is returned unchanged (no rounding).
|
|
1631
|
+
*/
|
|
1632
|
+
toSignificantDigits(n, roundingMode) {
|
|
1633
|
+
if (n === void 0) return this.clone();
|
|
1634
|
+
return this.roundToPrecision(n, roundingMode ?? globalConfig.rounding);
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* The decimal exponent of this number: `floor(log10(|x|))`.
|
|
1638
|
+
*
|
|
1639
|
+
* Matches Decimal.js semantics:
|
|
1640
|
+
* - `12345` → `4` (5 digits → the most-significant place is 10^4)
|
|
1641
|
+
* - `0.0123` → `-2` (first significant digit is at 10^-2)
|
|
1642
|
+
* - `100` → `2`
|
|
1643
|
+
* - `0.01` → `-2`
|
|
1644
|
+
* - `0` → `0` (by convention; `isZero()` can be used to distinguish)
|
|
1645
|
+
*
|
|
1646
|
+
* For special values (NaN, ±Infinity) returns 0 — callers check
|
|
1647
|
+
* `isFinite()` / `isNaN()` before using `.e`.
|
|
1648
|
+
*/
|
|
1649
|
+
get e() {
|
|
1650
|
+
if (this._isNaN || this._isInfinite || this._sign === 0) return 0;
|
|
1651
|
+
const d = this._coefficient.toString().length;
|
|
1652
|
+
return d - 1 + this._exponent;
|
|
1653
|
+
}
|
|
1582
1654
|
// ============================================================
|
|
1583
1655
|
// Modular Arithmetic
|
|
1584
1656
|
// ============================================================
|
|
@@ -1795,7 +1867,7 @@ var BigNumber = class _BigNumber {
|
|
|
1795
1867
|
if (val > 0.5) {
|
|
1796
1868
|
k = Math.ceil(Math.log2(val * 2));
|
|
1797
1869
|
}
|
|
1798
|
-
const divisor = _BigNumber.fromNumber(2
|
|
1870
|
+
const divisor = _BigNumber.fromNumber(2).pow(k);
|
|
1799
1871
|
const reduced = this.divide(divisor);
|
|
1800
1872
|
let sum = _BigNumber.fromNumber(1);
|
|
1801
1873
|
let term = _BigNumber.fromNumber(1);
|
|
@@ -1883,9 +1955,7 @@ var BigNumber = class _BigNumber {
|
|
|
1883
1955
|
for (let i = 0; i < 100; i++) {
|
|
1884
1956
|
const guessSquared = guess.multiply(guess);
|
|
1885
1957
|
const newGuess = guess.multiply(_BigNumber.fromNumber(2)).add(this.divide(guessSquared)).divide(three);
|
|
1886
|
-
if (guess.subtract(newGuess).abs().compareTo(
|
|
1887
|
-
guess.abs().multiply(_BigNumber.parse("1e-" + (precision + 5)))
|
|
1888
|
-
) <= 0) {
|
|
1958
|
+
if (guess.subtract(newGuess).abs().compareTo(guess.abs().multiply(_BigNumber.parse("1e-" + (precision + 5)))) <= 0) {
|
|
1889
1959
|
return newGuess.roundToPrecision(precision);
|
|
1890
1960
|
}
|
|
1891
1961
|
guess = newGuess;
|
|
@@ -1963,9 +2033,7 @@ var BigNumber = class _BigNumber {
|
|
|
1963
2033
|
const xSquared = this.multiply(this);
|
|
1964
2034
|
const maxIter = precision + 20;
|
|
1965
2035
|
for (let n = 1; n <= maxIter; n++) {
|
|
1966
|
-
term = term.multiply(xSquared).divide(
|
|
1967
|
-
_BigNumber.fromNumber(2 * n * (2 * n + 1))
|
|
1968
|
-
).negate();
|
|
2036
|
+
term = term.multiply(xSquared).divide(_BigNumber.fromNumber(2 * n * (2 * n + 1))).negate();
|
|
1969
2037
|
const newSum = sum.add(term);
|
|
1970
2038
|
if (sum.equals(newSum)) break;
|
|
1971
2039
|
sum = newSum;
|
|
@@ -1980,9 +2048,7 @@ var BigNumber = class _BigNumber {
|
|
|
1980
2048
|
const xSquared = this.multiply(this);
|
|
1981
2049
|
const maxIter = precision + 20;
|
|
1982
2050
|
for (let n = 1; n <= maxIter; n++) {
|
|
1983
|
-
term = term.multiply(xSquared).divide(
|
|
1984
|
-
_BigNumber.fromNumber((2 * n - 1) * (2 * n))
|
|
1985
|
-
).negate();
|
|
2051
|
+
term = term.multiply(xSquared).divide(_BigNumber.fromNumber((2 * n - 1) * (2 * n))).negate();
|
|
1986
2052
|
const newSum = sum.add(term);
|
|
1987
2053
|
if (sum.equals(newSum)) break;
|
|
1988
2054
|
sum = newSum;
|
|
@@ -2034,7 +2100,13 @@ var BigNumber = class _BigNumber {
|
|
|
2034
2100
|
return this._sign;
|
|
2035
2101
|
}
|
|
2036
2102
|
clone() {
|
|
2037
|
-
return new _BigNumber(
|
|
2103
|
+
return new _BigNumber(
|
|
2104
|
+
this._sign,
|
|
2105
|
+
this._coefficient,
|
|
2106
|
+
this._exponent,
|
|
2107
|
+
this._isNaN,
|
|
2108
|
+
this._isInfinite
|
|
2109
|
+
);
|
|
2038
2110
|
}
|
|
2039
2111
|
// ============================================================
|
|
2040
2112
|
// Private Helper Methods
|
|
@@ -2072,8 +2144,683 @@ var BIGNUMBER_NEG_ONE = BigNumber.fromNumber(-1);
|
|
|
2072
2144
|
var BIGNUMBER_TEN = BigNumber.fromNumber(10);
|
|
2073
2145
|
var BIGNUMBER_PI = BigNumber.parse("3.14159265358979323846264338327950288419716939937510");
|
|
2074
2146
|
var BIGNUMBER_E = BigNumber.parse("2.71828182845904523536028747135266249775724709369995");
|
|
2075
|
-
var BIGNUMBER_LN2 = BigNumber.parse(
|
|
2076
|
-
|
|
2147
|
+
var BIGNUMBER_LN2 = BigNumber.parse(
|
|
2148
|
+
"0.69314718055994530941723212145817656807550013436026"
|
|
2149
|
+
);
|
|
2150
|
+
var BIGNUMBER_LN10 = BigNumber.parse(
|
|
2151
|
+
"2.30258509299404568401799145468436420760110148862877"
|
|
2152
|
+
);
|
|
2153
|
+
|
|
2154
|
+
// src/types/unit-definitions.ts
|
|
2155
|
+
var DIMENSIONLESS = Object.freeze({
|
|
2156
|
+
length: 0,
|
|
2157
|
+
mass: 0,
|
|
2158
|
+
time: 0,
|
|
2159
|
+
current: 0,
|
|
2160
|
+
temperature: 0,
|
|
2161
|
+
amount: 0,
|
|
2162
|
+
luminosity: 0
|
|
2163
|
+
});
|
|
2164
|
+
function dim(partial) {
|
|
2165
|
+
return {
|
|
2166
|
+
length: partial.length ?? 0,
|
|
2167
|
+
mass: partial.mass ?? 0,
|
|
2168
|
+
time: partial.time ?? 0,
|
|
2169
|
+
current: partial.current ?? 0,
|
|
2170
|
+
temperature: partial.temperature ?? 0,
|
|
2171
|
+
amount: partial.amount ?? 0,
|
|
2172
|
+
luminosity: partial.luminosity ?? 0
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
var BASE_UNITS = {
|
|
2176
|
+
m: { multiplier: 1, dimensions: dim({ length: 1 }), prefixable: true },
|
|
2177
|
+
// The base unit of mass is *kilogram*, but the prefix-able symbol is "g".
|
|
2178
|
+
// Internally we treat 1 kg as the canonical value, so "g" -> 0.001 kg.
|
|
2179
|
+
kg: { multiplier: 1, dimensions: dim({ mass: 1 }) },
|
|
2180
|
+
s: { multiplier: 1, dimensions: dim({ time: 1 }), prefixable: true },
|
|
2181
|
+
A: { multiplier: 1, dimensions: dim({ current: 1 }), prefixable: true },
|
|
2182
|
+
K: { multiplier: 1, dimensions: dim({ temperature: 1 }), prefixable: true },
|
|
2183
|
+
mol: { multiplier: 1, dimensions: dim({ amount: 1 }), prefixable: true },
|
|
2184
|
+
cd: { multiplier: 1, dimensions: dim({ luminosity: 1 }), prefixable: true }
|
|
2185
|
+
};
|
|
2186
|
+
var DERIVED_UNITS = {
|
|
2187
|
+
// --- Length ---------------------------------------------------------------
|
|
2188
|
+
// Note: 'm' is a base unit (above), already prefixable.
|
|
2189
|
+
ft: { multiplier: 0.3048, dimensions: dim({ length: 1 }) },
|
|
2190
|
+
foot: { multiplier: 0.3048, dimensions: dim({ length: 1 }) },
|
|
2191
|
+
in: { multiplier: 0.0254, dimensions: dim({ length: 1 }) },
|
|
2192
|
+
inch: { multiplier: 0.0254, dimensions: dim({ length: 1 }) },
|
|
2193
|
+
yd: { multiplier: 0.9144, dimensions: dim({ length: 1 }) },
|
|
2194
|
+
yard: { multiplier: 0.9144, dimensions: dim({ length: 1 }) },
|
|
2195
|
+
mi: { multiplier: 1609.344, dimensions: dim({ length: 1 }) },
|
|
2196
|
+
mile: { multiplier: 1609.344, dimensions: dim({ length: 1 }) },
|
|
2197
|
+
// --- Mass -----------------------------------------------------------------
|
|
2198
|
+
// The "g" entry is gram (0.001 kg). It is prefixable, so kg/mg/µg work via
|
|
2199
|
+
// prefix application on this entry rather than the base "kg".
|
|
2200
|
+
g: { multiplier: 1e-3, dimensions: dim({ mass: 1 }), prefixable: true },
|
|
2201
|
+
lb: { multiplier: 0.45359237, dimensions: dim({ mass: 1 }) },
|
|
2202
|
+
lbm: { multiplier: 0.45359237, dimensions: dim({ mass: 1 }) },
|
|
2203
|
+
oz: { multiplier: 0.028349523125, dimensions: dim({ mass: 1 }) },
|
|
2204
|
+
ton: { multiplier: 907.18474, dimensions: dim({ mass: 1 }) },
|
|
2205
|
+
// US short ton
|
|
2206
|
+
tonne: { multiplier: 1e3, dimensions: dim({ mass: 1 }) },
|
|
2207
|
+
// metric ton
|
|
2208
|
+
// --- Time -----------------------------------------------------------------
|
|
2209
|
+
min: { multiplier: 60, dimensions: dim({ time: 1 }) },
|
|
2210
|
+
h: { multiplier: 3600, dimensions: dim({ time: 1 }) },
|
|
2211
|
+
hr: { multiplier: 3600, dimensions: dim({ time: 1 }) },
|
|
2212
|
+
day: { multiplier: 86400, dimensions: dim({ time: 1 }) },
|
|
2213
|
+
week: { multiplier: 604800, dimensions: dim({ time: 1 }) },
|
|
2214
|
+
year: { multiplier: 31557600, dimensions: dim({ time: 1 }) },
|
|
2215
|
+
// Julian year
|
|
2216
|
+
// --- Temperature ----------------------------------------------------------
|
|
2217
|
+
// K is the base.
|
|
2218
|
+
// °C → K is offset by 273.15 (no multiplicative factor change).
|
|
2219
|
+
// °F → K is more complex (offset & scale).
|
|
2220
|
+
degC: { multiplier: 1, offset: 273.15, dimensions: dim({ temperature: 1 }) },
|
|
2221
|
+
degF: { multiplier: 5 / 9, offset: 459.67 * (5 / 9), dimensions: dim({ temperature: 1 }) },
|
|
2222
|
+
degR: { multiplier: 5 / 9, dimensions: dim({ temperature: 1 }) },
|
|
2223
|
+
// Rankine
|
|
2224
|
+
// --- Plane angle (dimensionless, but often treated as units) --------------
|
|
2225
|
+
rad: { multiplier: 1, dimensions: dim({}), prefixable: true },
|
|
2226
|
+
deg: { multiplier: Math.PI / 180, dimensions: dim({}) },
|
|
2227
|
+
grad: { multiplier: Math.PI / 200, dimensions: dim({}) },
|
|
2228
|
+
// --- Force ----------------------------------------------------------------
|
|
2229
|
+
// N = kg·m·s^-2 = 1 kg·m·s^-2; in our base-units representation that's
|
|
2230
|
+
// value=1, dim {length: 1, mass: 1, time: -2}.
|
|
2231
|
+
N: { multiplier: 1, dimensions: dim({ length: 1, mass: 1, time: -2 }), prefixable: true },
|
|
2232
|
+
dyn: { multiplier: 1e-5, dimensions: dim({ length: 1, mass: 1, time: -2 }) },
|
|
2233
|
+
lbf: { multiplier: 4.4482216152605, dimensions: dim({ length: 1, mass: 1, time: -2 }) },
|
|
2234
|
+
// --- Energy ---------------------------------------------------------------
|
|
2235
|
+
J: { multiplier: 1, dimensions: dim({ length: 2, mass: 1, time: -2 }), prefixable: true },
|
|
2236
|
+
erg: { multiplier: 1e-7, dimensions: dim({ length: 2, mass: 1, time: -2 }) },
|
|
2237
|
+
cal: { multiplier: 4.184, dimensions: dim({ length: 2, mass: 1, time: -2 }) },
|
|
2238
|
+
eV: {
|
|
2239
|
+
multiplier: 1602176634e-28,
|
|
2240
|
+
dimensions: dim({ length: 2, mass: 1, time: -2 }),
|
|
2241
|
+
prefixable: true
|
|
2242
|
+
},
|
|
2243
|
+
BTU: { multiplier: 1055.05585262, dimensions: dim({ length: 2, mass: 1, time: -2 }) },
|
|
2244
|
+
// --- Power ----------------------------------------------------------------
|
|
2245
|
+
W: { multiplier: 1, dimensions: dim({ length: 2, mass: 1, time: -3 }), prefixable: true },
|
|
2246
|
+
hp: { multiplier: 745.6998715822702, dimensions: dim({ length: 2, mass: 1, time: -3 }) },
|
|
2247
|
+
// --- Pressure -------------------------------------------------------------
|
|
2248
|
+
Pa: { multiplier: 1, dimensions: dim({ length: -1, mass: 1, time: -2 }), prefixable: true },
|
|
2249
|
+
bar: { multiplier: 1e5, dimensions: dim({ length: -1, mass: 1, time: -2 }), prefixable: true },
|
|
2250
|
+
atm: { multiplier: 101325, dimensions: dim({ length: -1, mass: 1, time: -2 }) },
|
|
2251
|
+
psi: { multiplier: 6894.757293168361, dimensions: dim({ length: -1, mass: 1, time: -2 }) },
|
|
2252
|
+
torr: { multiplier: 133.322368421, dimensions: dim({ length: -1, mass: 1, time: -2 }) },
|
|
2253
|
+
mmHg: { multiplier: 133.322387415, dimensions: dim({ length: -1, mass: 1, time: -2 }) },
|
|
2254
|
+
// --- Electric -------------------------------------------------------------
|
|
2255
|
+
C: {
|
|
2256
|
+
multiplier: 1,
|
|
2257
|
+
dimensions: dim({ time: 1, current: 1 }),
|
|
2258
|
+
prefixable: true
|
|
2259
|
+
},
|
|
2260
|
+
V: {
|
|
2261
|
+
multiplier: 1,
|
|
2262
|
+
dimensions: dim({ length: 2, mass: 1, time: -3, current: -1 }),
|
|
2263
|
+
prefixable: true
|
|
2264
|
+
},
|
|
2265
|
+
ohm: {
|
|
2266
|
+
multiplier: 1,
|
|
2267
|
+
dimensions: dim({ length: 2, mass: 1, time: -3, current: -2 }),
|
|
2268
|
+
prefixable: true
|
|
2269
|
+
},
|
|
2270
|
+
\u03A9: {
|
|
2271
|
+
multiplier: 1,
|
|
2272
|
+
dimensions: dim({ length: 2, mass: 1, time: -3, current: -2 }),
|
|
2273
|
+
prefixable: true
|
|
2274
|
+
},
|
|
2275
|
+
F: {
|
|
2276
|
+
multiplier: 1,
|
|
2277
|
+
dimensions: dim({ length: -2, mass: -1, time: 4, current: 2 }),
|
|
2278
|
+
prefixable: true
|
|
2279
|
+
},
|
|
2280
|
+
H: {
|
|
2281
|
+
multiplier: 1,
|
|
2282
|
+
dimensions: dim({ length: 2, mass: 1, time: -2, current: -2 }),
|
|
2283
|
+
prefixable: true
|
|
2284
|
+
},
|
|
2285
|
+
// --- Frequency ------------------------------------------------------------
|
|
2286
|
+
Hz: { multiplier: 1, dimensions: dim({ time: -1 }), prefixable: true },
|
|
2287
|
+
// --- Volume ---------------------------------------------------------------
|
|
2288
|
+
L: { multiplier: 1e-3, dimensions: dim({ length: 3 }), prefixable: true },
|
|
2289
|
+
l: { multiplier: 1e-3, dimensions: dim({ length: 3 }), prefixable: true },
|
|
2290
|
+
gal: { multiplier: 0.003785411784, dimensions: dim({ length: 3 }) },
|
|
2291
|
+
// US gallon
|
|
2292
|
+
// --- Area -----------------------------------------------------------------
|
|
2293
|
+
ha: { multiplier: 1e4, dimensions: dim({ length: 2 }) },
|
|
2294
|
+
// hectare
|
|
2295
|
+
acre: { multiplier: 4046.8564224, dimensions: dim({ length: 2 }) }
|
|
2296
|
+
};
|
|
2297
|
+
var ALL_UNITS = { ...BASE_UNITS, ...DERIVED_UNITS };
|
|
2298
|
+
var UNIT_ALIASES = {
|
|
2299
|
+
"\xB0C": "degC",
|
|
2300
|
+
"\xB0F": "degF",
|
|
2301
|
+
"\xB0R": "degR",
|
|
2302
|
+
"\xB0": "deg"
|
|
2303
|
+
// The Unicode micro sign is handled in prefixes; °K is non-standard.
|
|
2304
|
+
};
|
|
2305
|
+
function getUnitDef(name) {
|
|
2306
|
+
return ALL_UNITS[name];
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
// src/types/unit-prefixes.ts
|
|
2310
|
+
var SI_PREFIXES = {
|
|
2311
|
+
// Positive powers of ten
|
|
2312
|
+
Y: 1e24,
|
|
2313
|
+
// yotta
|
|
2314
|
+
Z: 1e21,
|
|
2315
|
+
// zetta
|
|
2316
|
+
E: 1e18,
|
|
2317
|
+
// exa
|
|
2318
|
+
P: 1e15,
|
|
2319
|
+
// peta
|
|
2320
|
+
T: 1e12,
|
|
2321
|
+
// tera
|
|
2322
|
+
G: 1e9,
|
|
2323
|
+
// giga
|
|
2324
|
+
M: 1e6,
|
|
2325
|
+
// mega
|
|
2326
|
+
k: 1e3,
|
|
2327
|
+
// kilo
|
|
2328
|
+
h: 100,
|
|
2329
|
+
// hecto
|
|
2330
|
+
da: 10,
|
|
2331
|
+
// deka (two-letter)
|
|
2332
|
+
// Negative powers of ten
|
|
2333
|
+
d: 0.1,
|
|
2334
|
+
// deci
|
|
2335
|
+
c: 0.01,
|
|
2336
|
+
// centi
|
|
2337
|
+
m: 1e-3,
|
|
2338
|
+
// milli
|
|
2339
|
+
u: 1e-6,
|
|
2340
|
+
// micro (ASCII fallback)
|
|
2341
|
+
\u00B5: 1e-6,
|
|
2342
|
+
// micro (Unicode)
|
|
2343
|
+
n: 1e-9,
|
|
2344
|
+
// nano
|
|
2345
|
+
p: 1e-12,
|
|
2346
|
+
// pico
|
|
2347
|
+
f: 1e-15,
|
|
2348
|
+
// femto
|
|
2349
|
+
a: 1e-18,
|
|
2350
|
+
// atto
|
|
2351
|
+
z: 1e-21,
|
|
2352
|
+
// zepto
|
|
2353
|
+
y: 1e-24
|
|
2354
|
+
// yocto
|
|
2355
|
+
};
|
|
2356
|
+
var SI_PREFIX_KEYS = Object.keys(SI_PREFIXES).sort(
|
|
2357
|
+
(a, b) => b.length - a.length
|
|
2358
|
+
);
|
|
2359
|
+
var BEST_PREFIXES = [
|
|
2360
|
+
"Y",
|
|
2361
|
+
"Z",
|
|
2362
|
+
"E",
|
|
2363
|
+
"P",
|
|
2364
|
+
"T",
|
|
2365
|
+
"G",
|
|
2366
|
+
"M",
|
|
2367
|
+
"k",
|
|
2368
|
+
"",
|
|
2369
|
+
"m",
|
|
2370
|
+
"u",
|
|
2371
|
+
"n",
|
|
2372
|
+
"p",
|
|
2373
|
+
"f",
|
|
2374
|
+
"a",
|
|
2375
|
+
"z",
|
|
2376
|
+
"y"
|
|
2377
|
+
];
|
|
2378
|
+
function getPrefix(name) {
|
|
2379
|
+
return SI_PREFIXES[name];
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
// src/types/unit.ts
|
|
2383
|
+
var DimensionMismatchError = class extends Error {
|
|
2384
|
+
constructor(left, right, op) {
|
|
2385
|
+
super(
|
|
2386
|
+
`Dimension mismatch in ${op}: ${formatDimensions(left)} vs ${formatDimensions(right)}`
|
|
2387
|
+
);
|
|
2388
|
+
this.left = left;
|
|
2389
|
+
this.right = right;
|
|
2390
|
+
}
|
|
2391
|
+
left;
|
|
2392
|
+
right;
|
|
2393
|
+
name = "DimensionMismatchError";
|
|
2394
|
+
};
|
|
2395
|
+
var UnitParseError = class extends Error {
|
|
2396
|
+
name = "UnitParseError";
|
|
2397
|
+
constructor(notation, reason) {
|
|
2398
|
+
super(`Cannot parse unit notation "${notation}": ${reason}`);
|
|
2399
|
+
}
|
|
2400
|
+
};
|
|
2401
|
+
var DIM_KEYS = [
|
|
2402
|
+
"length",
|
|
2403
|
+
"mass",
|
|
2404
|
+
"time",
|
|
2405
|
+
"current",
|
|
2406
|
+
"temperature",
|
|
2407
|
+
"amount",
|
|
2408
|
+
"luminosity"
|
|
2409
|
+
];
|
|
2410
|
+
function dimsEqual(a, b) {
|
|
2411
|
+
for (const k of DIM_KEYS) {
|
|
2412
|
+
if (a[k] !== b[k]) return false;
|
|
2413
|
+
}
|
|
2414
|
+
return true;
|
|
2415
|
+
}
|
|
2416
|
+
function dimsAdd(a, b) {
|
|
2417
|
+
return {
|
|
2418
|
+
length: a.length + b.length,
|
|
2419
|
+
mass: a.mass + b.mass,
|
|
2420
|
+
time: a.time + b.time,
|
|
2421
|
+
current: a.current + b.current,
|
|
2422
|
+
temperature: a.temperature + b.temperature,
|
|
2423
|
+
amount: a.amount + b.amount,
|
|
2424
|
+
luminosity: a.luminosity + b.luminosity
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
function dimsSub(a, b) {
|
|
2428
|
+
return {
|
|
2429
|
+
length: a.length - b.length,
|
|
2430
|
+
mass: a.mass - b.mass,
|
|
2431
|
+
time: a.time - b.time,
|
|
2432
|
+
current: a.current - b.current,
|
|
2433
|
+
temperature: a.temperature - b.temperature,
|
|
2434
|
+
amount: a.amount - b.amount,
|
|
2435
|
+
luminosity: a.luminosity - b.luminosity
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2438
|
+
function dimsScale(a, n) {
|
|
2439
|
+
return {
|
|
2440
|
+
length: a.length * n,
|
|
2441
|
+
mass: a.mass * n,
|
|
2442
|
+
time: a.time * n,
|
|
2443
|
+
current: a.current * n,
|
|
2444
|
+
temperature: a.temperature * n,
|
|
2445
|
+
amount: a.amount * n,
|
|
2446
|
+
luminosity: a.luminosity * n
|
|
2447
|
+
};
|
|
2448
|
+
}
|
|
2449
|
+
function isDimensionless(d) {
|
|
2450
|
+
return dimsEqual(d, DIMENSIONLESS);
|
|
2451
|
+
}
|
|
2452
|
+
function formatDimensions(d) {
|
|
2453
|
+
const parts = [];
|
|
2454
|
+
for (const k of DIM_KEYS) {
|
|
2455
|
+
const e = d[k];
|
|
2456
|
+
if (e !== 0) parts.push(`${k}^${e}`);
|
|
2457
|
+
}
|
|
2458
|
+
return parts.length === 0 ? "dimensionless" : parts.join("\xB7");
|
|
2459
|
+
}
|
|
2460
|
+
function resolveAtom(token) {
|
|
2461
|
+
const aliased = UNIT_ALIASES[token] ?? token;
|
|
2462
|
+
const direct = getUnitDef(aliased);
|
|
2463
|
+
if (direct) {
|
|
2464
|
+
return {
|
|
2465
|
+
multiplier: direct.multiplier,
|
|
2466
|
+
offset: direct.offset ?? 0,
|
|
2467
|
+
dimensions: direct.dimensions,
|
|
2468
|
+
prefix: "",
|
|
2469
|
+
base: aliased
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
for (const prefix of SI_PREFIX_KEYS) {
|
|
2473
|
+
if (!aliased.startsWith(prefix) || aliased.length === prefix.length) continue;
|
|
2474
|
+
const rest = aliased.slice(prefix.length);
|
|
2475
|
+
const baseDef = getUnitDef(rest);
|
|
2476
|
+
if (!baseDef) continue;
|
|
2477
|
+
if (!baseDef.prefixable) continue;
|
|
2478
|
+
return {
|
|
2479
|
+
multiplier: baseDef.multiplier * SI_PREFIXES[prefix],
|
|
2480
|
+
// Offset is in base units, so prefix the multiplier but not the offset
|
|
2481
|
+
// (we never apply prefixes to temperature units in practice anyway).
|
|
2482
|
+
offset: baseDef.offset ?? 0,
|
|
2483
|
+
dimensions: baseDef.dimensions,
|
|
2484
|
+
prefix,
|
|
2485
|
+
base: rest
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
throw new UnitParseError(token, `unknown unit "${token}"`);
|
|
2489
|
+
}
|
|
2490
|
+
function splitScalar(input) {
|
|
2491
|
+
const m = input.match(/^\s*([+-]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\s+(.*)$/);
|
|
2492
|
+
if (m) {
|
|
2493
|
+
return { scalar: parseFloat(m[1]), rest: m[2].trim() };
|
|
2494
|
+
}
|
|
2495
|
+
const scalarOnly = input.match(/^\s*([+-]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\s*$/);
|
|
2496
|
+
if (scalarOnly) {
|
|
2497
|
+
return { scalar: parseFloat(scalarOnly[1]), rest: "" };
|
|
2498
|
+
}
|
|
2499
|
+
return { scalar: 1, rest: input.trim() };
|
|
2500
|
+
}
|
|
2501
|
+
function parseUnitExpr(expr) {
|
|
2502
|
+
if (expr === "" || expr === "1") return [];
|
|
2503
|
+
const normalized = expr.replace(/[·⋅]/g, "*");
|
|
2504
|
+
const atoms = [];
|
|
2505
|
+
let pos = 0;
|
|
2506
|
+
let nextSign = 1;
|
|
2507
|
+
while (pos < normalized.length) {
|
|
2508
|
+
while (pos < normalized.length && /\s/.test(normalized[pos])) pos++;
|
|
2509
|
+
if (pos >= normalized.length) break;
|
|
2510
|
+
const ch = normalized[pos];
|
|
2511
|
+
if (ch === "*") {
|
|
2512
|
+
pos++;
|
|
2513
|
+
continue;
|
|
2514
|
+
}
|
|
2515
|
+
if (ch === "/") {
|
|
2516
|
+
nextSign = -1;
|
|
2517
|
+
pos++;
|
|
2518
|
+
continue;
|
|
2519
|
+
}
|
|
2520
|
+
if (ch === "1") {
|
|
2521
|
+
const next = normalized[pos + 1];
|
|
2522
|
+
if (next === void 0 || next === " " || next === "/" || next === "*") {
|
|
2523
|
+
pos++;
|
|
2524
|
+
continue;
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
if (!/[A-Za-z°µΩ]/.test(ch)) {
|
|
2528
|
+
throw new UnitParseError(expr, `unexpected character "${ch}" at position ${pos}`);
|
|
2529
|
+
}
|
|
2530
|
+
const idStart = pos;
|
|
2531
|
+
while (pos < normalized.length && /[A-Za-z°µΩ]/.test(normalized[pos])) pos++;
|
|
2532
|
+
const identifier = normalized.slice(idStart, pos);
|
|
2533
|
+
let exponent = 1;
|
|
2534
|
+
if (pos < normalized.length && normalized[pos] === "^") {
|
|
2535
|
+
pos++;
|
|
2536
|
+
const expMatch = normalized.slice(pos).match(/^([+-]?\d+(?:\.\d+)?)/);
|
|
2537
|
+
if (!expMatch) {
|
|
2538
|
+
throw new UnitParseError(expr, `expected exponent after "^" at position ${pos}`);
|
|
2539
|
+
}
|
|
2540
|
+
exponent = parseFloat(expMatch[1]);
|
|
2541
|
+
pos += expMatch[1].length;
|
|
2542
|
+
} else {
|
|
2543
|
+
const supMatch = normalized.slice(pos).match(/^([²³⁴⁵⁶⁷⁸⁹⁰¹]+)/);
|
|
2544
|
+
if (supMatch) {
|
|
2545
|
+
exponent = parseSuperscript(supMatch[1]);
|
|
2546
|
+
pos += supMatch[1].length;
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
const resolved = resolveAtom(identifier);
|
|
2550
|
+
atoms.push({
|
|
2551
|
+
multiplier: resolved.multiplier,
|
|
2552
|
+
offset: resolved.offset,
|
|
2553
|
+
dimensions: resolved.dimensions,
|
|
2554
|
+
exponent: nextSign * exponent,
|
|
2555
|
+
source: identifier
|
|
2556
|
+
});
|
|
2557
|
+
nextSign = 1;
|
|
2558
|
+
}
|
|
2559
|
+
return atoms;
|
|
2560
|
+
}
|
|
2561
|
+
var SUPERSCRIPT_MAP = {
|
|
2562
|
+
"\u2070": "0",
|
|
2563
|
+
"\xB9": "1",
|
|
2564
|
+
"\xB2": "2",
|
|
2565
|
+
"\xB3": "3",
|
|
2566
|
+
"\u2074": "4",
|
|
2567
|
+
"\u2075": "5",
|
|
2568
|
+
"\u2076": "6",
|
|
2569
|
+
"\u2077": "7",
|
|
2570
|
+
"\u2078": "8",
|
|
2571
|
+
"\u2079": "9"
|
|
2572
|
+
};
|
|
2573
|
+
function parseSuperscript(s) {
|
|
2574
|
+
let out = "";
|
|
2575
|
+
for (const ch of s) out += SUPERSCRIPT_MAP[ch] ?? ch;
|
|
2576
|
+
return parseInt(out, 10);
|
|
2577
|
+
}
|
|
2578
|
+
function parse(input) {
|
|
2579
|
+
const { scalar, rest } = splitScalar(input);
|
|
2580
|
+
return { scalar, atoms: parseUnitExpr(rest) };
|
|
2581
|
+
}
|
|
2582
|
+
var Unit = class _Unit {
|
|
2583
|
+
/** Discriminator (matches the prototype-flag convention). */
|
|
2584
|
+
type = "Unit";
|
|
2585
|
+
/** Used by `isUnit()` and typed-function dispatch. */
|
|
2586
|
+
isUnit = true;
|
|
2587
|
+
/** Numeric magnitude in **SI base units**. */
|
|
2588
|
+
value;
|
|
2589
|
+
/** Dimensional signature. */
|
|
2590
|
+
dimensions;
|
|
2591
|
+
/** Original notation string, preserved for `toString()` round-trip. */
|
|
2592
|
+
notation;
|
|
2593
|
+
constructor(value, notationOrDims, notation) {
|
|
2594
|
+
if (typeof notationOrDims === "string") {
|
|
2595
|
+
const parsed = parse(notationOrDims);
|
|
2596
|
+
const composed = composeAtoms(parsed.atoms);
|
|
2597
|
+
const inputValue = parsed.scalar * value;
|
|
2598
|
+
let canonical;
|
|
2599
|
+
if (parsed.atoms.length === 1 && parsed.atoms[0].exponent === 1) {
|
|
2600
|
+
const a = parsed.atoms[0];
|
|
2601
|
+
canonical = inputValue * a.multiplier + a.offset;
|
|
2602
|
+
} else {
|
|
2603
|
+
canonical = inputValue * composed.multiplier;
|
|
2604
|
+
}
|
|
2605
|
+
this.value = canonical;
|
|
2606
|
+
this.dimensions = composed.dimensions;
|
|
2607
|
+
this.notation = notationOrDims;
|
|
2608
|
+
} else {
|
|
2609
|
+
this.value = value;
|
|
2610
|
+
this.dimensions = notationOrDims;
|
|
2611
|
+
this.notation = notation ?? "";
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
// -------------------------------------------------------------------------
|
|
2615
|
+
// Arithmetic
|
|
2616
|
+
// -------------------------------------------------------------------------
|
|
2617
|
+
/**
|
|
2618
|
+
* Add two units. Requires identical dimensions.
|
|
2619
|
+
*
|
|
2620
|
+
* @throws {DimensionMismatchError} if dimensions differ.
|
|
2621
|
+
*/
|
|
2622
|
+
add(other) {
|
|
2623
|
+
if (!dimsEqual(this.dimensions, other.dimensions)) {
|
|
2624
|
+
throw new DimensionMismatchError(this.dimensions, other.dimensions, "add");
|
|
2625
|
+
}
|
|
2626
|
+
return new _Unit(this.value + other.value, this.dimensions, this.notation);
|
|
2627
|
+
}
|
|
2628
|
+
/**
|
|
2629
|
+
* Subtract two units. Requires identical dimensions.
|
|
2630
|
+
*
|
|
2631
|
+
* @throws {DimensionMismatchError} if dimensions differ.
|
|
2632
|
+
*/
|
|
2633
|
+
sub(other) {
|
|
2634
|
+
if (!dimsEqual(this.dimensions, other.dimensions)) {
|
|
2635
|
+
throw new DimensionMismatchError(this.dimensions, other.dimensions, "sub");
|
|
2636
|
+
}
|
|
2637
|
+
return new _Unit(this.value - other.value, this.dimensions, this.notation);
|
|
2638
|
+
}
|
|
2639
|
+
/**
|
|
2640
|
+
* Multiply by another unit or a scalar. Dimensions add when multiplying by
|
|
2641
|
+
* another unit; unchanged when multiplying by a scalar.
|
|
2642
|
+
*/
|
|
2643
|
+
mul(other) {
|
|
2644
|
+
if (typeof other === "number") {
|
|
2645
|
+
return new _Unit(this.value * other, this.dimensions, this.notation);
|
|
2646
|
+
}
|
|
2647
|
+
return new _Unit(
|
|
2648
|
+
this.value * other.value,
|
|
2649
|
+
dimsAdd(this.dimensions, other.dimensions),
|
|
2650
|
+
composeNotation(this.notation, other.notation, "*")
|
|
2651
|
+
);
|
|
2652
|
+
}
|
|
2653
|
+
/**
|
|
2654
|
+
* Divide by another unit or a scalar. Dimensions subtract when dividing by
|
|
2655
|
+
* another unit; unchanged when dividing by a scalar.
|
|
2656
|
+
*/
|
|
2657
|
+
div(other) {
|
|
2658
|
+
if (typeof other === "number") {
|
|
2659
|
+
return new _Unit(this.value / other, this.dimensions, this.notation);
|
|
2660
|
+
}
|
|
2661
|
+
return new _Unit(
|
|
2662
|
+
this.value / other.value,
|
|
2663
|
+
dimsSub(this.dimensions, other.dimensions),
|
|
2664
|
+
composeNotation(this.notation, other.notation, "/")
|
|
2665
|
+
);
|
|
2666
|
+
}
|
|
2667
|
+
/**
|
|
2668
|
+
* Raise to an integer or rational power. Dimensions scale by `n`.
|
|
2669
|
+
*/
|
|
2670
|
+
pow(n) {
|
|
2671
|
+
return new _Unit(
|
|
2672
|
+
this.value ** n,
|
|
2673
|
+
dimsScale(this.dimensions, n),
|
|
2674
|
+
this.notation ? `(${this.notation})^${n}` : ""
|
|
2675
|
+
);
|
|
2676
|
+
}
|
|
2677
|
+
// -------------------------------------------------------------------------
|
|
2678
|
+
// Conversion
|
|
2679
|
+
// -------------------------------------------------------------------------
|
|
2680
|
+
/**
|
|
2681
|
+
* Convert to a target unit. Dimensions must match.
|
|
2682
|
+
*
|
|
2683
|
+
* @throws {DimensionMismatchError} if `targetNotation`'s dimensions differ
|
|
2684
|
+
* from this unit's.
|
|
2685
|
+
*/
|
|
2686
|
+
to(targetNotation) {
|
|
2687
|
+
const parsed = parse(targetNotation);
|
|
2688
|
+
const composed = composeAtoms(parsed.atoms);
|
|
2689
|
+
if (!dimsEqual(this.dimensions, composed.dimensions)) {
|
|
2690
|
+
throw new DimensionMismatchError(
|
|
2691
|
+
this.dimensions,
|
|
2692
|
+
composed.dimensions,
|
|
2693
|
+
`to("${targetNotation}")`
|
|
2694
|
+
);
|
|
2695
|
+
}
|
|
2696
|
+
return new _Unit(this.value, composed.dimensions, targetNotation);
|
|
2697
|
+
}
|
|
2698
|
+
/**
|
|
2699
|
+
* Pick a unit (with SI prefix) that gives a magnitude closest to 1.
|
|
2700
|
+
*
|
|
2701
|
+
* Searches the unit registry for entries matching this unit's dimensions,
|
|
2702
|
+
* then applies each SI prefix in {@link BEST_PREFIXES} to find the
|
|
2703
|
+
* representation minimising `|log10(displayed value)|`.
|
|
2704
|
+
*
|
|
2705
|
+
* If no registered unit matches the dimensions, falls back to a synthetic
|
|
2706
|
+
* notation describing the dimensional vector.
|
|
2707
|
+
*/
|
|
2708
|
+
toBest() {
|
|
2709
|
+
if (isDimensionless(this.dimensions)) {
|
|
2710
|
+
return new _Unit(this.value, this.dimensions, "");
|
|
2711
|
+
}
|
|
2712
|
+
let bestNotation;
|
|
2713
|
+
let bestScore = Infinity;
|
|
2714
|
+
for (const [name, def] of Object.entries(ALL_UNITS)) {
|
|
2715
|
+
if (name === "kg") continue;
|
|
2716
|
+
if (!dimsEqual(def.dimensions, this.dimensions)) continue;
|
|
2717
|
+
if (def.offset !== void 0) continue;
|
|
2718
|
+
const candidatePrefixes = def.prefixable ? BEST_PREFIXES : [""];
|
|
2719
|
+
for (const prefix of candidatePrefixes) {
|
|
2720
|
+
const factor = prefix === "" ? def.multiplier : def.multiplier * SI_PREFIXES[prefix];
|
|
2721
|
+
if (factor === 0) continue;
|
|
2722
|
+
const displayed = this.value / factor;
|
|
2723
|
+
if (displayed === 0) continue;
|
|
2724
|
+
const score = Math.abs(Math.log10(Math.abs(displayed)));
|
|
2725
|
+
if (score < bestScore) {
|
|
2726
|
+
bestScore = score;
|
|
2727
|
+
bestNotation = prefix + name;
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
if (bestNotation === void 0) {
|
|
2732
|
+
return new _Unit(
|
|
2733
|
+
this.value,
|
|
2734
|
+
this.dimensions,
|
|
2735
|
+
this.notation || formatDimensions(this.dimensions)
|
|
2736
|
+
);
|
|
2737
|
+
}
|
|
2738
|
+
return new _Unit(this.value, this.dimensions, bestNotation);
|
|
2739
|
+
}
|
|
2740
|
+
// -------------------------------------------------------------------------
|
|
2741
|
+
// Comparison
|
|
2742
|
+
// -------------------------------------------------------------------------
|
|
2743
|
+
/** True iff dimensions and canonical value are equal. */
|
|
2744
|
+
equals(other) {
|
|
2745
|
+
return dimsEqual(this.dimensions, other.dimensions) && this.value === other.value;
|
|
2746
|
+
}
|
|
2747
|
+
/** True iff dimensions match (values may differ). */
|
|
2748
|
+
dimensionsEqual(other) {
|
|
2749
|
+
return dimsEqual(this.dimensions, other.dimensions);
|
|
2750
|
+
}
|
|
2751
|
+
// -------------------------------------------------------------------------
|
|
2752
|
+
// Formatting
|
|
2753
|
+
// -------------------------------------------------------------------------
|
|
2754
|
+
/**
|
|
2755
|
+
* Pretty-print. If `notation` is set (the common case), display the value
|
|
2756
|
+
* in that notation — otherwise emit a dimensional signature.
|
|
2757
|
+
*/
|
|
2758
|
+
toString() {
|
|
2759
|
+
if (this.notation === "") {
|
|
2760
|
+
if (isDimensionless(this.dimensions)) return String(this.value);
|
|
2761
|
+
return `${this.value} ${formatDimensions(this.dimensions)}`;
|
|
2762
|
+
}
|
|
2763
|
+
const displayed = displayedValue(this.value, this.notation);
|
|
2764
|
+
return `${formatNumber(displayed)} ${this.notation}`;
|
|
2765
|
+
}
|
|
2766
|
+
/** JSON-serialisable record. */
|
|
2767
|
+
toJSON() {
|
|
2768
|
+
return { mathts: "Unit", value: this.value, notation: this.notation };
|
|
2769
|
+
}
|
|
2770
|
+
/**
|
|
2771
|
+
* Rehydrate from a JSON record produced by {@link toJSON}.
|
|
2772
|
+
*
|
|
2773
|
+
* @throws {UnitParseError} if `json.notation` cannot be parsed.
|
|
2774
|
+
*/
|
|
2775
|
+
static fromJSON(json) {
|
|
2776
|
+
const parsed = parse(json.notation);
|
|
2777
|
+
const composed = composeAtoms(parsed.atoms);
|
|
2778
|
+
return new _Unit(json.value, composed.dimensions, json.notation);
|
|
2779
|
+
}
|
|
2780
|
+
/**
|
|
2781
|
+
* Parse a full unit expression (with optional leading numeric coefficient).
|
|
2782
|
+
*
|
|
2783
|
+
* Example: `Unit.parse('5 km/h')`.
|
|
2784
|
+
*/
|
|
2785
|
+
static parse(input) {
|
|
2786
|
+
const { scalar, rest } = splitScalar(input);
|
|
2787
|
+
return new _Unit(scalar, rest);
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
Unit.prototype.isUnit = true;
|
|
2791
|
+
function composeAtoms(atoms) {
|
|
2792
|
+
let multiplier = 1;
|
|
2793
|
+
let dimensions = { ...DIMENSIONLESS };
|
|
2794
|
+
for (const a of atoms) {
|
|
2795
|
+
multiplier *= a.multiplier ** a.exponent;
|
|
2796
|
+
dimensions = dimsAdd(dimensions, dimsScale(a.dimensions, a.exponent));
|
|
2797
|
+
}
|
|
2798
|
+
return { multiplier, dimensions };
|
|
2799
|
+
}
|
|
2800
|
+
function displayedValue(canonical, notation) {
|
|
2801
|
+
const parsed = parse(notation);
|
|
2802
|
+
const composed = composeAtoms(parsed.atoms);
|
|
2803
|
+
if (parsed.atoms.length === 1 && parsed.atoms[0].exponent === 1) {
|
|
2804
|
+
const a = parsed.atoms[0];
|
|
2805
|
+
return (canonical - a.offset) / a.multiplier;
|
|
2806
|
+
}
|
|
2807
|
+
return canonical / composed.multiplier;
|
|
2808
|
+
}
|
|
2809
|
+
function formatNumber(n) {
|
|
2810
|
+
if (Number.isInteger(n)) return n.toString();
|
|
2811
|
+
const rounded = Number(n.toPrecision(12));
|
|
2812
|
+
return rounded.toString();
|
|
2813
|
+
}
|
|
2814
|
+
function composeNotation(a, b, op) {
|
|
2815
|
+
if (a === "" && b === "") return "";
|
|
2816
|
+
if (a === "") return op === "*" ? b : `1/${b}`;
|
|
2817
|
+
if (b === "") return a;
|
|
2818
|
+
const sep = op === "*" ? "*" : "/";
|
|
2819
|
+
return `${a}${sep}${b}`;
|
|
2820
|
+
}
|
|
2821
|
+
function isUnit(value) {
|
|
2822
|
+
return value instanceof Unit;
|
|
2823
|
+
}
|
|
2077
2824
|
|
|
2078
2825
|
// src/typed/mathts-typed.ts
|
|
2079
2826
|
import typed, { create } from "typed-function";
|
|
@@ -2115,7 +2862,14 @@ var isBigNumber2 = (x) => isBigNumber(x);
|
|
|
2115
2862
|
var isMatrix = (x) => isObject(x) && "rows" in x && "cols" in x && "get" in x && typeof x.type === "string";
|
|
2116
2863
|
var isDenseMatrix = (x) => isMatrix(x) && x.type === "DenseMatrix";
|
|
2117
2864
|
var isSparseMatrix = (x) => isMatrix(x) && x.type === "SparseMatrix";
|
|
2118
|
-
var
|
|
2865
|
+
var isUnit2 = (x) => {
|
|
2866
|
+
if (!isObject(x)) return false;
|
|
2867
|
+
const o = x;
|
|
2868
|
+
if (typeof o.type !== "string") return false;
|
|
2869
|
+
if (o.type === "Unit" && "value" in o && "dimensions" in o) return true;
|
|
2870
|
+
if ("value" in o && "unit" in o) return true;
|
|
2871
|
+
return false;
|
|
2872
|
+
};
|
|
2119
2873
|
var MATHTS_TYPES = [
|
|
2120
2874
|
// bigint is not built into typed-function
|
|
2121
2875
|
{ name: "bigint", test: isBigInt },
|
|
@@ -2135,7 +2889,7 @@ var MATHTS_TYPES = [
|
|
|
2135
2889
|
{ name: "SparseMatrix", test: isSparseMatrix },
|
|
2136
2890
|
{ name: "Matrix", test: isMatrix },
|
|
2137
2891
|
// Other types
|
|
2138
|
-
{ name: "Unit", test:
|
|
2892
|
+
{ name: "Unit", test: isUnit2 }
|
|
2139
2893
|
];
|
|
2140
2894
|
var MATHTS_CONVERSIONS = [
|
|
2141
2895
|
// -------------------------------------------------------------------------
|
|
@@ -2551,6 +3305,9 @@ var math = {
|
|
|
2551
3305
|
// src/index.ts
|
|
2552
3306
|
var VERSION = "0.1.0";
|
|
2553
3307
|
export {
|
|
3308
|
+
ALL_UNITS,
|
|
3309
|
+
BASE_UNITS,
|
|
3310
|
+
BEST_PREFIXES,
|
|
2554
3311
|
BIGNUMBER_E,
|
|
2555
3312
|
BIGNUMBER_LN10,
|
|
2556
3313
|
BIGNUMBER_LN2,
|
|
@@ -2565,6 +3322,9 @@ export {
|
|
|
2565
3322
|
COMPLEX_ZERO,
|
|
2566
3323
|
Complex,
|
|
2567
3324
|
DEFAULT_CONFIG,
|
|
3325
|
+
DERIVED_UNITS,
|
|
3326
|
+
DIMENSIONLESS,
|
|
3327
|
+
DimensionMismatchError,
|
|
2568
3328
|
FRACTION_HALF,
|
|
2569
3329
|
FRACTION_NEG_ONE,
|
|
2570
3330
|
FRACTION_ONE,
|
|
@@ -2576,12 +3336,19 @@ export {
|
|
|
2576
3336
|
I,
|
|
2577
3337
|
MATHTS_CONVERSIONS,
|
|
2578
3338
|
MATHTS_TYPES,
|
|
3339
|
+
SI_PREFIXES,
|
|
2579
3340
|
TypeRegistry,
|
|
3341
|
+
UNIT_ALIASES,
|
|
3342
|
+
Unit,
|
|
3343
|
+
UnitParseError,
|
|
2580
3344
|
VERSION,
|
|
2581
3345
|
create,
|
|
2582
3346
|
createFactory,
|
|
2583
3347
|
createMathTSTyped,
|
|
2584
3348
|
createTypedFunction,
|
|
3349
|
+
dim,
|
|
3350
|
+
getPrefix,
|
|
3351
|
+
getUnitDef,
|
|
2585
3352
|
initTypedWasm,
|
|
2586
3353
|
isArray,
|
|
2587
3354
|
isBigInt,
|
|
@@ -2599,7 +3366,8 @@ export {
|
|
|
2599
3366
|
isString,
|
|
2600
3367
|
isTypedWasmAvailable,
|
|
2601
3368
|
isUndefined,
|
|
2602
|
-
isUnit,
|
|
3369
|
+
isUnit2 as isUnit,
|
|
3370
|
+
isUnit as isUnitValue,
|
|
2603
3371
|
math,
|
|
2604
3372
|
mathTyped,
|
|
2605
3373
|
registerNativeTypes,
|