@hviana/sema 0.1.3 → 0.1.5

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.
Files changed (123) hide show
  1. package/dist/example/demo.js +25 -25
  2. package/dist/example/train_base.d.ts +21 -19
  3. package/dist/example/train_base.js +1952 -1684
  4. package/dist/src/alphabet.d.ts +3 -3
  5. package/dist/src/alphabet.js +27 -24
  6. package/dist/src/alu/src/alu.d.ts +194 -169
  7. package/dist/src/alu/src/alu.js +414 -374
  8. package/dist/src/alu/src/expr.d.ts +43 -36
  9. package/dist/src/alu/src/expr.js +278 -252
  10. package/dist/src/alu/src/index.d.ts +98 -9
  11. package/dist/src/alu/src/index.js +63 -9
  12. package/dist/src/alu/src/kernel-arith.d.ts +5 -1
  13. package/dist/src/alu/src/kernel-arith.js +285 -195
  14. package/dist/src/alu/src/kernel-bits.d.ts +5 -1
  15. package/dist/src/alu/src/kernel-bits.js +115 -81
  16. package/dist/src/alu/src/kernel-logic.js +63 -33
  17. package/dist/src/alu/src/kernel-nd.js +195 -155
  18. package/dist/src/alu/src/kernel-numeric.d.ts +80 -16
  19. package/dist/src/alu/src/kernel-numeric.js +331 -259
  20. package/dist/src/alu/src/operation.d.ts +132 -112
  21. package/dist/src/alu/src/operation.js +163 -149
  22. package/dist/src/alu/src/parser.d.ts +175 -168
  23. package/dist/src/alu/src/parser.js +421 -382
  24. package/dist/src/alu/src/resonance.d.ts +25 -16
  25. package/dist/src/alu/src/resonance.js +56 -49
  26. package/dist/src/alu/src/text.d.ts +7 -3
  27. package/dist/src/alu/src/text.js +37 -31
  28. package/dist/src/alu/src/value.d.ts +14 -14
  29. package/dist/src/alu/src/value.js +160 -150
  30. package/dist/src/alu/test/alu.test.js +656 -543
  31. package/dist/src/bytes.d.ts +5 -1
  32. package/dist/src/bytes.js +40 -33
  33. package/dist/src/config.d.ts +102 -102
  34. package/dist/src/config.js +83 -83
  35. package/dist/src/derive/src/deduction.d.ts +62 -60
  36. package/dist/src/derive/src/deduction.js +109 -103
  37. package/dist/src/derive/src/index.d.ts +7 -1
  38. package/dist/src/derive/src/priority-queue.d.ts +8 -8
  39. package/dist/src/derive/src/priority-queue.js +61 -57
  40. package/dist/src/derive/src/rewrite.d.ts +18 -15
  41. package/dist/src/derive/src/rewrite.js +67 -59
  42. package/dist/src/derive/src/trie.d.ts +56 -56
  43. package/dist/src/derive/src/trie.js +179 -175
  44. package/dist/src/derive/test/derive.test.js +100 -96
  45. package/dist/src/extension.d.ts +16 -13
  46. package/dist/src/geometry.d.ts +42 -17
  47. package/dist/src/geometry.js +266 -235
  48. package/dist/src/index.d.ts +17 -2
  49. package/dist/src/index.js +7 -1
  50. package/dist/src/ingest-cache.d.ts +30 -25
  51. package/dist/src/ingest-cache.js +127 -108
  52. package/dist/src/mind/articulation.d.ts +5 -1
  53. package/dist/src/mind/articulation.js +112 -77
  54. package/dist/src/mind/attention.d.ts +108 -40
  55. package/dist/src/mind/attention.js +871 -749
  56. package/dist/src/mind/canonical.d.ts +19 -4
  57. package/dist/src/mind/canonical.js +31 -27
  58. package/dist/src/mind/graph-search.d.ts +225 -201
  59. package/dist/src/mind/graph-search.js +821 -742
  60. package/dist/src/mind/index.d.ts +10 -2
  61. package/dist/src/mind/junction.d.ts +58 -31
  62. package/dist/src/mind/junction.js +237 -172
  63. package/dist/src/mind/learning.d.ts +52 -16
  64. package/dist/src/mind/learning.js +165 -143
  65. package/dist/src/mind/match.d.ts +69 -20
  66. package/dist/src/mind/match.js +318 -259
  67. package/dist/src/mind/mechanisms/alu.js +16 -16
  68. package/dist/src/mind/mechanisms/cast.d.ts +13 -9
  69. package/dist/src/mind/mechanisms/cast.js +456 -363
  70. package/dist/src/mind/mechanisms/confluence.d.ts +12 -8
  71. package/dist/src/mind/mechanisms/confluence.js +183 -152
  72. package/dist/src/mind/mechanisms/cover.d.ts +8 -2
  73. package/dist/src/mind/mechanisms/cover.js +210 -148
  74. package/dist/src/mind/mechanisms/extraction.d.ts +34 -8
  75. package/dist/src/mind/mechanisms/extraction.js +288 -234
  76. package/dist/src/mind/mechanisms/recall.d.ts +10 -6
  77. package/dist/src/mind/mechanisms/recall.js +185 -126
  78. package/dist/src/mind/mind.d.ts +154 -129
  79. package/dist/src/mind/mind.js +288 -258
  80. package/dist/src/mind/pipeline-mechanism.d.ts +124 -112
  81. package/dist/src/mind/pipeline-mechanism.js +172 -161
  82. package/dist/src/mind/pipeline.d.ts +14 -4
  83. package/dist/src/mind/pipeline.js +189 -125
  84. package/dist/src/mind/primitives.d.ts +32 -8
  85. package/dist/src/mind/primitives.js +117 -99
  86. package/dist/src/mind/rationale.d.ts +98 -86
  87. package/dist/src/mind/rationale.js +121 -113
  88. package/dist/src/mind/reasoning.d.ts +13 -2
  89. package/dist/src/mind/reasoning.js +166 -129
  90. package/dist/src/mind/recognition.d.ts +4 -1
  91. package/dist/src/mind/recognition.js +208 -183
  92. package/dist/src/mind/resonance.d.ts +22 -5
  93. package/dist/src/mind/resonance.js +0 -0
  94. package/dist/src/mind/trace.d.ts +25 -6
  95. package/dist/src/mind/trace.js +58 -50
  96. package/dist/src/mind/traverse.d.ts +58 -16
  97. package/dist/src/mind/traverse.js +357 -304
  98. package/dist/src/mind/types.d.ts +127 -120
  99. package/dist/src/mind/types.js +69 -60
  100. package/dist/src/rabitq-hnsw/src/database.d.ts +179 -177
  101. package/dist/src/rabitq-hnsw/src/database.js +359 -342
  102. package/dist/src/rabitq-hnsw/src/heap.d.ts +12 -12
  103. package/dist/src/rabitq-hnsw/src/heap.js +80 -75
  104. package/dist/src/rabitq-hnsw/src/hnsw.d.ts +103 -103
  105. package/dist/src/rabitq-hnsw/src/hnsw.js +454 -428
  106. package/dist/src/rabitq-hnsw/src/index.d.ts +6 -1
  107. package/dist/src/rabitq-hnsw/src/prng.d.ts +10 -10
  108. package/dist/src/rabitq-hnsw/src/prng.js +27 -25
  109. package/dist/src/rabitq-hnsw/src/rabitq.d.ts +69 -69
  110. package/dist/src/rabitq-hnsw/src/rabitq.js +262 -246
  111. package/dist/src/rabitq-hnsw/src/store.d.ts +132 -132
  112. package/dist/src/rabitq-hnsw/src/store.js +843 -752
  113. package/dist/src/rabitq-hnsw/test/hnsw.test.js +1054 -805
  114. package/dist/src/sema.d.ts +35 -0
  115. package/dist/src/sema.js +77 -0
  116. package/dist/src/store-sqlite.d.ts +166 -144
  117. package/dist/src/store-sqlite.js +660 -578
  118. package/dist/src/store.d.ts +630 -593
  119. package/dist/src/store.js +1581 -1440
  120. package/dist/src/vec.d.ts +9 -5
  121. package/dist/src/vec.js +73 -61
  122. package/package.json +2 -2
  123. package/src/sema.ts +102 -0
