eyeling 1.6.4 → 1.6.6
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/examples/output/cobalt-kepler-kitchen.n3 +3544 -3543
- package/examples/output/complex.n3 +25 -24
- package/examples/output/control-system.n3 +21 -20
- package/examples/output/cranberry-calculus.n3 +509 -508
- package/examples/output/drone-corridor-planner.n3 +154 -153
- package/examples/output/ev-roundtrip-planner.n3 +81 -80
- package/examples/output/gps.n3 +15 -14
- package/examples/output/jade-eigen-loom.n3 +2033 -2032
- package/examples/output/light-eaters.n3 +51 -50
- package/examples/output/lldm.n3 +244 -243
- package/examples/output/math-builtins-tests.n3 +40 -40
- package/examples/output/oslo-steps-library-scholarly.n3 +197 -196
- package/examples/output/oslo-steps-workflow-composition.n3 +29 -28
- package/examples/output/pi.n3 +5 -4
- package/examples/output/ruby-runge-workshop.n3 +106 -105
- package/examples/output/saffron-slopeworks.n3 +455 -454
- package/examples/output/spectral-week.n3 +81 -80
- package/examples/output/topaz-markov-mill.n3 +1618 -1617
- package/examples/output/ultramarine-simpson-forge.n3 +1213 -1212
- package/eyeling.js +315 -134
- package/package.json +1 -1
package/eyeling.js
CHANGED
|
@@ -2709,9 +2709,9 @@ function numEqualTerm(t, n, eps = 1e-9) {
|
|
|
2709
2709
|
}
|
|
2710
2710
|
|
|
2711
2711
|
function numericDatatypeFromLex(lex) {
|
|
2712
|
-
if (/[eE]/.test(lex)) return
|
|
2713
|
-
if (lex.includes('.')) return
|
|
2714
|
-
return
|
|
2712
|
+
if (/[eE]/.test(lex)) return XSD_DOUBLE_DT;
|
|
2713
|
+
if (lex.includes('.')) return XSD_DECIMAL_DT;
|
|
2714
|
+
return XSD_INTEGER_DT;
|
|
2715
2715
|
}
|
|
2716
2716
|
|
|
2717
2717
|
function parseNumericLiteralInfo(t) {
|
|
@@ -2724,21 +2724,21 @@ function parseNumericLiteralInfo(t) {
|
|
|
2724
2724
|
let lexStr;
|
|
2725
2725
|
|
|
2726
2726
|
if (dt2 !== null) {
|
|
2727
|
-
|
|
2727
|
+
// Accept all xsd numeric datatypes; normalize integer-derived to xsd:integer.
|
|
2728
|
+
if (!isXsdNumericDatatype(dt2)) return null;
|
|
2729
|
+
if (isXsdIntegerDatatype(dt2)) dt2 = XSD_INTEGER_DT;
|
|
2728
2730
|
lexStr = stripQuotes(lex);
|
|
2729
2731
|
} else {
|
|
2732
|
+
// Untyped numeric token (N3/Turtle numeric literal)
|
|
2730
2733
|
if (typeof v !== 'string') return null;
|
|
2731
2734
|
if (v.startsWith('"')) return null; // exclude quoted strings
|
|
2732
2735
|
if (!/^[+-]?(?:\d+\.\d*|\.\d+|\d+)(?:[eE][+-]?\d+)?$/.test(v)) return null;
|
|
2733
2736
|
|
|
2734
|
-
|
|
2735
|
-
else if (v.includes('.')) dt2 = XSD_NS + 'decimal';
|
|
2736
|
-
else dt2 = XSD_NS + 'integer';
|
|
2737
|
-
|
|
2737
|
+
dt2 = numericDatatypeFromLex(v);
|
|
2738
2738
|
lexStr = v;
|
|
2739
2739
|
}
|
|
2740
2740
|
|
|
2741
|
-
if (dt2 ===
|
|
2741
|
+
if (dt2 === XSD_INTEGER_DT) {
|
|
2742
2742
|
try {
|
|
2743
2743
|
return { dt: dt2, kind: 'bigint', value: BigInt(lexStr), lexStr };
|
|
2744
2744
|
} catch {
|
|
@@ -2751,6 +2751,59 @@ function parseNumericLiteralInfo(t) {
|
|
|
2751
2751
|
return { dt: dt2, kind: 'number', value: num, lexStr };
|
|
2752
2752
|
}
|
|
2753
2753
|
|
|
2754
|
+
function numericRank(dt) {
|
|
2755
|
+
if (dt === XSD_INTEGER_DT) return 0;
|
|
2756
|
+
if (dt === XSD_DECIMAL_DT) return 1;
|
|
2757
|
+
if (dt === XSD_FLOAT_DT) return 2;
|
|
2758
|
+
if (dt === XSD_DOUBLE_DT) return 3;
|
|
2759
|
+
return -1;
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
function numericDatatypeOfTerm(t) {
|
|
2763
|
+
if (!(t instanceof Literal)) return null;
|
|
2764
|
+
const [lex, dt] = literalParts(t.value);
|
|
2765
|
+
|
|
2766
|
+
if (dt !== null) {
|
|
2767
|
+
if (!isXsdNumericDatatype(dt)) return null;
|
|
2768
|
+
if (isXsdIntegerDatatype(dt)) return XSD_INTEGER_DT;
|
|
2769
|
+
if (dt === XSD_DECIMAL_DT || dt === XSD_FLOAT_DT || dt === XSD_DOUBLE_DT) return dt;
|
|
2770
|
+
return null;
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
// Untyped numeric token
|
|
2774
|
+
if (!looksLikeUntypedNumericTokenLex(lex)) return null;
|
|
2775
|
+
return numericDatatypeFromLex(lex);
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
function commonNumericDatatype(terms, outTerm) {
|
|
2779
|
+
let r = 0;
|
|
2780
|
+
const all = Array.isArray(terms) ? terms.slice() : [];
|
|
2781
|
+
if (outTerm) all.push(outTerm);
|
|
2782
|
+
|
|
2783
|
+
for (const t of all) {
|
|
2784
|
+
const dt = numericDatatypeOfTerm(t);
|
|
2785
|
+
if (!dt) continue;
|
|
2786
|
+
const rr = numericRank(dt);
|
|
2787
|
+
if (rr > r) r = rr;
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
if (r === 3) return XSD_DOUBLE_DT;
|
|
2791
|
+
if (r === 2) return XSD_FLOAT_DT;
|
|
2792
|
+
if (r === 1) return XSD_DECIMAL_DT;
|
|
2793
|
+
return XSD_INTEGER_DT;
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
function makeNumericOutputLiteral(val, dt) {
|
|
2797
|
+
if (dt === XSD_INTEGER_DT) {
|
|
2798
|
+
if (typeof val === 'bigint') return new Literal(val.toString());
|
|
2799
|
+
if (Number.isInteger(val)) return new Literal(String(val));
|
|
2800
|
+
// If a non-integer sneaks in, promote to decimal.
|
|
2801
|
+
return new Literal(`"${formatNum(val)}"^^<${XSD_DECIMAL_DT}>`);
|
|
2802
|
+
}
|
|
2803
|
+
const lex = typeof val === 'bigint' ? val.toString() : formatNum(val);
|
|
2804
|
+
return new Literal(`"${lex}"^^<${dt}>`);
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2754
2807
|
function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
|
|
2755
2808
|
const sIsUnbound = g.s instanceof Var || g.s instanceof Blank;
|
|
2756
2809
|
const oIsUnbound = g.o instanceof Var || g.o instanceof Blank;
|
|
@@ -2762,9 +2815,13 @@ function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
|
|
|
2762
2815
|
if (a !== null) {
|
|
2763
2816
|
const outVal = forwardFn(a);
|
|
2764
2817
|
if (!Number.isFinite(outVal)) return [];
|
|
2818
|
+
|
|
2819
|
+
let outDt = commonNumericDatatype([g.s], g.o);
|
|
2820
|
+
if (outDt === XSD_INTEGER_DT && !Number.isInteger(outVal)) outDt = XSD_DECIMAL_DT;
|
|
2821
|
+
|
|
2765
2822
|
if (g.o instanceof Var) {
|
|
2766
2823
|
const s2 = { ...subst };
|
|
2767
|
-
s2[g.o.name] =
|
|
2824
|
+
s2[g.o.name] = makeNumericOutputLiteral(outVal, outDt);
|
|
2768
2825
|
return [s2];
|
|
2769
2826
|
}
|
|
2770
2827
|
if (g.o instanceof Blank) return [{ ...subst }];
|
|
@@ -2776,9 +2833,13 @@ function evalUnaryMathRel(g, subst, forwardFn, inverseFn /* may be null */) {
|
|
|
2776
2833
|
if (b !== null && typeof inverseFn === 'function') {
|
|
2777
2834
|
const inVal = inverseFn(b);
|
|
2778
2835
|
if (!Number.isFinite(inVal)) return [];
|
|
2836
|
+
|
|
2837
|
+
let inDt = commonNumericDatatype([g.o], g.s);
|
|
2838
|
+
if (inDt === XSD_INTEGER_DT && !Number.isInteger(inVal)) inDt = XSD_DECIMAL_DT;
|
|
2839
|
+
|
|
2779
2840
|
if (g.s instanceof Var) {
|
|
2780
2841
|
const s2 = { ...subst };
|
|
2781
|
-
s2[g.s.name] =
|
|
2842
|
+
s2[g.s.name] = makeNumericOutputLiteral(inVal, inDt);
|
|
2782
2843
|
return [s2];
|
|
2783
2844
|
}
|
|
2784
2845
|
if (g.s instanceof Blank) return [{ ...subst }];
|
|
@@ -2959,151 +3020,225 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
2959
3020
|
}
|
|
2960
3021
|
|
|
2961
3022
|
// math:sum
|
|
3023
|
+
// Schema: ( $s.i+ )+ math:sum $o-
|
|
2962
3024
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'sum') {
|
|
2963
|
-
if (g.s instanceof ListTerm
|
|
2964
|
-
|
|
2965
|
-
|
|
3025
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length < 2) return [];
|
|
3026
|
+
const xs = g.s.elems;
|
|
3027
|
+
|
|
3028
|
+
const dtOut0 = commonNumericDatatype(xs, g.o);
|
|
3029
|
+
|
|
3030
|
+
// Exact integer mode
|
|
3031
|
+
if (dtOut0 === XSD_INTEGER_DT) {
|
|
3032
|
+
let total = 0n;
|
|
2966
3033
|
for (const t of xs) {
|
|
2967
|
-
const v =
|
|
3034
|
+
const v = parseIntLiteral(t);
|
|
2968
3035
|
if (v === null) return [];
|
|
2969
|
-
|
|
2970
|
-
}
|
|
2971
|
-
|
|
2972
|
-
let lit;
|
|
2973
|
-
const allBig = values.every((v) => typeof v === 'bigint');
|
|
2974
|
-
if (allBig) {
|
|
2975
|
-
let total = 0n;
|
|
2976
|
-
for (const v of values) total += v;
|
|
2977
|
-
lit = new Literal(total.toString());
|
|
2978
|
-
} else {
|
|
2979
|
-
let total = 0.0;
|
|
2980
|
-
for (const v of values) {
|
|
2981
|
-
total += typeof v === 'bigint' ? Number(v) : v;
|
|
2982
|
-
}
|
|
2983
|
-
lit = new Literal(formatNum(total));
|
|
3036
|
+
total += v;
|
|
2984
3037
|
}
|
|
2985
3038
|
|
|
2986
3039
|
if (g.o instanceof Var) {
|
|
2987
3040
|
const s2 = { ...subst };
|
|
2988
|
-
s2[g.o.name] =
|
|
3041
|
+
s2[g.o.name] = makeNumericOutputLiteral(total, XSD_INTEGER_DT);
|
|
2989
3042
|
return [s2];
|
|
2990
3043
|
}
|
|
2991
|
-
|
|
2992
|
-
|
|
3044
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3045
|
+
|
|
3046
|
+
const oi = parseIntLiteral(g.o);
|
|
3047
|
+
if (oi !== null && oi === total) return [{ ...subst }];
|
|
3048
|
+
|
|
3049
|
+
// Fallback numeric compare
|
|
3050
|
+
if (numEqualTerm(g.o, Number(total))) return [{ ...subst }];
|
|
3051
|
+
return [];
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
// Numeric mode (decimal/float/double)
|
|
3055
|
+
let total = 0.0;
|
|
3056
|
+
for (const t of xs) {
|
|
3057
|
+
const v = parseNum(t);
|
|
3058
|
+
if (v === null) return [];
|
|
3059
|
+
total += v;
|
|
3060
|
+
}
|
|
3061
|
+
|
|
3062
|
+
let dtOut = dtOut0;
|
|
3063
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(total)) dtOut = XSD_DECIMAL_DT;
|
|
3064
|
+
const lit = makeNumericOutputLiteral(total, dtOut);
|
|
3065
|
+
|
|
3066
|
+
if (g.o instanceof Var) {
|
|
3067
|
+
const s2 = { ...subst };
|
|
3068
|
+
s2[g.o.name] = lit;
|
|
3069
|
+
return [s2];
|
|
2993
3070
|
}
|
|
3071
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3072
|
+
if (numEqualTerm(g.o, total)) return [{ ...subst }];
|
|
2994
3073
|
return [];
|
|
2995
3074
|
}
|
|
2996
3075
|
|
|
2997
3076
|
// math:product
|
|
3077
|
+
// Schema: ( $s.i+ )+ math:product $o-
|
|
2998
3078
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'product') {
|
|
2999
|
-
if (g.s instanceof ListTerm
|
|
3000
|
-
|
|
3001
|
-
|
|
3079
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length < 2) return [];
|
|
3080
|
+
const xs = g.s.elems;
|
|
3081
|
+
|
|
3082
|
+
const dtOut0 = commonNumericDatatype(xs, g.o);
|
|
3083
|
+
|
|
3084
|
+
// Exact integer mode
|
|
3085
|
+
if (dtOut0 === XSD_INTEGER_DT) {
|
|
3086
|
+
let prod = 1n;
|
|
3002
3087
|
for (const t of xs) {
|
|
3003
|
-
const v =
|
|
3088
|
+
const v = parseIntLiteral(t);
|
|
3004
3089
|
if (v === null) return [];
|
|
3005
|
-
|
|
3006
|
-
}
|
|
3007
|
-
|
|
3008
|
-
let lit;
|
|
3009
|
-
const allBig = values.every((v) => typeof v === 'bigint');
|
|
3010
|
-
if (allBig) {
|
|
3011
|
-
let prod = 1n;
|
|
3012
|
-
for (const v of values) prod *= v;
|
|
3013
|
-
lit = new Literal(prod.toString());
|
|
3014
|
-
} else {
|
|
3015
|
-
let prod = 1.0;
|
|
3016
|
-
for (const v of values) {
|
|
3017
|
-
prod *= typeof v === 'bigint' ? Number(v) : v;
|
|
3018
|
-
}
|
|
3019
|
-
lit = new Literal(formatNum(prod));
|
|
3090
|
+
prod *= v;
|
|
3020
3091
|
}
|
|
3021
3092
|
|
|
3022
3093
|
if (g.o instanceof Var) {
|
|
3023
3094
|
const s2 = { ...subst };
|
|
3024
|
-
s2[g.o.name] =
|
|
3095
|
+
s2[g.o.name] = makeNumericOutputLiteral(prod, XSD_INTEGER_DT);
|
|
3025
3096
|
return [s2];
|
|
3026
3097
|
}
|
|
3027
|
-
if (g.o instanceof
|
|
3028
|
-
|
|
3029
|
-
|
|
3098
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3099
|
+
|
|
3100
|
+
const oi = parseIntLiteral(g.o);
|
|
3101
|
+
if (oi !== null && oi === prod) return [{ ...subst }];
|
|
3102
|
+
if (numEqualTerm(g.o, Number(prod))) return [{ ...subst }];
|
|
3030
3103
|
return [];
|
|
3031
3104
|
}
|
|
3105
|
+
|
|
3106
|
+
// Numeric mode (decimal/float/double)
|
|
3107
|
+
let prod = 1.0;
|
|
3108
|
+
for (const t of xs) {
|
|
3109
|
+
const v = parseNum(t);
|
|
3110
|
+
if (v === null) return [];
|
|
3111
|
+
prod *= v;
|
|
3112
|
+
}
|
|
3113
|
+
|
|
3114
|
+
let dtOut = dtOut0;
|
|
3115
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(prod)) dtOut = XSD_DECIMAL_DT;
|
|
3116
|
+
const lit = makeNumericOutputLiteral(prod, dtOut);
|
|
3117
|
+
|
|
3118
|
+
if (g.o instanceof Var) {
|
|
3119
|
+
const s2 = { ...subst };
|
|
3120
|
+
s2[g.o.name] = lit;
|
|
3121
|
+
return [s2];
|
|
3122
|
+
}
|
|
3123
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3124
|
+
if (numEqualTerm(g.o, prod)) return [{ ...subst }];
|
|
3125
|
+
return [];
|
|
3032
3126
|
}
|
|
3033
3127
|
|
|
3034
3128
|
// math:difference
|
|
3129
|
+
// Schema: ( $s.1+ $s.2+ )+ math:difference $o-
|
|
3035
3130
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'difference') {
|
|
3036
|
-
if (g.s instanceof ListTerm
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3131
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3132
|
+
const [a0, b0] = g.s.elems;
|
|
3133
|
+
|
|
3134
|
+
// 1) Date/datetime difference -> duration (needed for examples/age.n3)
|
|
3135
|
+
const aDt = parseDatetimeLike(a0);
|
|
3136
|
+
const bDt = parseDatetimeLike(b0);
|
|
3137
|
+
if (aDt !== null && bDt !== null) {
|
|
3138
|
+
const diffSecs = (aDt.getTime() - bDt.getTime()) / 1000.0;
|
|
3139
|
+
const durTerm = formatDurationLiteralFromSeconds(diffSecs);
|
|
3140
|
+
if (g.o instanceof Var) {
|
|
3141
|
+
const s2 = { ...subst };
|
|
3142
|
+
s2[g.o.name] = durTerm;
|
|
3143
|
+
return [s2];
|
|
3144
|
+
}
|
|
3145
|
+
const s2 = unifyTerm(g.o, durTerm, subst);
|
|
3146
|
+
return s2 !== null ? [s2] : [];
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
// 2) Date/datetime minus duration/seconds -> dateTime (keeps older functionality)
|
|
3150
|
+
if (aDt !== null) {
|
|
3151
|
+
const secs = parseNumOrDuration(b0);
|
|
3152
|
+
if (secs !== null) {
|
|
3153
|
+
const outSecs = aDt.getTime() / 1000.0 - secs;
|
|
3154
|
+
const lex = utcIsoDateTimeStringFromEpochSeconds(outSecs);
|
|
3155
|
+
const lit = new Literal(`"${lex}"^^<${XSD_NS}dateTime>`);
|
|
3045
3156
|
if (g.o instanceof Var) {
|
|
3046
3157
|
const s2 = { ...subst };
|
|
3047
3158
|
s2[g.o.name] = lit;
|
|
3048
3159
|
return [s2];
|
|
3049
|
-
} else {
|
|
3050
|
-
const s2 = unifyTerm(g.o, lit, subst);
|
|
3051
|
-
return s2 !== null ? [s2] : [];
|
|
3052
3160
|
}
|
|
3161
|
+
const s2 = unifyTerm(g.o, lit, subst);
|
|
3162
|
+
return s2 !== null ? [s2] : [];
|
|
3053
3163
|
}
|
|
3164
|
+
}
|
|
3054
3165
|
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
if (g.o instanceof Literal && g.o.value === formatNum(c)) {
|
|
3066
|
-
return [{ ...subst }];
|
|
3067
|
-
}
|
|
3166
|
+
// 3) Exact integer difference (BigInt)
|
|
3167
|
+
const ai = parseIntLiteral(a0);
|
|
3168
|
+
const bi = parseIntLiteral(b0);
|
|
3169
|
+
if (ai !== null && bi !== null) {
|
|
3170
|
+
const ci = ai - bi;
|
|
3171
|
+
const lit = new Literal(ci.toString());
|
|
3172
|
+
if (g.o instanceof Var) {
|
|
3173
|
+
const s2 = { ...subst };
|
|
3174
|
+
s2[g.o.name] = lit;
|
|
3175
|
+
return [s2];
|
|
3068
3176
|
}
|
|
3177
|
+
const s2 = unifyTerm(g.o, lit, subst);
|
|
3178
|
+
return s2 !== null ? [s2] : [];
|
|
3179
|
+
}
|
|
3069
3180
|
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3181
|
+
// 4) Numeric difference (your “typed output + numeric compare” version)
|
|
3182
|
+
const a = parseNum(a0);
|
|
3183
|
+
const b = parseNum(b0);
|
|
3184
|
+
if (a === null || b === null) return [];
|
|
3185
|
+
|
|
3186
|
+
const c = a - b;
|
|
3187
|
+
if (!Number.isFinite(c)) return [];
|
|
3188
|
+
|
|
3189
|
+
// If you added commonNumericDatatype/makeNumericOutputLiteral, keep using them:
|
|
3190
|
+
if (typeof commonNumericDatatype === 'function' && typeof makeNumericOutputLiteral === 'function') {
|
|
3191
|
+
let dtOut = commonNumericDatatype([a0, b0], g.o);
|
|
3192
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(c)) dtOut = XSD_DECIMAL_DT;
|
|
3193
|
+
const lit = makeNumericOutputLiteral(c, dtOut);
|
|
3194
|
+
|
|
3195
|
+
if (g.o instanceof Var) {
|
|
3196
|
+
const s2 = { ...subst };
|
|
3197
|
+
s2[g.o.name] = lit;
|
|
3198
|
+
return [s2];
|
|
3084
3199
|
}
|
|
3200
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3201
|
+
if (numEqualTerm(g.o, c)) return [{ ...subst }];
|
|
3085
3202
|
return [];
|
|
3086
3203
|
}
|
|
3204
|
+
|
|
3205
|
+
// Fallback (if you *don’t* have those helpers yet):
|
|
3206
|
+
const lit = new Literal(formatNum(c));
|
|
3207
|
+
if (g.o instanceof Var) {
|
|
3208
|
+
const s2 = { ...subst };
|
|
3209
|
+
s2[g.o.name] = lit;
|
|
3210
|
+
return [s2];
|
|
3211
|
+
}
|
|
3212
|
+
const s2 = unifyTerm(g.o, lit, subst);
|
|
3213
|
+
return s2 !== null ? [s2] : [];
|
|
3087
3214
|
}
|
|
3088
3215
|
|
|
3089
3216
|
// math:quotient
|
|
3217
|
+
// Schema: ( $s.1+ $s.2+ )+ math:quotient $o-
|
|
3090
3218
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'quotient') {
|
|
3091
|
-
if (g.s instanceof ListTerm
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3219
|
+
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3220
|
+
const [a0, b0] = g.s.elems;
|
|
3221
|
+
|
|
3222
|
+
const a = parseNum(a0);
|
|
3223
|
+
const b = parseNum(b0);
|
|
3224
|
+
if (a === null || b === null) return [];
|
|
3225
|
+
if (!Number.isFinite(a) || !Number.isFinite(b) || b === 0) return [];
|
|
3226
|
+
|
|
3227
|
+
const c = a / b;
|
|
3228
|
+
if (!Number.isFinite(c)) return [];
|
|
3229
|
+
|
|
3230
|
+
let dtOut = commonNumericDatatype([a0, b0], g.o);
|
|
3231
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(c)) dtOut = XSD_DECIMAL_DT;
|
|
3232
|
+
const lit = makeNumericOutputLiteral(c, dtOut);
|
|
3233
|
+
|
|
3234
|
+
if (g.o instanceof Var) {
|
|
3235
|
+
const s2 = { ...subst };
|
|
3236
|
+
s2[g.o.name] = lit;
|
|
3237
|
+
return [s2];
|
|
3106
3238
|
}
|
|
3239
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3240
|
+
if (numEqualTerm(g.o, c)) return [{ ...subst }];
|
|
3241
|
+
return [];
|
|
3107
3242
|
}
|
|
3108
3243
|
|
|
3109
3244
|
// math:integerQuotient
|
|
@@ -3150,30 +3285,43 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3150
3285
|
// math:exponentiation
|
|
3151
3286
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'exponentiation') {
|
|
3152
3287
|
if (g.s instanceof ListTerm && g.s.elems.length === 2) {
|
|
3153
|
-
const
|
|
3154
|
-
const
|
|
3155
|
-
|
|
3288
|
+
const baseTerm = g.s.elems[0];
|
|
3289
|
+
const expTerm = g.s.elems[1];
|
|
3290
|
+
|
|
3291
|
+
const a = parseNum(baseTerm);
|
|
3156
3292
|
let b = null;
|
|
3157
|
-
if (a !== null
|
|
3293
|
+
if (a !== null) b = parseNum(expTerm);
|
|
3158
3294
|
|
|
3295
|
+
// Forward mode: base and exponent are numeric
|
|
3159
3296
|
if (a !== null && b !== null) {
|
|
3160
3297
|
const cVal = a ** b;
|
|
3298
|
+
if (!Number.isFinite(cVal)) return [];
|
|
3299
|
+
|
|
3300
|
+
let dtOut = commonNumericDatatype([baseTerm, expTerm], g.o);
|
|
3301
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(cVal)) dtOut = XSD_DECIMAL_DT;
|
|
3302
|
+
const lit = makeNumericOutputLiteral(cVal, dtOut);
|
|
3303
|
+
|
|
3161
3304
|
if (g.o instanceof Var) {
|
|
3162
3305
|
const s2 = { ...subst };
|
|
3163
|
-
s2[g.o.name] =
|
|
3306
|
+
s2[g.o.name] = lit;
|
|
3164
3307
|
return [s2];
|
|
3165
3308
|
}
|
|
3166
|
-
if (
|
|
3167
|
-
|
|
3168
|
-
}
|
|
3309
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3310
|
+
if (numEqualTerm(g.o, cVal)) return [{ ...subst }];
|
|
3169
3311
|
}
|
|
3170
3312
|
|
|
3171
|
-
//
|
|
3172
|
-
|
|
3313
|
+
// Inverse mode: solve exponent
|
|
3314
|
+
const c = parseNum(g.o);
|
|
3315
|
+
if (a !== null && expTerm instanceof Var && c !== null) {
|
|
3173
3316
|
if (a > 0.0 && a !== 1.0 && c > 0.0) {
|
|
3174
3317
|
const bVal = Math.log(c) / Math.log(a);
|
|
3318
|
+
if (!Number.isFinite(bVal)) return [];
|
|
3319
|
+
|
|
3320
|
+
let dtB = commonNumericDatatype([baseTerm, g.o], expTerm);
|
|
3321
|
+
if (dtB === XSD_INTEGER_DT && !Number.isInteger(bVal)) dtB = XSD_DECIMAL_DT;
|
|
3322
|
+
|
|
3175
3323
|
const s2 = { ...subst };
|
|
3176
|
-
s2[
|
|
3324
|
+
s2[expTerm.name] = makeNumericOutputLiteral(bVal, dtB);
|
|
3177
3325
|
return [s2];
|
|
3178
3326
|
}
|
|
3179
3327
|
}
|
|
@@ -3184,15 +3332,21 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3184
3332
|
// math:absoluteValue
|
|
3185
3333
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'absoluteValue') {
|
|
3186
3334
|
const a = parseNum(g.s);
|
|
3187
|
-
if (a
|
|
3335
|
+
if (a === null) return [];
|
|
3336
|
+
|
|
3337
|
+
const outVal = Math.abs(a);
|
|
3338
|
+
if (!Number.isFinite(outVal)) return [];
|
|
3339
|
+
|
|
3340
|
+
let dtOut = commonNumericDatatype([g.s], g.o);
|
|
3341
|
+
if (dtOut === XSD_INTEGER_DT && !Number.isInteger(outVal)) dtOut = XSD_DECIMAL_DT;
|
|
3342
|
+
|
|
3343
|
+
if (g.o instanceof Var) {
|
|
3188
3344
|
const s2 = { ...subst };
|
|
3189
|
-
s2[g.o.name] =
|
|
3345
|
+
s2[g.o.name] = makeNumericOutputLiteral(outVal, dtOut);
|
|
3190
3346
|
return [s2];
|
|
3191
3347
|
}
|
|
3192
|
-
|
|
3193
|
-
if (
|
|
3194
|
-
if (Math.abs(Math.abs(a) - b) < 1e-9) return [{ ...subst }];
|
|
3195
|
-
}
|
|
3348
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3349
|
+
if (numEqualTerm(g.o, outVal)) return [{ ...subst }];
|
|
3196
3350
|
return [];
|
|
3197
3351
|
}
|
|
3198
3352
|
|
|
@@ -3258,20 +3412,47 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
|
|
|
3258
3412
|
// Schema: ( $a $b ) math:remainder $r
|
|
3259
3413
|
if (g.p instanceof Iri && g.p.value === MATH_NS + 'remainder') {
|
|
3260
3414
|
if (!(g.s instanceof ListTerm) || g.s.elems.length !== 2) return [];
|
|
3261
|
-
const
|
|
3262
|
-
|
|
3263
|
-
|
|
3415
|
+
const [a0, b0] = g.s.elems;
|
|
3416
|
+
|
|
3417
|
+
// Prefer exact integer arithmetic (BigInt)
|
|
3418
|
+
const ai = parseIntLiteral(a0);
|
|
3419
|
+
const bi = parseIntLiteral(b0);
|
|
3420
|
+
if (ai !== null && bi !== null) {
|
|
3421
|
+
if (bi === 0n) return [];
|
|
3422
|
+
const r = ai % bi;
|
|
3423
|
+
const lit = makeNumericOutputLiteral(r, XSD_INTEGER_DT);
|
|
3424
|
+
|
|
3425
|
+
if (g.o instanceof Var) {
|
|
3426
|
+
const s2 = { ...subst };
|
|
3427
|
+
s2[g.o.name] = lit;
|
|
3428
|
+
return [s2];
|
|
3429
|
+
}
|
|
3430
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3431
|
+
|
|
3432
|
+
const oi = parseIntLiteral(g.o);
|
|
3433
|
+
if (oi !== null && oi === r) return [{ ...subst }];
|
|
3434
|
+
if (numEqualTerm(g.o, Number(r))) return [{ ...subst }];
|
|
3435
|
+
return [];
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
// Fallback: allow Number literals that still represent integers
|
|
3439
|
+
const a = parseNum(a0);
|
|
3440
|
+
const b = parseNum(b0);
|
|
3441
|
+
if (a === null || b === null) return [];
|
|
3442
|
+
if (!Number.isFinite(a) || !Number.isFinite(b) || b === 0) return [];
|
|
3443
|
+
if (!Number.isInteger(a) || !Number.isInteger(b)) return [];
|
|
3444
|
+
|
|
3264
3445
|
const rVal = a % b;
|
|
3265
|
-
|
|
3266
|
-
const lit = new Literal(formatNum(rVal));
|
|
3446
|
+
const lit = makeNumericOutputLiteral(rVal, XSD_INTEGER_DT);
|
|
3267
3447
|
|
|
3268
3448
|
if (g.o instanceof Var) {
|
|
3269
3449
|
const s2 = { ...subst };
|
|
3270
3450
|
s2[g.o.name] = lit;
|
|
3271
3451
|
return [s2];
|
|
3272
3452
|
}
|
|
3273
|
-
|
|
3274
|
-
|
|
3453
|
+
if (g.o instanceof Blank) return [{ ...subst }];
|
|
3454
|
+
if (numEqualTerm(g.o, rVal)) return [{ ...subst }];
|
|
3455
|
+
return [];
|
|
3275
3456
|
}
|
|
3276
3457
|
|
|
3277
3458
|
// math:rounded
|