@hviana/sema 0.1.4 → 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.
- package/dist/example/demo.js +25 -25
- package/dist/example/train_base.d.ts +21 -19
- package/dist/example/train_base.js +1952 -1684
- package/dist/src/alphabet.d.ts +3 -3
- package/dist/src/alphabet.js +27 -24
- package/dist/src/alu/src/alu.d.ts +194 -169
- package/dist/src/alu/src/alu.js +414 -374
- package/dist/src/alu/src/expr.d.ts +43 -36
- package/dist/src/alu/src/expr.js +278 -252
- package/dist/src/alu/src/index.d.ts +98 -9
- package/dist/src/alu/src/index.js +63 -9
- package/dist/src/alu/src/kernel-arith.d.ts +5 -1
- package/dist/src/alu/src/kernel-arith.js +285 -195
- package/dist/src/alu/src/kernel-bits.d.ts +5 -1
- package/dist/src/alu/src/kernel-bits.js +115 -81
- package/dist/src/alu/src/kernel-logic.js +63 -33
- package/dist/src/alu/src/kernel-nd.js +195 -155
- package/dist/src/alu/src/kernel-numeric.d.ts +80 -16
- package/dist/src/alu/src/kernel-numeric.js +331 -259
- package/dist/src/alu/src/operation.d.ts +132 -112
- package/dist/src/alu/src/operation.js +163 -149
- package/dist/src/alu/src/parser.d.ts +175 -168
- package/dist/src/alu/src/parser.js +421 -382
- package/dist/src/alu/src/resonance.d.ts +25 -16
- package/dist/src/alu/src/resonance.js +56 -49
- package/dist/src/alu/src/text.d.ts +7 -3
- package/dist/src/alu/src/text.js +37 -31
- package/dist/src/alu/src/value.d.ts +14 -14
- package/dist/src/alu/src/value.js +160 -150
- package/dist/src/alu/test/alu.test.js +656 -543
- package/dist/src/bytes.d.ts +5 -1
- package/dist/src/bytes.js +40 -33
- package/dist/src/config.d.ts +102 -102
- package/dist/src/config.js +83 -83
- package/dist/src/derive/src/deduction.d.ts +62 -60
- package/dist/src/derive/src/deduction.js +109 -103
- package/dist/src/derive/src/index.d.ts +7 -1
- package/dist/src/derive/src/priority-queue.d.ts +8 -8
- package/dist/src/derive/src/priority-queue.js +61 -57
- package/dist/src/derive/src/rewrite.d.ts +18 -15
- package/dist/src/derive/src/rewrite.js +67 -59
- package/dist/src/derive/src/trie.d.ts +56 -56
- package/dist/src/derive/src/trie.js +179 -175
- package/dist/src/derive/test/derive.test.js +100 -96
- package/dist/src/extension.d.ts +16 -13
- package/dist/src/geometry.d.ts +42 -17
- package/dist/src/geometry.js +266 -235
- package/dist/src/index.d.ts +17 -2
- package/dist/src/index.js +7 -1
- package/dist/src/ingest-cache.d.ts +30 -25
- package/dist/src/ingest-cache.js +127 -108
- package/dist/src/mind/articulation.d.ts +5 -1
- package/dist/src/mind/articulation.js +112 -77
- package/dist/src/mind/attention.d.ts +108 -40
- package/dist/src/mind/attention.js +871 -749
- package/dist/src/mind/canonical.d.ts +19 -4
- package/dist/src/mind/canonical.js +31 -27
- package/dist/src/mind/graph-search.d.ts +225 -201
- package/dist/src/mind/graph-search.js +821 -742
- package/dist/src/mind/index.d.ts +10 -2
- package/dist/src/mind/junction.d.ts +58 -31
- package/dist/src/mind/junction.js +237 -172
- package/dist/src/mind/learning.d.ts +52 -16
- package/dist/src/mind/learning.js +165 -143
- package/dist/src/mind/match.d.ts +69 -20
- package/dist/src/mind/match.js +318 -259
- package/dist/src/mind/mechanisms/alu.js +16 -16
- package/dist/src/mind/mechanisms/cast.d.ts +13 -9
- package/dist/src/mind/mechanisms/cast.js +456 -363
- package/dist/src/mind/mechanisms/confluence.d.ts +12 -8
- package/dist/src/mind/mechanisms/confluence.js +183 -152
- package/dist/src/mind/mechanisms/cover.d.ts +8 -2
- package/dist/src/mind/mechanisms/cover.js +210 -148
- package/dist/src/mind/mechanisms/extraction.d.ts +34 -8
- package/dist/src/mind/mechanisms/extraction.js +288 -234
- package/dist/src/mind/mechanisms/recall.d.ts +10 -6
- package/dist/src/mind/mechanisms/recall.js +185 -126
- package/dist/src/mind/mind.d.ts +154 -129
- package/dist/src/mind/mind.js +288 -258
- package/dist/src/mind/pipeline-mechanism.d.ts +124 -112
- package/dist/src/mind/pipeline-mechanism.js +172 -161
- package/dist/src/mind/pipeline.d.ts +14 -4
- package/dist/src/mind/pipeline.js +189 -125
- package/dist/src/mind/primitives.d.ts +32 -8
- package/dist/src/mind/primitives.js +117 -99
- package/dist/src/mind/rationale.d.ts +98 -86
- package/dist/src/mind/rationale.js +121 -113
- package/dist/src/mind/reasoning.d.ts +13 -2
- package/dist/src/mind/reasoning.js +166 -129
- package/dist/src/mind/recognition.d.ts +4 -1
- package/dist/src/mind/recognition.js +208 -183
- package/dist/src/mind/resonance.d.ts +22 -5
- package/dist/src/mind/resonance.js +0 -0
- package/dist/src/mind/trace.d.ts +25 -6
- package/dist/src/mind/trace.js +58 -50
- package/dist/src/mind/traverse.d.ts +58 -16
- package/dist/src/mind/traverse.js +357 -304
- package/dist/src/mind/types.d.ts +127 -120
- package/dist/src/mind/types.js +69 -60
- package/dist/src/rabitq-hnsw/src/database.d.ts +179 -177
- package/dist/src/rabitq-hnsw/src/database.js +359 -342
- package/dist/src/rabitq-hnsw/src/heap.d.ts +12 -12
- package/dist/src/rabitq-hnsw/src/heap.js +80 -75
- package/dist/src/rabitq-hnsw/src/hnsw.d.ts +103 -103
- package/dist/src/rabitq-hnsw/src/hnsw.js +454 -428
- package/dist/src/rabitq-hnsw/src/index.d.ts +6 -1
- package/dist/src/rabitq-hnsw/src/prng.d.ts +10 -10
- package/dist/src/rabitq-hnsw/src/prng.js +27 -25
- package/dist/src/rabitq-hnsw/src/rabitq.d.ts +69 -69
- package/dist/src/rabitq-hnsw/src/rabitq.js +262 -246
- package/dist/src/rabitq-hnsw/src/store.d.ts +132 -132
- package/dist/src/rabitq-hnsw/src/store.js +843 -752
- package/dist/src/rabitq-hnsw/test/hnsw.test.js +1054 -805
- package/dist/src/sema.d.ts +13 -9
- package/dist/src/sema.js +40 -26
- package/dist/src/store-sqlite.d.ts +166 -144
- package/dist/src/store-sqlite.js +660 -578
- package/dist/src/store.d.ts +630 -593
- package/dist/src/store.js +1581 -1440
- package/dist/src/vec.d.ts +9 -5
- package/dist/src/vec.js +73 -61
- package/package.json +2 -2
package/dist/src/mind/types.d.ts
CHANGED
|
@@ -11,156 +11,156 @@ export type Input = string | Uint8Array | Grid | Grid[];
|
|
|
11
11
|
/** The host capabilities GraphSearch consults during a cover. MindContext
|
|
12
12
|
* extends this so the Mind can pass itself as the host. */
|
|
13
13
|
export interface GraphSearchHost {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
resolve(bytes: Uint8Array): number | null;
|
|
15
|
+
recogniseSpan?(bytes: Uint8Array): {
|
|
16
|
+
sites: ReadonlyArray<Site>;
|
|
17
|
+
leaves: ReadonlyArray<Leaf>;
|
|
18
|
+
splits: ReadonlySet<number>;
|
|
19
|
+
};
|
|
20
|
+
chooseNext?(node: number): number | undefined;
|
|
21
21
|
}
|
|
22
22
|
export interface Recognition {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
/** Forms that can lead somewhere — they have an edge or a halo. */
|
|
24
|
+
sites: Site[];
|
|
25
|
+
/** The query's perceived leaves (the search's covering axioms). */
|
|
26
|
+
leaves: Leaf[];
|
|
27
|
+
/** Sub-leaf positions where a form boundary falls between leaf edges. */
|
|
28
|
+
splits: Set<number>;
|
|
29
29
|
}
|
|
30
30
|
/** How the consensus climb weights a region's Document-Frequency reach. */
|
|
31
31
|
export type DFMode = "inverse" | "direct" | "combined";
|
|
32
32
|
/** One POINT OF ATTENTION the consensus climb resolved. */
|
|
33
33
|
export interface Attention {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
/** The learnt context this point resolves to. */
|
|
35
|
+
anchor: number;
|
|
36
|
+
/** IDF-weighted consensus vote — the strength that orders points. */
|
|
37
|
+
vote: number;
|
|
38
|
+
/** The union of the query byte-spans whose evidence supports this point. */
|
|
39
|
+
start: number;
|
|
40
|
+
end: number;
|
|
41
41
|
}
|
|
42
42
|
/** Both read-outs of one consensus climb. */
|
|
43
43
|
export interface AttentionRead {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
roots: Attention[];
|
|
45
|
+
ranked: Attention[];
|
|
46
46
|
}
|
|
47
47
|
/** A positioned region of a byte stream paired with its gist. */
|
|
48
48
|
export interface Segment {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
start: number;
|
|
50
|
+
end: number;
|
|
51
|
+
v: Vec;
|
|
52
52
|
}
|
|
53
53
|
/** A region of the query's perceived tree for the consensus climb. */
|
|
54
54
|
export interface Region {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
v: Vec;
|
|
56
|
+
start: number;
|
|
57
|
+
end: number;
|
|
58
|
+
chunk: boolean;
|
|
59
|
+
/** Whether the region's bytes resolve to a KNOWN node (content-addressed,
|
|
60
|
+
* exact). Exact regions vote with full weight; approximate ones pay the
|
|
61
|
+
* contrastive margin (see voteRegions) — under the linear fold a raw
|
|
62
|
+
* resonance score is byte-overlap, evidence only in excess of its best
|
|
63
|
+
* rival conclusion. */
|
|
64
|
+
known: boolean;
|
|
65
65
|
}
|
|
66
66
|
/** Per-region vote data from the consensus climb's resonance pass. */
|
|
67
67
|
export interface RegionVote {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
start: number;
|
|
69
|
+
end: number;
|
|
70
|
+
canonicalFailed: boolean;
|
|
71
|
+
roots: readonly number[];
|
|
72
|
+
w: number;
|
|
73
|
+
wFocus: number;
|
|
74
74
|
}
|
|
75
75
|
/** The edge-bearing contexts reached by climbing from a node, plus saturation info. */
|
|
76
76
|
export interface AncestorReach {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
roots: number[];
|
|
78
|
+
contextsReached: number;
|
|
79
|
+
saturated: boolean;
|
|
80
80
|
}
|
|
81
81
|
/** Saturated-interval information for the noise-drop gate. */
|
|
82
82
|
export interface SaturationInfo {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
leadingEnd: number;
|
|
84
|
+
hasLeading: boolean;
|
|
85
|
+
intervals: Array<{
|
|
86
|
+
start: number;
|
|
87
|
+
end: number;
|
|
88
|
+
}>;
|
|
89
89
|
}
|
|
90
90
|
/** The items of poolVotes' deduction system. */
|
|
91
91
|
export type AItem = {
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
kind: "region";
|
|
93
|
+
ri: number;
|
|
94
94
|
} | {
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
kind: "anchor";
|
|
96
|
+
id: number;
|
|
97
97
|
} | {
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
kind: "anchorFocus";
|
|
99
|
+
id: number;
|
|
100
100
|
};
|
|
101
101
|
export interface MindContext extends GraphSearchHost {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
102
|
+
store: Store;
|
|
103
|
+
space: Space;
|
|
104
|
+
alphabet: Alphabet;
|
|
105
|
+
cfg: MindConfig;
|
|
106
|
+
search: GraphSearch;
|
|
107
|
+
trace: Rationale | null;
|
|
108
|
+
climbMemo: WeakMap<Uint8Array, Map<string, AttentionRead>> | null;
|
|
109
|
+
/** Per-response memo of {@link recognise} keyed by the byte-array OBJECT
|
|
110
|
+
* (think, articulate, and the post-grounding pre-consume all recognise the
|
|
111
|
+
* same query/answer objects). Valid because the store is read-only while
|
|
112
|
+
* a response is in flight; bypassed when a trace is attached so every
|
|
113
|
+
* recognise still emits its rationale step. Null outside respond(). */
|
|
114
|
+
recogniseMemo: WeakMap<Uint8Array, Recognition> | null;
|
|
115
|
+
/** Per-response memo of {@link perceive} keyed by the byte-array OBJECT —
|
|
116
|
+
* the GENERAL memo the result-level ones (recogniseMemo, climbMemo,
|
|
117
|
+
* _gistCache) each partially compensate for: resolve(), gistOf(), and
|
|
118
|
+
* every mechanism's re-perception of the same query/answer object hit it
|
|
119
|
+
* (a reason hop used to fold the same answer three times). Valid because
|
|
120
|
+
* the store is read-only while a response is in flight and perception is
|
|
121
|
+
* a pure function of bytes; only inference-shaped calls (plain Uint8Array,
|
|
122
|
+
* no leafAt/lookup capabilities) are memoised, so the deposit path never
|
|
123
|
+
* sees it. Keyed by CONTENT (latin1 of the bytes), not object identity —
|
|
124
|
+
* mechanisms materialise the same span in fresh subarrays constantly
|
|
125
|
+
* (measured on a trained store: 46% of one response's perceptions were
|
|
126
|
+
* byte-identical repeats an identity key missed). NOT bypassed under
|
|
127
|
+
* trace — perception emits no rationale steps, so there is nothing a memo
|
|
128
|
+
* hit could swallow. Null outside respond(). */
|
|
129
|
+
perceiveMemo: Map<string, Sema> | null;
|
|
130
|
+
_edgeGuide: Vec | null;
|
|
131
|
+
_edgeChoice: Map<number, number>;
|
|
132
|
+
_prevSeen: Set<number> | null;
|
|
133
|
+
/** Session cache of node-id → perceived gist, for candidate scoring
|
|
134
|
+
* ({@link chooseAmong} in the reverse projection's recall path re-gists up to
|
|
135
|
+
* √N contexts per pick — the measured bottleneck there). `chooseNext` does
|
|
136
|
+
* NOT use this cache; forward-edge disambiguation uses prevOf counts
|
|
137
|
+
* (distributional evidence) instead of gist comparison, because for short
|
|
138
|
+
* answer candidates the gist is dominated by accidental byte-pattern
|
|
139
|
+
* correlations. A node's bytes are immutable and perception is a pure
|
|
140
|
+
* function of bytes, so an entry stays valid for the store's lifetime —
|
|
141
|
+
* never invalidated. Bounded LRU (byte-sized); a miss only re-perceives,
|
|
142
|
+
* never a correctness risk. */
|
|
143
|
+
_gistCache: BoundedMap<number, Vec>;
|
|
144
|
+
/** DEPOSIT-path stable-prefix tree cache: content key (latin1) of a
|
|
145
|
+
* deposited input → its plain-fold level PYRAMID, so the NEXT deposit
|
|
146
|
+
* whose prefix it is (a conversation's accumulated context) reuses every
|
|
147
|
+
* full aligned block and refolds only the right edge of each level —
|
|
148
|
+
* O(turn) per deposit instead of O(context), with the tree BIT-IDENTICAL
|
|
149
|
+
* to a from-scratch fold (see FoldPyramid). Purely a performance cache.
|
|
150
|
+
* Entry-count bounded (a pyramid is ~KB/byte of content; only the few
|
|
151
|
+
* live conversation chains matter). */
|
|
152
|
+
_depositTrees: BoundedMap<string, FoldPyramid>;
|
|
153
|
+
/** The byte lengths present in {@link _depositTrees} — the candidate
|
|
154
|
+
* prefix lengths probed (longest first). Drifts on eviction (a stale
|
|
155
|
+
* length only costs a miss); cleared with the map when it outgrows the
|
|
156
|
+
* probe budget. */
|
|
157
|
+
_depositLens: Set<number>;
|
|
158
|
+
/** Mind-lifetime intern memo by NODE IDENTITY: perceived-tree node → its
|
|
159
|
+
* content-addressed id. Valid forever (ids are permanent, Sema nodes
|
|
160
|
+
* immutable); WeakMap, so entries live exactly as long as the pyramid
|
|
161
|
+
* cache keeps the shared subtrees alive. Lets internTreeIds skip whole
|
|
162
|
+
* shared subtrees and indexSubSpans keep its seenBefore window skip. */
|
|
163
|
+
_internIds: WeakMap<Sema, number>;
|
|
164
164
|
}
|
|
165
165
|
/** Read a whole node's bytes. */
|
|
166
166
|
export declare const ALL = 2147483647;
|
|
@@ -168,7 +168,14 @@ export declare const ALL = 2147483647;
|
|
|
168
168
|
export declare function spliceAll(segs: Seg[]): Uint8Array | null;
|
|
169
169
|
/** Lift the answer out of the cover for think: the recognised region, free of
|
|
170
170
|
* the asker's surrounding (unrecognised) framing. */
|
|
171
|
-
export declare function liftAnswer(
|
|
171
|
+
export declare function liftAnswer(
|
|
172
|
+
segs: Seg[],
|
|
173
|
+
queryLen: number,
|
|
174
|
+
): Uint8Array | null;
|
|
172
175
|
/** The CHANGED NODES of a freshly-perceived `tree` against the node ids a previous
|
|
173
176
|
* tracked deposit interned (`prevSeen`). */
|
|
174
|
-
export declare function changedNodes(
|
|
177
|
+
export declare function changedNodes(
|
|
178
|
+
tree: Sema,
|
|
179
|
+
ids: Map<Sema, number>,
|
|
180
|
+
prevSeen: Set<number>,
|
|
181
|
+
): Sema[];
|
package/dist/src/mind/types.js
CHANGED
|
@@ -11,74 +11,83 @@ import { dominates } from "../geometry.js";
|
|
|
11
11
|
export const ALL = 0x7fffffff;
|
|
12
12
|
/** Splice every chosen span in order — the whole cover as one byte string. */
|
|
13
13
|
export function spliceAll(segs) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
if (!segs.some((s) => s.rec)) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return concatBytes(segs.map((s) => s.bytes));
|
|
17
18
|
}
|
|
18
19
|
/** Lift the answer out of the cover for think: the recognised region, free of
|
|
19
20
|
* the asker's surrounding (unrecognised) framing. */
|
|
20
21
|
export function liftAnswer(segs, queryLen) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (recognised.length === 0)
|
|
26
|
-
return null;
|
|
27
|
-
if (recognised.length === 1) {
|
|
28
|
-
const s = segs[recognised[0]];
|
|
29
|
-
if (dominates(s.j - s.i, queryLen)) {
|
|
30
|
-
return concatBytes(segs.map((x) => x.bytes));
|
|
31
|
-
}
|
|
32
|
-
return s.bytes;
|
|
22
|
+
const recognised = [];
|
|
23
|
+
for (let k = 0; k < segs.length; k++) {
|
|
24
|
+
if (segs[k].rec) {
|
|
25
|
+
recognised.push(k);
|
|
33
26
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return
|
|
27
|
+
}
|
|
28
|
+
if (recognised.length === 0) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if (recognised.length === 1) {
|
|
32
|
+
const s = segs[recognised[0]];
|
|
33
|
+
if (dominates(s.j - s.i, queryLen)) {
|
|
34
|
+
return concatBytes(segs.map((x) => x.bytes));
|
|
35
|
+
}
|
|
36
|
+
return s.bytes;
|
|
37
|
+
}
|
|
38
|
+
const lo = recognised[0];
|
|
39
|
+
const hi = recognised[recognised.length - 1];
|
|
40
|
+
return concatBytes(segs.slice(lo, hi + 1).map((x) => x.bytes));
|
|
37
41
|
}
|
|
38
42
|
/** The CHANGED NODES of a freshly-perceived `tree` against the node ids a previous
|
|
39
43
|
* tracked deposit interned (`prevSeen`). */
|
|
40
44
|
export function changedNodes(tree, ids, prevSeen) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
45
|
+
const newCount = new Map();
|
|
46
|
+
const count = (n) => {
|
|
47
|
+
const memo = newCount.get(n);
|
|
48
|
+
if (memo !== undefined) {
|
|
49
|
+
return memo;
|
|
50
|
+
}
|
|
51
|
+
const id = ids.get(n);
|
|
52
|
+
// PRUNE: a node whose id the previous deposit already interned is old,
|
|
53
|
+
// and content addressing makes that transitive — the same id names the
|
|
54
|
+
// same content, so every descendant was interned then too. The whole
|
|
55
|
+
// subtree counts 0 without walking it; with the pyramid fold sharing a
|
|
56
|
+
// conversation's prefix subtree, this is what keeps the changed-nodes
|
|
57
|
+
// read O(new nodes) instead of O(context). (A node internTreeIds
|
|
58
|
+
// memo-skipped has an id here exactly when it is such a shared root.)
|
|
59
|
+
if (id !== undefined && prevSeen.has(id)) {
|
|
60
|
+
newCount.set(n, 0);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
let c = 1; // reachable only when NOT pruned above ⇒ this node is new
|
|
64
|
+
if (n.kids) {
|
|
65
|
+
for (const k of n.kids) {
|
|
66
|
+
c += count(k);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
newCount.set(n, c);
|
|
70
|
+
return c;
|
|
71
|
+
};
|
|
72
|
+
const total = count(tree);
|
|
73
|
+
if (total === 0) {
|
|
74
|
+
return [tree];
|
|
75
|
+
}
|
|
76
|
+
let n = tree;
|
|
77
|
+
for (;;) {
|
|
78
|
+
if (n.kids === null) {
|
|
79
|
+
return [n];
|
|
80
|
+
}
|
|
81
|
+
let holder = null;
|
|
82
|
+
for (const k of n.kids) {
|
|
83
|
+
if (newCount.get(k) === total) {
|
|
84
|
+
holder = k;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (holder === null) {
|
|
89
|
+
return [n];
|
|
83
90
|
}
|
|
91
|
+
n = holder;
|
|
92
|
+
}
|
|
84
93
|
}
|