@@ -36,65 +36,73 @@ import { lightestDerivation } from "./deduction.js";
36
36
  * start there. No quadratic span structure.
37
37
  */
38
38
  export function coverSequence(length, candidates) {
39
- if (length <= 0)
40
- return { spans: [], covered: 0, uncovered: 0 };
41
- // Candidates indexed by where they start, and by their exact (start,end) edge
42
- // for reconstruction. Both are O(|candidates|) to build and to read.
43
- const byStart = Array.from({ length }, () => []);
44
- const byEdge = new Map();
45
- let maxWeight = 1;
46
- for (const c of candidates) {
47
- if (c.end <= c.start || c.start < 0 || c.end > length)
48
- continue;
49
- const w = c.weight ?? 1;
50
- if (w > maxWeight)
51
- maxWeight = w;
52
- byStart[c.start].push(c);
53
- const edge = c.start * (length + 1) + c.end;
54
- const prev = byEdge.get(edge);
55
- if (!prev || w < (prev.weight ?? 1))
56
- byEdge.set(edge, c);
39
+ if (length <= 0) {
40
+ return { spans: [], covered: 0, uncovered: 0 };
41
+ }
42
+ // Candidates indexed by where they start, and by their exact (start,end) edge
43
+ // for reconstruction. Both are O(|candidates|) to build and to read.
44
+ const byStart = Array.from({ length }, () => []);
45
+ const byEdge = new Map();
46
+ let maxWeight = 1;
47
+ for (const c of candidates) {
48
+ if (c.end <= c.start || c.start < 0 || c.end > length) {
49
+ continue;
57
50
  }
58
- // One uncovered symbol must outweigh any sum of span weights, so coverage is
59
- // strictly the primary objective and weight only breaks ties.
60
- const skipCost = maxWeight * length + 1;
61
- const system = {
62
- key: (p) => "" + p,
63
- axioms: () => [{ item: 0, cost: 0 }],
64
- isGoal: (p) => p === length,
65
- // No nonzero admissible bound is available here: a single candidate can
66
- // cover the whole remainder for unit cost, so any per-symbol estimate would
67
- // overestimate. With h = 0 this is exact Knuth/Dijkstra over the frontier —
68
- // optimal, and linear in the positions. (The A* outside bound is for systems
69
- // whose remaining cost can be genuinely lower-bounded; see the engine.)
70
- *rules(p) {
71
- if (p >= length)
72
- return;
73
- yield { premises: [p], conclusion: p + 1, cost: skipCost }; // leave uncovered
74
- for (const c of byStart[p]) {
75
- yield { premises: [p], conclusion: c.end, cost: c.weight ?? 1 };
76
- }
77
- },
78
- };
79
- const best = lightestDerivation(system);
80
- if (!best)
81
- return { spans: [], covered: 0, uncovered: length };
82
- // Walk the frontier chain back to the axiom; an edge p→q is a chosen span iff
83
- // a candidate spans exactly [p, q) (a covering edge is always cheaper than the
84
- // skips it replaces, so it appears on the optimal path wherever it is used).
85
- const spans = [];
86
- let node = best;
87
- while (node && node.rule) {
88
- const to = node.item;
89
- const from = node.premises[0].item;
90
- const span = byEdge.get(from * (length + 1) + to);
91
- if (span)
92
- spans.push(span);
93
- node = node.premises[0];
51
+ const w = c.weight ?? 1;
52
+ if (w > maxWeight) {
53
+ maxWeight = w;
94
54
  }
95
- spans.reverse();
96
- let covered = 0;
97
- for (const s of spans)
98
- covered += s.end - s.start;
99
- return { spans, covered, uncovered: length - covered };
55
+ byStart[c.start].push(c);
56
+ const edge = c.start * (length + 1) + c.end;
57
+ const prev = byEdge.get(edge);
58
+ if (!prev || w < (prev.weight ?? 1)) {
59
+ byEdge.set(edge, c);
60
+ }
61
+ }
62
+ // One uncovered symbol must outweigh any sum of span weights, so coverage is
63
+ // strictly the primary objective and weight only breaks ties.
64
+ const skipCost = maxWeight * length + 1;
65
+ const system = {
66
+ key: (p) => "" + p,
67
+ axioms: () => [{ item: 0, cost: 0 }],
68
+ isGoal: (p) => p === length,
69
+ // No nonzero admissible bound is available here: a single candidate can
70
+ // cover the whole remainder for unit cost, so any per-symbol estimate would
71
+ // overestimate. With h = 0 this is exact Knuth/Dijkstra over the frontier —
72
+ // optimal, and linear in the positions. (The A* outside bound is for systems
73
+ // whose remaining cost can be genuinely lower-bounded; see the engine.)
74
+ *rules(p) {
75
+ if (p >= length) {
76
+ return;
77
+ }
78
+ yield { premises: [p], conclusion: p + 1, cost: skipCost }; // leave uncovered
79
+ for (const c of byStart[p]) {
80
+ yield { premises: [p], conclusion: c.end, cost: c.weight ?? 1 };
81
+ }
82
+ },
83
+ };
84
+ const best = lightestDerivation(system);
85
+ if (!best) {
86
+ return { spans: [], covered: 0, uncovered: length };
87
+ }
88
+ // Walk the frontier chain back to the axiom; an edge p→q is a chosen span iff
89
+ // a candidate spans exactly [p, q) (a covering edge is always cheaper than the
90
+ // skips it replaces, so it appears on the optimal path wherever it is used).
91
+ const spans = [];
92
+ let node = best;
93
+ while (node && node.rule) {
94
+ const to = node.item;
95
+ const from = node.premises[0].item;
96
+ const span = byEdge.get(from * (length + 1) + to);
97
+ if (span) {
98
+ spans.push(span);
99
+ }
100
+ node = node.premises[0];
101
+ }
102
+ spans.reverse();
103
+ let covered = 0;
104
+ for (const s of spans) {
105
+ covered += s.end - s.start;
106
+ }
107
+ return { spans, covered, uncovered: length - covered };
100
108
  }
@@ -29,62 +29,62 @@
29
29
  */
30
30
  /** One pattern occurrence found by the trie. */
31
31
  export interface Match<P> {
32
- /** Start offset in the searched sequence (inclusive). */
33
- start: number;
34
- /** End offset (exclusive); `end - start === length`. */
35
- end: number;
36
- /** Length of the matched pattern. */
37
- length: number;
38
- /** Pattern id, assigned in insertion order (0-based). */
39
- id: number;
40
- /** Payload registered with the pattern. */
41
- payload: P;
32
+ /** Start offset in the searched sequence (inclusive). */
33
+ start: number;
34
+ /** End offset (exclusive); `end - start === length`. */
35
+ end: number;
36
+ /** Length of the matched pattern. */
37
+ length: number;
38
+ /** Pattern id, assigned in insertion order (0-based). */
39
+ id: number;
40
+ /** Payload registered with the pattern. */
41
+ payload: P;
42
42
  }
43
43
  export declare class Trie<P = undefined> {
44
- private _nxt;
45
- private _sym;
46
- private _multi;
47
- private _end;
48
- private readonly _lens;
49
- private readonly _vals;
50
- private _len;
51
- constructor();
52
- /** The root state, for cursor walks. */
53
- get root(): number;
54
- /** Number of distinct patterns stored. */
55
- get size(): number;
56
- /** Allocate a fresh state, growing the typed arrays by a fixed increment
57
- * when full. No power-of-2 doubling — the transient double-memory spike
58
- * during growth is bounded to the increment. */
59
- private _state;
60
- /** Follow symbol `c` from state `s`. Returns the next state, or -1. */
61
- private _follow;
62
- /** Follow one symbol from `state`; returns the next state or -1 if none. */
63
- step(state: number, symbol: number): number;
64
- /** The pattern ending exactly at `state`, or null. */
65
- terminal(state: number): {
66
- id: number;
67
- payload: P;
68
- } | null;
69
- /** Length of the pattern with this id. */
70
- lengthOf(id: number): number;
71
- /**
72
- * Insert a pattern, returning its id. Inserting the same symbol-sequence
73
- * twice returns the first id and keeps the first payload (patterns are keyed
74
- * by content). Empty patterns are ignored and return -1.
75
- */
76
- insert(pattern: ArrayLike<number>, payload: P): number;
77
- /**
78
- * Every stored pattern that begins exactly at `pos` in `seq`, shortest first.
79
- * Walks forward from the root in O(longest match); reports nothing about any
80
- * other position. This is the on-demand probe the search uses.
81
- */
82
- matchesAt(seq: ArrayLike<number>, pos: number): Array<Match<P>>;
83
- /**
84
- * Every occurrence of every pattern anywhere in `seq` (eager form, the union
85
- * of {@link matchesAt} over all start positions). O(seq · longest pattern),
86
- * independent of how many patterns are stored. Use {@link matchesAt} when the
87
- * search only needs the sites at a particular position.
88
- */
89
- scan(seq: ArrayLike<number>): Array<Match<P>>;
44
+ private _nxt;
45
+ private _sym;
46
+ private _multi;
47
+ private _end;
48
+ private readonly _lens;
49
+ private readonly _vals;
50
+ private _len;
51
+ constructor();
52
+ /** The root state, for cursor walks. */
53
+ get root(): number;
54
+ /** Number of distinct patterns stored. */
55
+ get size(): number;
56
+ /** Allocate a fresh state, growing the typed arrays by a fixed increment
57
+ * when full. No power-of-2 doubling — the transient double-memory spike
58
+ * during growth is bounded to the increment. */
59
+ private _state;
60
+ /** Follow symbol `c` from state `s`. Returns the next state, or -1. */
61
+ private _follow;
62
+ /** Follow one symbol from `state`; returns the next state or -1 if none. */
63
+ step(state: number, symbol: number): number;
64
+ /** The pattern ending exactly at `state`, or null. */
65
+ terminal(state: number): {
66
+ id: number;
67
+ payload: P;
68
+ } | null;
69
+ /** Length of the pattern with this id. */
70
+ lengthOf(id: number): number;
71
+ /**
72
+ * Insert a pattern, returning its id. Inserting the same symbol-sequence
73
+ * twice returns the first id and keeps the first payload (patterns are keyed
74
+ * by content). Empty patterns are ignored and return -1.
75
+ */
76
+ insert(pattern: ArrayLike<number>, payload: P): number;
77
+ /**
78
+ * Every stored pattern that begins exactly at `pos` in `seq`, shortest first.
79
+ * Walks forward from the root in O(longest match); reports nothing about any
80
+ * other position. This is the on-demand probe the search uses.
81
+ */
82
+ matchesAt(seq: ArrayLike<number>, pos: number): Array<Match<P>>;
83
+ /**
84
+ * Every occurrence of every pattern anywhere in `seq` (eager form, the union
85
+ * of {@link matchesAt} over all start positions). O(seq · longest pattern),
86
+ * independent of how many patterns are stored. Use {@link matchesAt} when the
87
+ * search only needs the sites at a particular position.
88
+ */
89
+ scan(seq: ArrayLike<number>): Array<Match<P>>;
90
90
  }
@@ -29,189 +29,193 @@
29
29
  */
30
30
  const ROOT = 0;
31
31
  export class Trie {
32
- // ── compact transition storage ──────────────────────────────────────────
33
- // _nxt[s] — -1 terminal (no outgoing transition)
34
- // -2 multi-transition → _multi.get(s)
35
- // >=0 singleton target; the symbol is _sym[s]
36
- _nxt;
37
- _sym;
38
- _multi = null;
39
- // _end[s] is the pattern id ending exactly at s, or -1.
40
- _end;
41
- // Per-pattern data, indexed by pattern id.
42
- _lens = [];
43
- _vals = [];
44
- _len = 1; // next free state (state 0 = root)
45
- constructor() {
46
- const c = 256;
47
- this._nxt = new Int32Array(c);
48
- this._sym = new Uint8Array(c);
49
- this._end = new Int32Array(c);
50
- this._nxt[ROOT] = -1;
51
- this._end[ROOT] = -1;
32
+ // ── compact transition storage ──────────────────────────────────────────
33
+ // _nxt[s] — -1 terminal (no outgoing transition)
34
+ // -2 multi-transition → _multi.get(s)
35
+ // >=0 singleton target; the symbol is _sym[s]
36
+ _nxt;
37
+ _sym;
38
+ _multi = null;
39
+ // _end[s] is the pattern id ending exactly at s, or -1.
40
+ _end;
41
+ // Per-pattern data, indexed by pattern id.
42
+ _lens = [];
43
+ _vals = [];
44
+ _len = 1; // next free state (state 0 = root)
45
+ constructor() {
46
+ const c = 256;
47
+ this._nxt = new Int32Array(c);
48
+ this._sym = new Uint8Array(c);
49
+ this._end = new Int32Array(c);
50
+ this._nxt[ROOT] = -1;
51
+ this._end[ROOT] = -1;
52
+ }
53
+ /** The root state, for cursor walks. */
54
+ get root() {
55
+ return ROOT;
56
+ }
57
+ /** Number of distinct patterns stored. */
58
+ get size() {
59
+ return this._lens.length;
60
+ }
61
+ // ── internal helpers ────────────────────────────────────────────────────
62
+ /** Allocate a fresh state, growing the typed arrays by a fixed increment
63
+ * when full. No power-of-2 doubling — the transient double-memory spike
64
+ * during growth is bounded to the increment. */
65
+ _state() {
66
+ const s = this._len++;
67
+ if (s >= this._nxt.length) {
68
+ const c = s + 4096;
69
+ const nn = new Int32Array(c);
70
+ nn.set(this._nxt);
71
+ this._nxt = nn;
72
+ const ns = new Uint8Array(c);
73
+ ns.set(this._sym);
74
+ this._sym = ns;
75
+ const ne = new Int32Array(c);
76
+ ne.set(this._end);
77
+ this._end = ne;
52
78
  }
53
- /** The root state, for cursor walks. */
54
- get root() {
55
- return ROOT;
79
+ this._nxt[s] = -1;
80
+ this._end[s] = -1;
81
+ return s;
82
+ }
83
+ /** Follow symbol `c` from state `s`. Returns the next state, or -1. */
84
+ _follow(s, c) {
85
+ const n = this._nxt[s];
86
+ if (n >= 0) {
87
+ return this._sym[s] === c ? n : -1;
56
88
  }
57
- /** Number of distinct patterns stored. */
58
- get size() {
59
- return this._lens.length;
89
+ if (n === -2) {
90
+ return this._multi.get(s).get(c) ?? -1;
60
91
  }
61
- // ── internal helpers ────────────────────────────────────────────────────
62
- /** Allocate a fresh state, growing the typed arrays by a fixed increment
63
- * when full. No power-of-2 doubling — the transient double-memory spike
64
- * during growth is bounded to the increment. */
65
- _state() {
66
- const s = this._len++;
67
- if (s >= this._nxt.length) {
68
- const c = s + 4096;
69
- const nn = new Int32Array(c);
70
- nn.set(this._nxt);
71
- this._nxt = nn;
72
- const ns = new Uint8Array(c);
73
- ns.set(this._sym);
74
- this._sym = ns;
75
- const ne = new Int32Array(c);
76
- ne.set(this._end);
77
- this._end = ne;
78
- }
79
- this._nxt[s] = -1;
80
- this._end[s] = -1;
81
- return s;
82
- }
83
- /** Follow symbol `c` from state `s`. Returns the next state, or -1. */
84
- _follow(s, c) {
85
- const n = this._nxt[s];
86
- if (n >= 0)
87
- return this._sym[s] === c ? n : -1;
88
- if (n === -2)
89
- return this._multi.get(s).get(c) ?? -1;
90
- return -1; // n === -1
92
+ return -1; // n === -1
93
+ }
94
+ // ── cursor API (allocation-free) ─────────────────────────────────────────
95
+ /** Follow one symbol from `state`; returns the next state or -1 if none. */
96
+ step(state, symbol) {
97
+ return this._follow(state, symbol);
98
+ }
99
+ /** The pattern ending exactly at `state`, or null. */
100
+ terminal(state) {
101
+ const id = this._end[state];
102
+ return id === -1 ? null : { id, payload: this._vals[id] };
103
+ }
104
+ /** Length of the pattern with this id. */
105
+ lengthOf(id) {
106
+ return this._lens[id];
107
+ }
108
+ // ── build ───────────────────────────────────────────────────────────────
109
+ /**
110
+ * Insert a pattern, returning its id. Inserting the same symbol-sequence
111
+ * twice returns the first id and keeps the first payload (patterns are keyed
112
+ * by content). Empty patterns are ignored and return -1.
113
+ */
114
+ insert(pattern, payload) {
115
+ const n = pattern.length;
116
+ if (n === 0) {
117
+ return -1;
91
118
  }
92
- // ── cursor API (allocation-free) ─────────────────────────────────────────
93
- /** Follow one symbol from `state`; returns the next state or -1 if none. */
94
- step(state, symbol) {
95
- return this._follow(state, symbol);
96
- }
97
- /** The pattern ending exactly at `state`, or null. */
98
- terminal(state) {
99
- const id = this._end[state];
100
- return id === -1 ? null : { id, payload: this._vals[id] };
119
+ let s = ROOT;
120
+ for (let i = 0; i < n; i++) {
121
+ const c = pattern[i];
122
+ const t = this._nxt[s];
123
+ if (t === -1) {
124
+ // Terminal place first transition as a singleton.
125
+ const ns = this._state();
126
+ this._nxt[s] = ns;
127
+ this._sym[s] = c;
128
+ s = ns;
129
+ } else if (t >= 0) {
130
+ // Singleton — either advance on match, or expand to multi.
131
+ if (this._sym[s] === c) {
132
+ s = t;
133
+ } else {
134
+ const map = new Map();
135
+ map.set(this._sym[s], t);
136
+ const ns = this._state();
137
+ map.set(c, ns);
138
+ if (!this._multi) {
139
+ this._multi = new Map();
140
+ }
141
+ this._multi.set(s, map);
142
+ this._nxt[s] = -2;
143
+ s = ns;
144
+ }
145
+ } else {
146
+ // Multi (t === -2) — extend the existing Map.
147
+ const map = this._multi.get(s);
148
+ let ns = map.get(c);
149
+ if (ns === undefined) {
150
+ ns = this._state();
151
+ map.set(c, ns);
152
+ }
153
+ s = ns;
154
+ }
101
155
  }
102
- /** Length of the pattern with this id. */
103
- lengthOf(id) {
104
- return this._lens[id];
156
+ if (this._end[s] !== -1) {
157
+ return this._end[s]; // already present
105
158
  }
106
- // ── build ───────────────────────────────────────────────────────────────
107
- /**
108
- * Insert a pattern, returning its id. Inserting the same symbol-sequence
109
- * twice returns the first id and keeps the first payload (patterns are keyed
110
- * by content). Empty patterns are ignored and return -1.
111
- */
112
- insert(pattern, payload) {
113
- const n = pattern.length;
114
- if (n === 0)
115
- return -1;
116
- let s = ROOT;
117
- for (let i = 0; i < n; i++) {
118
- const c = pattern[i];
119
- const t = this._nxt[s];
120
- if (t === -1) {
121
- // Terminal place first transition as a singleton.
122
- const ns = this._state();
123
- this._nxt[s] = ns;
124
- this._sym[s] = c;
125
- s = ns;
126
- }
127
- else if (t >= 0) {
128
- // Singleton — either advance on match, or expand to multi.
129
- if (this._sym[s] === c) {
130
- s = t;
131
- }
132
- else {
133
- const map = new Map();
134
- map.set(this._sym[s], t);
135
- const ns = this._state();
136
- map.set(c, ns);
137
- if (!this._multi)
138
- this._multi = new Map();
139
- this._multi.set(s, map);
140
- this._nxt[s] = -2;
141
- s = ns;
142
- }
143
- }
144
- else {
145
- // Multi (t === -2) — extend the existing Map.
146
- const map = this._multi.get(s);
147
- let ns = map.get(c);
148
- if (ns === undefined) {
149
- ns = this._state();
150
- map.set(c, ns);
151
- }
152
- s = ns;
153
- }
154
- }
155
- if (this._end[s] !== -1)
156
- return this._end[s]; // already present
157
- const id = this._lens.length;
158
- this._lens.push(n);
159
- this._vals.push(payload);
160
- this._end[s] = id;
161
- return id;
159
+ const id = this._lens.length;
160
+ this._lens.push(n);
161
+ this._vals.push(payload);
162
+ this._end[s] = id;
163
+ return id;
164
+ }
165
+ // ── matching ───────────────────────────────────────────────────────────
166
+ /**
167
+ * Every stored pattern that begins exactly at `pos` in `seq`, shortest first.
168
+ * Walks forward from the root in O(longest match); reports nothing about any
169
+ * other position. This is the on-demand probe the search uses.
170
+ */
171
+ matchesAt(seq, pos) {
172
+ const out = [];
173
+ let s = ROOT;
174
+ for (let i = pos, n = seq.length; i < n; i++) {
175
+ s = this._follow(s, seq[i]);
176
+ if (s === -1) {
177
+ break;
178
+ }
179
+ const id = this._end[s];
180
+ if (id !== -1) {
181
+ out.push({
182
+ start: pos,
183
+ end: i + 1,
184
+ length: this._lens[id],
185
+ id,
186
+ payload: this._vals[id],
187
+ });
188
+ }
162
189
  }
163
- // ── matching ───────────────────────────────────────────────────────────
164
- /**
165
- * Every stored pattern that begins exactly at `pos` in `seq`, shortest first.
166
- * Walks forward from the root in O(longest match); reports nothing about any
167
- * other position. This is the on-demand probe the search uses.
168
- */
169
- matchesAt(seq, pos) {
170
- const out = [];
171
- let s = ROOT;
172
- for (let i = pos, n = seq.length; i < n; i++) {
173
- s = this._follow(s, seq[i]);
174
- if (s === -1)
175
- break;
176
- const id = this._end[s];
177
- if (id !== -1) {
178
- out.push({
179
- start: pos,
180
- end: i + 1,
181
- length: this._lens[id],
182
- id,
183
- payload: this._vals[id],
184
- });
185
- }
190
+ return out;
191
+ }
192
+ /**
193
+ * Every occurrence of every pattern anywhere in `seq` (eager form, the union
194
+ * of {@link matchesAt} over all start positions). O(seq · longest pattern),
195
+ * independent of how many patterns are stored. Use {@link matchesAt} when the
196
+ * search only needs the sites at a particular position.
197
+ */
198
+ scan(seq) {
199
+ const out = [];
200
+ for (let pos = 0, n = seq.length; pos < n; pos++) {
201
+ let s = ROOT;
202
+ for (let i = pos; i < n; i++) {
203
+ s = this._follow(s, seq[i]);
204
+ if (s === -1) {
205
+ break;
186
206
  }
187
- return out;
188
- }
189
- /**
190
- * Every occurrence of every pattern anywhere in `seq` (eager form, the union
191
- * of {@link matchesAt} over all start positions). O(seq · longest pattern),
192
- * independent of how many patterns are stored. Use {@link matchesAt} when the
193
- * search only needs the sites at a particular position.
194
- */
195
- scan(seq) {
196
- const out = [];
197
- for (let pos = 0, n = seq.length; pos < n; pos++) {
198
- let s = ROOT;
199
- for (let i = pos; i < n; i++) {
200
- s = this._follow(s, seq[i]);
201
- if (s === -1)
202
- break;
203
- const id = this._end[s];
204
- if (id !== -1) {
205
- out.push({
206
- start: pos,
207
- end: i + 1,
208
- length: this._lens[id],
209
- id,
210
- payload: this._vals[id],
211
- });
212
- }
213
- }
207
+ const id = this._end[s];
208
+ if (id !== -1) {
209
+ out.push({
210
+ start: pos,
211
+ end: i + 1,
212
+ length: this._lens[id],
213
+ id,
214
+ payload: this._vals[id],
215
+ });
214
216
  }
215
- return out;
217
+ }
216
218
  }
219
+ return out;
220
+ }
217
221
  }