@openmbee/opensysml 0.9.1 → 0.10.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 +355 -33
- package/dist/browser/index.d.ts +9 -0
- package/dist/browser/index.js +16 -0
- package/dist/browser/wasm-worker.d.ts +1 -0
- package/dist/browser/wasm-worker.js +2 -0
- package/dist/browser/wasm.d.ts +17 -0
- package/dist/browser/wasm.js +94 -0
- package/dist/core/arguments.d.ts +4 -0
- package/dist/core/arguments.js +19 -0
- package/dist/core/capabilities.d.ts +90 -9
- package/dist/core/capabilities.js +111 -10
- package/dist/core/connection.d.ts +203 -2
- package/dist/core/connection.js +810 -17
- package/dist/core/conversion.d.ts +153 -0
- package/dist/core/conversion.js +191 -0
- package/dist/core/document.d.ts +188 -0
- package/dist/core/document.js +457 -0
- package/dist/core/edit.d.ts +446 -0
- package/dist/core/edit.js +1994 -0
- package/dist/core/engines.d.ts +64 -0
- package/dist/core/engines.js +74 -0
- package/dist/core/errors.d.ts +164 -2
- package/dist/core/errors.js +209 -3
- package/dist/core/exploration.d.ts +71 -0
- package/dist/core/exploration.js +116 -0
- package/dist/core/graphs.d.ts +15 -0
- package/dist/core/graphs.js +3 -0
- package/dist/core/index.d.ts +30 -9
- package/dist/core/index.js +14 -5
- package/dist/core/model.d.ts +168 -8
- package/dist/core/model.js +258 -31
- package/dist/core/package.d.ts +2 -0
- package/dist/core/package.js +2 -0
- package/dist/core/query.d.ts +50 -0
- package/dist/core/query.js +197 -0
- package/dist/core/render-view.d.ts +97 -0
- package/dist/core/render-view.js +120 -0
- package/dist/core/sources.d.ts +34 -0
- package/dist/core/sources.js +134 -0
- package/dist/core/status.d.ts +9 -2
- package/dist/core/status.js +24 -8
- package/dist/core/typed.d.ts +71 -0
- package/dist/core/typed.js +245 -0
- package/dist/core/values.d.ts +127 -8
- package/dist/core/values.js +536 -30
- package/dist/core/verdict.d.ts +271 -0
- package/dist/core/verdict.js +567 -0
- package/dist/core/wasm.d.ts +114 -0
- package/dist/core/wasm.js +514 -0
- package/dist/generated/sysml_pb.d.ts +1080 -22
- package/dist/generated/sysml_pb.js +200 -100
- package/dist/node/binary.d.ts +2 -0
- package/dist/node/binary.js +8 -6
- package/dist/node/generate-cli.d.ts +2 -0
- package/dist/node/generate-cli.js +10 -0
- package/dist/node/generate.d.ts +99 -0
- package/dist/node/generate.js +757 -0
- package/dist/node/index.d.ts +29 -2
- package/dist/node/index.js +211 -10
- package/dist/node/service.d.ts +5 -3
- package/dist/node/service.js +32 -26
- package/dist/node/wasm-source.d.ts +18 -0
- package/dist/node/wasm-source.js +103 -0
- package/dist/node/wasm-worker.d.ts +1 -0
- package/dist/node/wasm-worker.js +10 -0
- package/dist/node/wasm.d.ts +17 -0
- package/dist/node/wasm.js +63 -0
- package/package.json +19 -7
- package/release-digests.json +105 -0
package/dist/core/values.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// Values, quantities and verdicts as discriminated unions: a caller switches on
|
|
2
2
|
// `kind` and the compiler checks the switch is exhaustive.
|
|
3
3
|
import { create } from "@bufbuild/protobuf";
|
|
4
|
-
import { ArraySchema, ComplexSchema, EnumLiteralSchema, FailureReason, FunctionSchema, MeasurementRefSchema, MetaobjectSchema, QuantitySchema, TensorQuantitySchema, UnitFactorSchema, UnitTermSchema, ValueSchema, ValueSequenceSchema, ValueSetSchema, VectorQuantitySchema, VectorSchema, } from "../generated/sysml_pb.js";
|
|
5
|
-
import {
|
|
4
|
+
import { ArraySchema, ComplexSchema, EnumLiteralSchema, FailureReason, FunctionSchema, MeasurementRefSchema, MetaobjectSchema, QuantitySchema, RationalSchema, TensorQuantitySchema, UnitFactorSchema, UnitTermSchema, ValueSchema, ValueSequenceSchema, ValueSetSchema, VectorQuantitySchema, VectorSchema, } from "../generated/sysml_pb.js";
|
|
5
|
+
import { CAPABILITY_BIG_INT_VALUES, CAPABILITY_COMPLEX_VALUES, CAPABILITY_FUNCTION_VALUES, CAPABILITY_INFINITY_VALUE, CAPABILITY_MEASUREMENT_REFS, CAPABILITY_METAOBJECT_VALUES, CAPABILITY_RATIONAL_VALUES, CAPABILITY_SET_VALUES, CAPABILITY_STRUCTURED_VALUES, CAPABILITY_TENSOR_VALUES, requireCapability, upgradeRemedy, } from "./capabilities.js";
|
|
6
|
+
import { MalformedValueError, UnsupportedValueError } from "./errors.js";
|
|
6
7
|
/**
|
|
7
8
|
* Decodes a `sysml.Value` into the union.
|
|
8
9
|
*
|
|
@@ -22,6 +23,10 @@ export function decodeValue(value) {
|
|
|
22
23
|
switch (kind.case) {
|
|
23
24
|
case "intValue":
|
|
24
25
|
return { kind: "int", value: kind.value };
|
|
26
|
+
case "bigIntValue":
|
|
27
|
+
return { kind: "int", value: decodeBigInteger(kind.value) };
|
|
28
|
+
case "rationalValue":
|
|
29
|
+
return { kind: "rational", ...decodeRational(kind.value) };
|
|
25
30
|
case "realValue":
|
|
26
31
|
return { kind: "real", value: kind.value };
|
|
27
32
|
case "complex":
|
|
@@ -85,7 +90,8 @@ export function decodeValue(value) {
|
|
|
85
90
|
export function encodeValue(value) {
|
|
86
91
|
switch (value.kind) {
|
|
87
92
|
case "int":
|
|
88
|
-
|
|
93
|
+
case "rational":
|
|
94
|
+
return encodeMagnitude(value);
|
|
89
95
|
case "real":
|
|
90
96
|
return create(ValueSchema, { kind: { case: "realValue", value: value.value } });
|
|
91
97
|
case "complex":
|
|
@@ -180,15 +186,23 @@ export function encodeValue(value) {
|
|
|
180
186
|
}
|
|
181
187
|
}
|
|
182
188
|
/** Decodes a `sysml.Verdict` into the union. */
|
|
183
|
-
export function decodeVerdict(verdict) {
|
|
189
|
+
export function decodeVerdict(verdict, verifications = []) {
|
|
184
190
|
const subject = {
|
|
185
191
|
kind: verdict.kind,
|
|
186
192
|
elementId: verdict.elementId,
|
|
187
193
|
element: verdict.element,
|
|
188
194
|
...(verdict.instanceId === 0n ? {} : { instanceId: verdict.instanceId }),
|
|
189
195
|
...(verdict.instanceTypeId === "" ? {} : { instanceTypeId: verdict.instanceTypeId }),
|
|
196
|
+
...(verdict.requirementId === "" ? {} : { requirementId: verdict.requirementId }),
|
|
197
|
+
...(verdict.instancePath === "" ? {} : { instancePath: verdict.instancePath }),
|
|
190
198
|
};
|
|
191
199
|
const standing = decodeStanding(verdict);
|
|
200
|
+
const witness = verdict.witness.map((assignment) => ({
|
|
201
|
+
feature: assignment.feature,
|
|
202
|
+
value: decodeValue(assignment.value),
|
|
203
|
+
unit: assignment.unit,
|
|
204
|
+
exact: assignment.exact,
|
|
205
|
+
}));
|
|
192
206
|
if (verdict.error !== "") {
|
|
193
207
|
return {
|
|
194
208
|
kind: "undecided",
|
|
@@ -196,18 +210,42 @@ export function decodeVerdict(verdict) {
|
|
|
196
210
|
error: verdict.error,
|
|
197
211
|
cause: failureCause(verdict.failureReason),
|
|
198
212
|
standing,
|
|
213
|
+
question: verdict.question,
|
|
214
|
+
status: verdict.status,
|
|
215
|
+
witness,
|
|
216
|
+
verifications: [...verifications],
|
|
199
217
|
};
|
|
200
218
|
}
|
|
201
219
|
return verdict.holds
|
|
202
|
-
? {
|
|
203
|
-
|
|
220
|
+
? {
|
|
221
|
+
kind: "holds",
|
|
222
|
+
subject,
|
|
223
|
+
standing,
|
|
224
|
+
question: verdict.question,
|
|
225
|
+
status: verdict.status,
|
|
226
|
+
witness,
|
|
227
|
+
verifications: [...verifications],
|
|
228
|
+
}
|
|
229
|
+
: {
|
|
230
|
+
kind: "fails",
|
|
231
|
+
subject,
|
|
232
|
+
condition: verdict.condition,
|
|
233
|
+
standing,
|
|
234
|
+
question: verdict.question,
|
|
235
|
+
status: verdict.status,
|
|
236
|
+
witness,
|
|
237
|
+
verifications: [...verifications],
|
|
238
|
+
};
|
|
204
239
|
}
|
|
205
240
|
/** Reads the standing fields the `engines` capability adds to a verdict. */
|
|
206
241
|
export function decodeStanding(verdict) {
|
|
242
|
+
const bounds = verdict.bounds.map((b) => ({ name: b.name, limit: b.limit, reached: b.reached }));
|
|
207
243
|
return {
|
|
208
244
|
engine: verdict.engine,
|
|
209
245
|
strength: verdict.strength,
|
|
210
|
-
bounds
|
|
246
|
+
bounds,
|
|
247
|
+
reported: verdict.strength !== "",
|
|
248
|
+
reached: bounds.filter((bound) => bound.reached),
|
|
211
249
|
};
|
|
212
250
|
}
|
|
213
251
|
/** Names the enum the service reports for a failure it could not answer through. */
|
|
@@ -230,6 +268,8 @@ export function formatValue(value) {
|
|
|
230
268
|
switch (value.kind) {
|
|
231
269
|
case "int":
|
|
232
270
|
return value.value.toString();
|
|
271
|
+
case "rational":
|
|
272
|
+
return formatRational(value);
|
|
233
273
|
case "real":
|
|
234
274
|
return formatReal(value.value);
|
|
235
275
|
case "complex":
|
|
@@ -280,7 +320,37 @@ function formatReal(value) {
|
|
|
280
320
|
return Number.isInteger(value) ? value.toFixed(1) : value.toString();
|
|
281
321
|
}
|
|
282
322
|
function formatMagnitude(magnitude) {
|
|
283
|
-
|
|
323
|
+
switch (magnitude.kind) {
|
|
324
|
+
case "int":
|
|
325
|
+
return magnitude.value.toString();
|
|
326
|
+
case "rational":
|
|
327
|
+
return formatRational(magnitude);
|
|
328
|
+
case "real":
|
|
329
|
+
return formatReal(magnitude.value);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/** A terminating Rational as its decimal, `0.1`; any other as `numerator/denominator`, `1/3`. */
|
|
333
|
+
export function formatRational(value) {
|
|
334
|
+
let twos = 0;
|
|
335
|
+
let fives = 0;
|
|
336
|
+
let rest = value.denominator;
|
|
337
|
+
while (rest % 2n === 0n) {
|
|
338
|
+
rest /= 2n;
|
|
339
|
+
twos++;
|
|
340
|
+
}
|
|
341
|
+
while (rest % 5n === 0n) {
|
|
342
|
+
rest /= 5n;
|
|
343
|
+
fives++;
|
|
344
|
+
}
|
|
345
|
+
if (rest !== 1n) {
|
|
346
|
+
return `${value.numerator.toString()}/${value.denominator.toString()}`;
|
|
347
|
+
}
|
|
348
|
+
const places = Math.max(twos, fives, 1);
|
|
349
|
+
const scaled = (value.numerator * 10n ** BigInt(places)) / value.denominator;
|
|
350
|
+
const negative = scaled < 0n;
|
|
351
|
+
const digits = (negative ? -scaled : scaled).toString().padStart(places + 1, "0");
|
|
352
|
+
const point = digits.length - places;
|
|
353
|
+
return `${negative ? "-" : ""}${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
284
354
|
}
|
|
285
355
|
/** `Tensor(2, 2, 2)[1.0, …, 8.0][Pa]` when every component shares a unit; else each with its own. */
|
|
286
356
|
function formatTensorQuantity(tensor) {
|
|
@@ -304,9 +374,15 @@ function decodeQuantity(quantity) {
|
|
|
304
374
|
case "intMagnitude":
|
|
305
375
|
magnitude = { kind: "int", value: quantity.magnitude.value };
|
|
306
376
|
break;
|
|
377
|
+
case "bigIntMagnitude":
|
|
378
|
+
magnitude = { kind: "int", value: decodeBigInteger(quantity.magnitude.value) };
|
|
379
|
+
break;
|
|
307
380
|
case "realMagnitude":
|
|
308
381
|
magnitude = { kind: "real", value: quantity.magnitude.value };
|
|
309
382
|
break;
|
|
383
|
+
case "rationalMagnitude":
|
|
384
|
+
magnitude = { kind: "rational", ...decodeRational(quantity.magnitude.value) };
|
|
385
|
+
break;
|
|
310
386
|
default:
|
|
311
387
|
throw new MalformedValueError(`a quantity in [${quantity.unit}] has no magnitude`);
|
|
312
388
|
}
|
|
@@ -319,9 +395,7 @@ function decodeQuantity(quantity) {
|
|
|
319
395
|
}
|
|
320
396
|
function encodeQuantity(quantity) {
|
|
321
397
|
return create(QuantitySchema, {
|
|
322
|
-
magnitude: quantity.magnitude
|
|
323
|
-
? { case: "intMagnitude", value: quantity.magnitude.value }
|
|
324
|
-
: { case: "realMagnitude", value: quantity.magnitude.value },
|
|
398
|
+
magnitude: encodeQuantityMagnitude(quantity.magnitude),
|
|
325
399
|
unit: quantity.unit,
|
|
326
400
|
...(quantity.unitTerm === undefined ? {} : { unitTerm: encodeUnitTerm(quantity.unitTerm) }),
|
|
327
401
|
});
|
|
@@ -388,10 +462,191 @@ function formatUnitTerm(term) {
|
|
|
388
462
|
}
|
|
389
463
|
return parts.length === 0 ? "1" : parts.join("·");
|
|
390
464
|
}
|
|
465
|
+
/** A quantity magnitude as the wire's `Quantity.magnitude` oneof writes it. */
|
|
466
|
+
export function encodeQuantityMagnitude(magnitude) {
|
|
467
|
+
switch (magnitude.kind) {
|
|
468
|
+
case "real":
|
|
469
|
+
return { case: "realMagnitude", value: magnitude.value };
|
|
470
|
+
case "rational": {
|
|
471
|
+
return { case: "rationalMagnitude", value: encodeRational(magnitude) };
|
|
472
|
+
}
|
|
473
|
+
case "int":
|
|
474
|
+
return fitsInt64(magnitude.value)
|
|
475
|
+
? { case: "intMagnitude", value: magnitude.value }
|
|
476
|
+
: { case: "bigIntMagnitude", value: magnitude.value.toString() };
|
|
477
|
+
}
|
|
478
|
+
}
|
|
391
479
|
function encodeMagnitude(magnitude) {
|
|
392
|
-
|
|
480
|
+
if (magnitude.kind === "real") {
|
|
481
|
+
return create(ValueSchema, { kind: { case: "realValue", value: magnitude.value } });
|
|
482
|
+
}
|
|
483
|
+
if (magnitude.kind === "rational") {
|
|
484
|
+
return create(ValueSchema, { kind: { case: "rationalValue", value: encodeRational(magnitude) } });
|
|
485
|
+
}
|
|
486
|
+
return fitsInt64(magnitude.value)
|
|
393
487
|
? create(ValueSchema, { kind: { case: "intValue", value: magnitude.value } })
|
|
394
|
-
: create(ValueSchema, { kind: { case: "
|
|
488
|
+
: create(ValueSchema, { kind: { case: "bigIntValue", value: magnitude.value.toString() } });
|
|
489
|
+
}
|
|
490
|
+
const INT64_MIN = -(2n ** 63n);
|
|
491
|
+
const INT64_MAX = 2n ** 63n - 1n;
|
|
492
|
+
/** Whether an Integer travels as `int_value`; one beyond int64 travels as `big_int_value`. */
|
|
493
|
+
export function fitsInt64(value) {
|
|
494
|
+
return value >= INT64_MIN && value <= INT64_MAX;
|
|
495
|
+
}
|
|
496
|
+
/** Reads the decimal of a `big_int_value` or `big_int_magnitude`. */
|
|
497
|
+
export function decodeBigInteger(text) {
|
|
498
|
+
if (!/^-?\d+$/.test(text)) {
|
|
499
|
+
throw new MalformedValueError(`a big Integer ${JSON.stringify(text)} is not decimal`);
|
|
500
|
+
}
|
|
501
|
+
return BigInt(text);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Rewrites in place each Rational arm of a wire value a double holds exactly as
|
|
505
|
+
* that double: the form a service without `rational_values` reads. A Rational
|
|
506
|
+
* no double holds is left as it is; a collection's elements are values of their own.
|
|
507
|
+
*/
|
|
508
|
+
export function rationalsAsReals(value) {
|
|
509
|
+
switch (value.kind.case) {
|
|
510
|
+
case "rationalValue": {
|
|
511
|
+
const double = wireRationalAsDouble(value.kind.value);
|
|
512
|
+
if (double !== undefined) {
|
|
513
|
+
value.kind = { case: "realValue", value: double };
|
|
514
|
+
}
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
case "quantity":
|
|
518
|
+
quantityRationalAsReal(value.kind.value);
|
|
519
|
+
return;
|
|
520
|
+
case "vector":
|
|
521
|
+
value.kind.value.components.forEach(rationalsAsReals);
|
|
522
|
+
return;
|
|
523
|
+
case "vectorQuantity":
|
|
524
|
+
value.kind.value.components.forEach(quantityRationalAsReal);
|
|
525
|
+
return;
|
|
526
|
+
case "tensorQuantity":
|
|
527
|
+
value.kind.value.components.forEach(quantityRationalAsReal);
|
|
528
|
+
return;
|
|
529
|
+
default:
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/** Rewrites a quantity's Rational magnitude a double holds exactly as `real_magnitude`. */
|
|
534
|
+
export function quantityRationalAsReal(quantity) {
|
|
535
|
+
if (quantity.magnitude.case === "rationalMagnitude") {
|
|
536
|
+
const double = wireRationalAsDouble(quantity.magnitude.value);
|
|
537
|
+
if (double !== undefined) {
|
|
538
|
+
quantity.magnitude = { case: "realMagnitude", value: double };
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
function wireRationalAsDouble(wire) {
|
|
543
|
+
return rationalAsDouble({ numerator: BigInt(wire.numerator), denominator: BigInt(wire.denominator) });
|
|
544
|
+
}
|
|
545
|
+
/** The `Rational` message of an exact Rational, numerator and denominator in full. */
|
|
546
|
+
export function encodeRational(value) {
|
|
547
|
+
return create(RationalSchema, {
|
|
548
|
+
numerator: value.numerator.toString(),
|
|
549
|
+
denominator: value.denominator.toString(),
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Reads a `rational_value` or `rational_magnitude`.
|
|
554
|
+
*
|
|
555
|
+
* @throws {MalformedValueError} for one not in lowest terms over a positive
|
|
556
|
+
* denominator, or one a double holds, which travels as `real_value`.
|
|
557
|
+
*/
|
|
558
|
+
export function decodeRational(rational) {
|
|
559
|
+
const numerator = decodeBigInteger(rational.numerator);
|
|
560
|
+
const denominator = decodeBigInteger(rational.denominator);
|
|
561
|
+
const text = `${rational.numerator}/${rational.denominator}`;
|
|
562
|
+
if (denominator <= 0n || gcd(numerator, denominator) !== 1n) {
|
|
563
|
+
throw new MalformedValueError(`a Rational ${text} is not in lowest terms over a positive denominator`);
|
|
564
|
+
}
|
|
565
|
+
if (rationalAsDouble({ numerator, denominator }) !== undefined) {
|
|
566
|
+
throw new MalformedValueError(`a Rational ${text} is a double, which travels as real_value`);
|
|
567
|
+
}
|
|
568
|
+
return { numerator, denominator };
|
|
569
|
+
}
|
|
570
|
+
/** The exact Rational `numerator/denominator`, reduced; throws a RangeError over a zero denominator. */
|
|
571
|
+
export function rational(numerator, denominator = 1n) {
|
|
572
|
+
if (denominator === 0n) {
|
|
573
|
+
throw new RangeError("a Rational's denominator is not zero");
|
|
574
|
+
}
|
|
575
|
+
const sign = denominator < 0n ? -1n : 1n;
|
|
576
|
+
const divisor = gcd(numerator, denominator);
|
|
577
|
+
return { kind: "rational", numerator: (sign * numerator) / divisor, denominator: (sign * denominator) / divisor };
|
|
578
|
+
}
|
|
579
|
+
function gcd(a, b) {
|
|
580
|
+
a = a < 0n ? -a : a;
|
|
581
|
+
b = b < 0n ? -b : b;
|
|
582
|
+
while (b !== 0n) {
|
|
583
|
+
[a, b] = [b, a % b];
|
|
584
|
+
}
|
|
585
|
+
return a;
|
|
586
|
+
}
|
|
587
|
+
function bitLength(n) {
|
|
588
|
+
return n === 0n ? 0 : (n < 0n ? -n : n).toString(2).length;
|
|
589
|
+
}
|
|
590
|
+
/** The exact Rational a finite double holds. */
|
|
591
|
+
export function rationalOfDouble(x) {
|
|
592
|
+
if (!Number.isFinite(x)) {
|
|
593
|
+
throw new RangeError(`${x} is no Rational`);
|
|
594
|
+
}
|
|
595
|
+
let whole = x;
|
|
596
|
+
let denominator = 1n;
|
|
597
|
+
while (!Number.isInteger(whole)) {
|
|
598
|
+
whole *= 2;
|
|
599
|
+
denominator *= 2n;
|
|
600
|
+
}
|
|
601
|
+
return rational(BigInt(whole), denominator);
|
|
602
|
+
}
|
|
603
|
+
/** The double that holds a reduced Rational exactly, or undefined where none does. */
|
|
604
|
+
export function rationalAsDouble(value) {
|
|
605
|
+
const { numerator, denominator } = value;
|
|
606
|
+
if ((denominator & (denominator - 1n)) !== 0n) {
|
|
607
|
+
return undefined;
|
|
608
|
+
}
|
|
609
|
+
const shift = bitLength(denominator) - 1;
|
|
610
|
+
let odd = numerator;
|
|
611
|
+
let zeros = 0;
|
|
612
|
+
while (odd !== 0n && odd % 2n === 0n) {
|
|
613
|
+
odd /= 2n;
|
|
614
|
+
zeros++;
|
|
615
|
+
}
|
|
616
|
+
if (bitLength(odd) > 53 || zeros - shift < -1074 || bitLength(numerator) - shift > 1024) {
|
|
617
|
+
return undefined;
|
|
618
|
+
}
|
|
619
|
+
return scaleByPowerOfTwo(Number(odd), zeros - shift);
|
|
620
|
+
}
|
|
621
|
+
/** The double nearest the Rational: how a Real reads one. */
|
|
622
|
+
export function rationalToNumber(value) {
|
|
623
|
+
const exact = rationalAsDouble(value);
|
|
624
|
+
if (exact !== undefined) {
|
|
625
|
+
return exact;
|
|
626
|
+
}
|
|
627
|
+
const negative = value.numerator < 0n;
|
|
628
|
+
const magnitude = negative ? -value.numerator : value.numerator;
|
|
629
|
+
// A quotient of at least 55 bits, its last bit sticky, rounds once to 53.
|
|
630
|
+
const shift = 55 - (bitLength(magnitude) - bitLength(value.denominator));
|
|
631
|
+
const num = shift >= 0 ? magnitude << BigInt(shift) : magnitude;
|
|
632
|
+
const den = shift >= 0 ? value.denominator : value.denominator << BigInt(-shift);
|
|
633
|
+
let quotient = num / den;
|
|
634
|
+
if (num % den !== 0n) {
|
|
635
|
+
quotient |= 1n;
|
|
636
|
+
}
|
|
637
|
+
const result = scaleByPowerOfTwo(Number(quotient), -shift);
|
|
638
|
+
return negative ? -result : result;
|
|
639
|
+
}
|
|
640
|
+
function scaleByPowerOfTwo(x, exponent) {
|
|
641
|
+
while (exponent > 1000) {
|
|
642
|
+
x *= 2 ** 1000;
|
|
643
|
+
exponent -= 1000;
|
|
644
|
+
}
|
|
645
|
+
while (exponent < -1000) {
|
|
646
|
+
x *= 2 ** -1000;
|
|
647
|
+
exponent += 1000;
|
|
648
|
+
}
|
|
649
|
+
return x * 2 ** exponent;
|
|
395
650
|
}
|
|
396
651
|
/** The flattened size the dimensions demand, refusing a dimension that is not positive. */
|
|
397
652
|
function checkShape(what, dimensions, elementCount) {
|
|
@@ -441,6 +696,7 @@ export function valuesEqual(a, b) {
|
|
|
441
696
|
}
|
|
442
697
|
switch (a.kind) {
|
|
443
698
|
case "int":
|
|
699
|
+
case "rational":
|
|
444
700
|
case "real":
|
|
445
701
|
case "complex":
|
|
446
702
|
return numbersEqual(a, b);
|
|
@@ -531,18 +787,40 @@ function numbersEqual(a, b) {
|
|
|
531
787
|
}
|
|
532
788
|
b = { kind: "real", value: b.value.real };
|
|
533
789
|
}
|
|
534
|
-
if (a.kind === "
|
|
535
|
-
|
|
536
|
-
return a.value === b.value;
|
|
537
|
-
return b.kind === "real" && realIsInt(b.value, a.value);
|
|
790
|
+
if (a.kind === "real" && b.kind === "real") {
|
|
791
|
+
return a.value === b.value;
|
|
538
792
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
793
|
+
// A Rational meets a Real at Real precision, as the service compares them.
|
|
794
|
+
if (a.kind === "rational" && b.kind === "real") {
|
|
795
|
+
return rationalToNumber(a) === b.value;
|
|
796
|
+
}
|
|
797
|
+
if (a.kind === "real" && b.kind === "rational") {
|
|
798
|
+
return a.value === rationalToNumber(b);
|
|
799
|
+
}
|
|
800
|
+
const x = exactOf(a);
|
|
801
|
+
const y = exactOf(b);
|
|
802
|
+
return x !== undefined && y !== undefined && x.numerator * y.denominator === y.numerator * x.denominator;
|
|
542
803
|
}
|
|
543
|
-
//
|
|
544
|
-
function
|
|
545
|
-
|
|
804
|
+
// The exact number a value holds, a finite Real's double included, never rounding.
|
|
805
|
+
function exactOf(value) {
|
|
806
|
+
switch (value.kind) {
|
|
807
|
+
case "int":
|
|
808
|
+
return { numerator: value.value, denominator: 1n };
|
|
809
|
+
case "rational":
|
|
810
|
+
return value;
|
|
811
|
+
case "real":
|
|
812
|
+
return Number.isFinite(value.value) ? doubleAsRational(value.value) : undefined;
|
|
813
|
+
default:
|
|
814
|
+
return undefined;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
function doubleAsRational(x) {
|
|
818
|
+
let denominator = 1n;
|
|
819
|
+
while (!Number.isInteger(x)) {
|
|
820
|
+
x *= 2;
|
|
821
|
+
denominator *= 2n;
|
|
822
|
+
}
|
|
823
|
+
return { numerator: BigInt(x), denominator };
|
|
546
824
|
}
|
|
547
825
|
function magnitudesEqual(a, b) {
|
|
548
826
|
return a.length === b.length && a.every((m, i) => numbersEqual(m, b[i]));
|
|
@@ -564,18 +842,18 @@ function quantitiesEqual(a, b) {
|
|
|
564
842
|
zeroScale(b.unitTerm)) {
|
|
565
843
|
return false;
|
|
566
844
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
wholeScale(b.unitTerm)) {
|
|
845
|
+
const x = a.magnitude.kind === "real" ? undefined : exactOf(a.magnitude);
|
|
846
|
+
const y = b.magnitude.kind === "real" ? undefined : exactOf(b.magnitude);
|
|
847
|
+
if (x !== undefined && y !== undefined && wholeScale(a.unitTerm) && wholeScale(b.unitTerm)) {
|
|
571
848
|
// m₁·n₁/d₁ = m₂·n₂/d₂ exactly, cross-multiplied in bigint.
|
|
572
|
-
return (
|
|
573
|
-
|
|
849
|
+
return (x.numerator * y.denominator * BigInt(a.unitTerm.scaleNum) * BigInt(b.unitTerm.scaleDen) ===
|
|
850
|
+
y.numerator * x.denominator * BigInt(b.unitTerm.scaleNum) * BigInt(a.unitTerm.scaleDen));
|
|
574
851
|
}
|
|
575
852
|
return baseMagnitude(a.magnitude, a.unitTerm) === baseMagnitude(b.magnitude, b.unitTerm);
|
|
576
853
|
}
|
|
577
854
|
function baseMagnitude(magnitude, term) {
|
|
578
|
-
|
|
855
|
+
const value = magnitude.kind === "rational" ? rationalToNumber(magnitude) : Number(magnitude.value);
|
|
856
|
+
return (value * term.scaleNum) / term.scaleDen;
|
|
579
857
|
}
|
|
580
858
|
// The reduction as base unit → exponent, repeated base units summed and
|
|
581
859
|
// cancelled ones dropped, so two reductions compare however they are listed.
|
|
@@ -639,8 +917,12 @@ function decodeVector(vector) {
|
|
|
639
917
|
switch (component.kind.case) {
|
|
640
918
|
case "intValue":
|
|
641
919
|
return { kind: "int", value: component.kind.value };
|
|
920
|
+
case "bigIntValue":
|
|
921
|
+
return { kind: "int", value: decodeBigInteger(component.kind.value) };
|
|
642
922
|
case "realValue":
|
|
643
923
|
return { kind: "real", value: component.kind.value };
|
|
924
|
+
case "rationalValue":
|
|
925
|
+
return { kind: "rational", ...decodeRational(component.kind.value) };
|
|
644
926
|
default:
|
|
645
927
|
throw new MalformedValueError(`a vector component is ${component.kind.case ?? "empty"}, not a number`);
|
|
646
928
|
}
|
|
@@ -685,3 +967,227 @@ function encodeEnumLiteral(literal) {
|
|
|
685
967
|
value: literal.value === undefined ? undefined : encodeValue(literal.value),
|
|
686
968
|
});
|
|
687
969
|
}
|
|
970
|
+
/** Refuses an instance or `self` id outside int64, the width of the wire's id fields. */
|
|
971
|
+
function checkInt64(value) {
|
|
972
|
+
if (!fitsInt64(value)) {
|
|
973
|
+
throw new RangeError(`value out of range: ${value.toString()}`);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Encodes a caller-facing value for the wire, requiring of `info` each
|
|
978
|
+
* capability a value kind needs before the call is sent — exactly the checks
|
|
979
|
+
* Python's `Connection._python_to_value` makes. A `SysMLValue` encodes as
|
|
980
|
+
* itself; a boolean, string, bigint and number encode as the scalar arms; a
|
|
981
|
+
* JavaScript array and `Set` encode as a sequence and a set.
|
|
982
|
+
*/
|
|
983
|
+
export function toValue(input, info) {
|
|
984
|
+
const probe = input;
|
|
985
|
+
if (typeof probe === "object" && probe !== null && "$typeName" in probe) {
|
|
986
|
+
return probe;
|
|
987
|
+
}
|
|
988
|
+
return encodeInput(normalizeInput(input), info);
|
|
989
|
+
}
|
|
990
|
+
/** The `SysMLValue` one input names, collection elements converted the same way. */
|
|
991
|
+
function normalizeInput(input) {
|
|
992
|
+
switch (typeof input) {
|
|
993
|
+
case "boolean":
|
|
994
|
+
return { kind: "boolean", value: input };
|
|
995
|
+
case "bigint":
|
|
996
|
+
return { kind: "int", value: input };
|
|
997
|
+
case "number":
|
|
998
|
+
return { kind: "real", value: input };
|
|
999
|
+
case "string":
|
|
1000
|
+
return { kind: "string", value: input };
|
|
1001
|
+
case "undefined":
|
|
1002
|
+
throw new RangeError("unsupported input type: undefined");
|
|
1003
|
+
case "function":
|
|
1004
|
+
case "symbol":
|
|
1005
|
+
case "object":
|
|
1006
|
+
break;
|
|
1007
|
+
}
|
|
1008
|
+
if (input === null) {
|
|
1009
|
+
return { kind: "null", reason: "" };
|
|
1010
|
+
}
|
|
1011
|
+
if (Array.isArray(input)) {
|
|
1012
|
+
return { kind: "sequence", elements: input.map(normalizeInput) };
|
|
1013
|
+
}
|
|
1014
|
+
if (input instanceof Set) {
|
|
1015
|
+
return { kind: "set", elements: [...input].map(normalizeInput) };
|
|
1016
|
+
}
|
|
1017
|
+
const value = input;
|
|
1018
|
+
switch (value.kind) {
|
|
1019
|
+
case "sequence":
|
|
1020
|
+
return { kind: "sequence", elements: value.elements.map(normalizeInput) };
|
|
1021
|
+
case "set":
|
|
1022
|
+
return { kind: "set", elements: value.elements.map(normalizeInput) };
|
|
1023
|
+
case "array":
|
|
1024
|
+
return {
|
|
1025
|
+
kind: "array",
|
|
1026
|
+
dimensions: value.dimensions,
|
|
1027
|
+
elements: value.elements.map(normalizeInput),
|
|
1028
|
+
};
|
|
1029
|
+
case "enum":
|
|
1030
|
+
return value.value.value === undefined
|
|
1031
|
+
? value
|
|
1032
|
+
: { kind: "enum", value: { ...value.value, value: normalizeInput(value.value.value) } };
|
|
1033
|
+
case "unset":
|
|
1034
|
+
throw new RangeError("an unset value cannot be sent as an input; it is a value the service answers, not one it takes");
|
|
1035
|
+
default:
|
|
1036
|
+
if (typeof value.kind === "string") {
|
|
1037
|
+
return value;
|
|
1038
|
+
}
|
|
1039
|
+
throw new RangeError(`unsupported input type: ${typeof input}`);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
function encodeInput(value, info) {
|
|
1043
|
+
requireInput(value, info);
|
|
1044
|
+
switch (value.kind) {
|
|
1045
|
+
case "sequence":
|
|
1046
|
+
return create(ValueSchema, {
|
|
1047
|
+
kind: {
|
|
1048
|
+
case: "sequence",
|
|
1049
|
+
value: create(ValueSequenceSchema, {
|
|
1050
|
+
elements: value.elements.map((element) => encodeInput(element, info)),
|
|
1051
|
+
}),
|
|
1052
|
+
},
|
|
1053
|
+
});
|
|
1054
|
+
case "array":
|
|
1055
|
+
checkShape("an array", value.dimensions, value.elements.length);
|
|
1056
|
+
return create(ValueSchema, {
|
|
1057
|
+
kind: {
|
|
1058
|
+
case: "array",
|
|
1059
|
+
value: create(ArraySchema, {
|
|
1060
|
+
dimensions: value.dimensions,
|
|
1061
|
+
elements: value.elements.map((element) => encodeInput(element, info)),
|
|
1062
|
+
}),
|
|
1063
|
+
},
|
|
1064
|
+
});
|
|
1065
|
+
case "set":
|
|
1066
|
+
return create(ValueSchema, {
|
|
1067
|
+
kind: {
|
|
1068
|
+
case: "set",
|
|
1069
|
+
value: create(ValueSetSchema, {
|
|
1070
|
+
elements: uniqueMembers(value.elements).map((element) => encodeInput(element, info)),
|
|
1071
|
+
}),
|
|
1072
|
+
},
|
|
1073
|
+
});
|
|
1074
|
+
default: {
|
|
1075
|
+
const wire = encodeValue(value);
|
|
1076
|
+
if (!info.has(CAPABILITY_RATIONAL_VALUES)) {
|
|
1077
|
+
rationalsAsReals(wire);
|
|
1078
|
+
}
|
|
1079
|
+
return wire;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
/** The capabilities one input value needs of the service that will decode it. */
|
|
1084
|
+
export function requireInput(value, info) {
|
|
1085
|
+
const require = (capability) => {
|
|
1086
|
+
requireCapability(info, capability, upgradeRemedy(capability));
|
|
1087
|
+
};
|
|
1088
|
+
const requireMagnitudes = (magnitudes) => {
|
|
1089
|
+
if (magnitudes.some((magnitude) => magnitude.kind === "int" && !fitsInt64(magnitude.value))) {
|
|
1090
|
+
require(CAPABILITY_BIG_INT_VALUES);
|
|
1091
|
+
}
|
|
1092
|
+
if (magnitudes.some((magnitude) => magnitude.kind === "rational" && rationalAsDouble(magnitude) === undefined)) {
|
|
1093
|
+
require(CAPABILITY_RATIONAL_VALUES);
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
switch (value.kind) {
|
|
1097
|
+
case "int":
|
|
1098
|
+
case "rational":
|
|
1099
|
+
requireMagnitudes([value]);
|
|
1100
|
+
return;
|
|
1101
|
+
case "complex":
|
|
1102
|
+
require(CAPABILITY_COMPLEX_VALUES);
|
|
1103
|
+
return;
|
|
1104
|
+
case "quantity":
|
|
1105
|
+
requireMagnitudes([value.magnitude]);
|
|
1106
|
+
checkReduction(`quantity in [${value.unit}]`, value.unit, value.unitTerm);
|
|
1107
|
+
return;
|
|
1108
|
+
case "measurementRef": {
|
|
1109
|
+
require(CAPABILITY_MEASUREMENT_REFS);
|
|
1110
|
+
const unitTerm = value.unitTerm;
|
|
1111
|
+
if (value.unit === "" && (value.unitId ?? "") === "" && unitTerm === undefined) {
|
|
1112
|
+
throw new UnsupportedValueError("measurement reference naming no unit");
|
|
1113
|
+
}
|
|
1114
|
+
// The wire needs the reduction on every reference, id-only ones too;
|
|
1115
|
+
// encodeMeasurementRef reads unitTerm unconditionally.
|
|
1116
|
+
if (unitTerm === undefined) {
|
|
1117
|
+
throw new UnsupportedValueError(`measurement reference ${value.unit || value.unitId} carries no reduction to base units, so the service cannot tell what it ` +
|
|
1118
|
+
"measures: build it from a unit the service sent, or from one the model declares");
|
|
1119
|
+
}
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
case "instance":
|
|
1123
|
+
checkInt64(value.id);
|
|
1124
|
+
return;
|
|
1125
|
+
case "unset":
|
|
1126
|
+
throw new RangeError("an unset value cannot be sent as an input; it is a value the service answers, not one it takes");
|
|
1127
|
+
case "function":
|
|
1128
|
+
require(CAPABILITY_FUNCTION_VALUES);
|
|
1129
|
+
if (value.selfId !== undefined) {
|
|
1130
|
+
checkInt64(value.selfId);
|
|
1131
|
+
}
|
|
1132
|
+
return;
|
|
1133
|
+
case "metaobject":
|
|
1134
|
+
require(CAPABILITY_METAOBJECT_VALUES);
|
|
1135
|
+
return;
|
|
1136
|
+
case "array":
|
|
1137
|
+
require(CAPABILITY_STRUCTURED_VALUES);
|
|
1138
|
+
value.elements.forEach((element) => {
|
|
1139
|
+
requireInput(element, info);
|
|
1140
|
+
});
|
|
1141
|
+
return;
|
|
1142
|
+
case "vector":
|
|
1143
|
+
require(CAPABILITY_STRUCTURED_VALUES);
|
|
1144
|
+
requireMagnitudes(value.components);
|
|
1145
|
+
return;
|
|
1146
|
+
case "vectorQuantity":
|
|
1147
|
+
require(CAPABILITY_STRUCTURED_VALUES);
|
|
1148
|
+
requireMagnitudes(value.components.map((component) => component.magnitude));
|
|
1149
|
+
value.components.forEach((component) => {
|
|
1150
|
+
checkReduction(`quantity in [${component.unit}]`, component.unit, component.unitTerm);
|
|
1151
|
+
});
|
|
1152
|
+
return;
|
|
1153
|
+
case "set":
|
|
1154
|
+
require(CAPABILITY_SET_VALUES);
|
|
1155
|
+
value.elements.forEach((element) => {
|
|
1156
|
+
requireInput(element, info);
|
|
1157
|
+
});
|
|
1158
|
+
return;
|
|
1159
|
+
case "tensorQuantity":
|
|
1160
|
+
require(CAPABILITY_TENSOR_VALUES);
|
|
1161
|
+
requireMagnitudes(value.components.map((component) => component.magnitude));
|
|
1162
|
+
value.components.forEach((component) => {
|
|
1163
|
+
checkReduction(`quantity in [${component.unit}]`, component.unit, component.unitTerm);
|
|
1164
|
+
});
|
|
1165
|
+
return;
|
|
1166
|
+
case "infinity":
|
|
1167
|
+
require(CAPABILITY_INFINITY_VALUE);
|
|
1168
|
+
return;
|
|
1169
|
+
case "enum":
|
|
1170
|
+
if (value.value.value !== undefined) {
|
|
1171
|
+
requireInput(value.value.value, info);
|
|
1172
|
+
}
|
|
1173
|
+
return;
|
|
1174
|
+
case "sequence":
|
|
1175
|
+
value.elements.forEach((element) => {
|
|
1176
|
+
requireInput(element, info);
|
|
1177
|
+
});
|
|
1178
|
+
return;
|
|
1179
|
+
default:
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Refuses a unit named without its reduction to base units — the service
|
|
1185
|
+
* decides commensurability over the reduction and rejects a unit sent without
|
|
1186
|
+
* one; an unnamed unit means dimension one and carries none.
|
|
1187
|
+
*/
|
|
1188
|
+
function checkReduction(what, unit, unitTerm) {
|
|
1189
|
+
if (unit !== "" && unitTerm === undefined) {
|
|
1190
|
+
throw new UnsupportedValueError(`${what} carries no reduction to base units, so the service cannot tell what it ` +
|
|
1191
|
+
"measures: build it from a unit the service sent, or from one the model declares");
|
|
1192
|
+
}
|
|
1193
|
+
}
|