@msb235/blits 0.14.0 → 0.15.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.
Files changed (173) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/README.md +3 -3
  3. package/dist/ahead.d.ts +26 -0
  4. package/dist/ahead.js +264 -0
  5. package/dist/around.d.ts +11 -0
  6. package/dist/around.js +32 -0
  7. package/dist/bare.js +15 -7
  8. package/dist/blank.d.ts +10 -0
  9. package/dist/blank.js +26 -0
  10. package/dist/blend.d.ts +2 -1
  11. package/dist/blend.js +1 -6
  12. package/dist/bound.d.ts +70 -0
  13. package/dist/bound.js +351 -0
  14. package/dist/chain.d.ts +2 -1
  15. package/dist/chain.js +7 -5
  16. package/dist/changes.d.ts +24 -0
  17. package/dist/changes.js +189 -0
  18. package/dist/channels.d.ts +39 -13
  19. package/dist/channels.js +14 -5
  20. package/dist/color.d.ts +12 -10
  21. package/dist/color.js +25 -25
  22. package/dist/columns.js +7 -3
  23. package/dist/contribution.d.ts +30 -0
  24. package/dist/contribution.js +374 -0
  25. package/dist/copyout.d.ts +10 -0
  26. package/dist/copyout.js +69 -24
  27. package/dist/crowd.d.ts +8 -2
  28. package/dist/crowd.js +4 -5
  29. package/dist/cue.d.ts +8 -1
  30. package/dist/cue.js +39 -13
  31. package/dist/cut.d.ts +8 -0
  32. package/dist/cut.js +4 -0
  33. package/dist/drop.d.ts +9 -0
  34. package/dist/drop.js +84 -0
  35. package/dist/due.d.ts +24 -5
  36. package/dist/due.js +113 -33
  37. package/dist/easing.d.ts +4 -4
  38. package/dist/easing.js +3 -3
  39. package/dist/everyone.d.ts +3 -2
  40. package/dist/everyone.js +3 -3
  41. package/dist/fade.d.ts +2 -1
  42. package/dist/fade.js +2 -1
  43. package/dist/fill.d.ts +3 -2
  44. package/dist/fill.js +51 -19
  45. package/dist/fit.d.ts +44 -26
  46. package/dist/fit.js +10 -9
  47. package/dist/fold.d.ts +4 -37
  48. package/dist/fold.js +5 -513
  49. package/dist/gather.d.ts +12 -7
  50. package/dist/gather.js +179 -44
  51. package/dist/gone.js +2 -2
  52. package/dist/grid.d.ts +47 -0
  53. package/dist/grid.js +247 -0
  54. package/dist/handle.d.ts +2 -0
  55. package/dist/handle.js +14 -8
  56. package/dist/held.d.ts +2 -1
  57. package/dist/history.d.ts +6 -5
  58. package/dist/history.js +7 -9
  59. package/dist/hosts.js +9 -15
  60. package/dist/index.d.ts +5 -4
  61. package/dist/keyfill.d.ts +1 -1
  62. package/dist/keyfill.js +5 -3
  63. package/dist/keyrows.d.ts +12 -0
  64. package/dist/keyrows.js +99 -12
  65. package/dist/lane.d.ts +36 -6
  66. package/dist/lane.js +32 -1
  67. package/dist/lanes.d.ts +23 -8
  68. package/dist/lanes.js +29 -8
  69. package/dist/leavings.d.ts +2 -2
  70. package/dist/leavings.js +1 -1
  71. package/dist/lives.d.ts +47 -0
  72. package/dist/lives.js +180 -0
  73. package/dist/meet.d.ts +2 -2
  74. package/dist/meet.js +90 -4
  75. package/dist/mixer.d.ts +54 -36
  76. package/dist/mixer.js +53 -171
  77. package/dist/motion.d.ts +14 -118
  78. package/dist/motion.js +16 -8
  79. package/dist/motions.d.ts +62 -155
  80. package/dist/motions.js +56 -370
  81. package/dist/moved.d.ts +1 -1
  82. package/dist/moved.js +16 -7
  83. package/dist/named.d.ts +0 -1
  84. package/dist/named.js +20 -2
  85. package/dist/numbers.d.ts +2 -0
  86. package/dist/numbers.js +4 -0
  87. package/dist/owed.d.ts +2 -0
  88. package/dist/owed.js +4 -0
  89. package/dist/pack.d.ts +2 -1
  90. package/dist/pack.js +2 -3
  91. package/dist/patch.d.ts +7 -3
  92. package/dist/patch.js +6 -2
  93. package/dist/places.d.ts +53 -0
  94. package/dist/places.js +171 -0
  95. package/dist/plot.d.ts +12 -0
  96. package/dist/plot.js +52 -0
  97. package/dist/project.d.ts +4 -4
  98. package/dist/project.js +9 -212
  99. package/dist/pull.js +11 -8
  100. package/dist/qualify.js +84 -21
  101. package/dist/reading.d.ts +6 -0
  102. package/dist/reading.js +5 -0
  103. package/dist/record.d.ts +5 -1
  104. package/dist/record.js +3 -2
  105. package/dist/relink.d.ts +1 -1
  106. package/dist/restored.d.ts +2 -2
  107. package/dist/restored.js +1 -1
  108. package/dist/rests.d.ts +9 -0
  109. package/dist/rests.js +76 -0
  110. package/dist/revive.d.ts +3 -2
  111. package/dist/revive.js +2 -2
  112. package/dist/{locus.d.ts → role.d.ts} +10 -9
  113. package/dist/{locus.js → role.js} +3 -3
  114. package/dist/roles.d.ts +10 -0
  115. package/dist/roles.js +158 -0
  116. package/dist/roster.js +3 -3
  117. package/dist/rotation.d.ts +1 -1
  118. package/dist/rotation.js +1 -1
  119. package/dist/rows.js +1 -1
  120. package/dist/sample.d.ts +2 -2
  121. package/dist/sample.js +33 -7
  122. package/dist/seek.js +5 -5
  123. package/dist/setting.d.ts +27 -0
  124. package/dist/setting.js +55 -0
  125. package/dist/shared.d.ts +1 -1
  126. package/dist/shared.js +1 -1
  127. package/dist/signals.d.ts +64 -32
  128. package/dist/signals.js +20 -18
  129. package/dist/stack.d.ts +5 -5
  130. package/dist/stack.js +5 -5
  131. package/dist/standing.d.ts +2 -1
  132. package/dist/standing.js +3 -2
  133. package/dist/store.d.ts +9 -0
  134. package/dist/store.js +24 -0
  135. package/dist/strays.d.ts +2 -1
  136. package/dist/strays.js +20 -13
  137. package/dist/stretch.d.ts +112 -0
  138. package/dist/stretch.js +9 -0
  139. package/dist/subject.d.ts +142 -0
  140. package/dist/subject.js +1 -0
  141. package/dist/tape.d.ts +9 -5
  142. package/dist/tape.js +5 -1
  143. package/dist/then.d.ts +9 -0
  144. package/dist/then.js +57 -0
  145. package/dist/ticker.d.ts +4 -4
  146. package/dist/transport.d.ts +0 -8
  147. package/dist/transport.js +0 -4
  148. package/dist/turns.d.ts +4 -3
  149. package/dist/turns.js +4 -3
  150. package/dist/types/arc.d.ts +12 -11
  151. package/dist/types/channel.d.ts +12 -8
  152. package/dist/types/history.d.ts +35 -19
  153. package/dist/types/mix.d.ts +222 -163
  154. package/dist/types/motion.d.ts +151 -0
  155. package/dist/types/motion.js +1 -0
  156. package/dist/types/patch.d.ts +67 -34
  157. package/dist/types/place.d.ts +51 -36
  158. package/dist/types/transport.d.ts +20 -18
  159. package/dist/types/voice.d.ts +136 -70
  160. package/dist/types/watch.d.ts +88 -40
  161. package/dist/types.d.ts +1 -0
  162. package/dist/voice.d.ts +7 -143
  163. package/dist/voice.js +28 -79
  164. package/dist/warn.d.ts +2 -0
  165. package/dist/warn.js +1 -1
  166. package/dist/watch.d.ts +2 -2
  167. package/dist/watch.js +5 -3
  168. package/dist/wave.d.ts +4 -3
  169. package/dist/wave.js +3 -2
  170. package/dist/weigh.d.ts +7 -1
  171. package/dist/weigh.js +27 -0
  172. package/docs/schema.html +212 -34
  173. package/package.json +2 -2
package/dist/lane.js CHANGED
@@ -1,6 +1,15 @@
1
1
  import { unbareLane } from './bare.js';
2
2
  import { frozenTime } from './clock.js';
3
3
  import { motionOf } from './motion.js';
4
+ /**
5
+ * Keeps the weight a probe read from the last fill as `Row.PROBED`, for `weightOf`, before a fill
6
+ * writes the position's new weight; reads `per` only where the copy could change a bit.
7
+ */
8
+ export function keepProbed(data, o, per, q, fills) {
9
+ const w = data[o + 2 /* Row.WEIGHT */];
10
+ if ((w !== data[o + 3 /* Row.PROBED */] || w === 0) && per[q + 5 /* Per.LANE_FILL */] === fills - 1)
11
+ data[o + 3 /* Row.PROBED */] = w;
12
+ }
4
13
  /**
5
14
  * Below what share of its subjects probed last frame a lane stops filling, and above what share it
6
15
  * fills again; between the two it keeps doing what it did, so a host near the line does not flip
@@ -70,6 +79,8 @@ export class Lane {
70
79
  fade = 1;
71
80
  /** Whether the voice has no fade in or out this fill, so its envelope is 1 for every subject. */
72
81
  flat = false;
82
+ /** The fill that last ran it, so what it remembers within one fill is that fill's. */
83
+ ran = -1;
73
84
  /** The voice's clock at this fill, set before `one` runs its subjects. */
74
85
  elapsedNow = 0;
75
86
  /** A motion patch's state, which the lane samples in place of calling the patch. */
@@ -80,8 +91,12 @@ export class Lane {
80
91
  bareFill = -1;
81
92
  bareElapsed = Number.NaN;
82
93
  bareSeeks = Number.NaN;
83
- /** The locus its voice shares, gathered before the voices are folded in order. */
94
+ /** The role its voice shares, gathered before the voices are folded in order. */
84
95
  group = null;
96
+ /** The `Arg.MARK` it was last placed under, for a role run a subject at a time to put back. */
97
+ mark = Number.NaN;
98
+ /** Whether it took every subject when it opened, so a fill makes each one's first call. */
99
+ every = false;
85
100
  voiceAt() {
86
101
  return this.voice;
87
102
  }
@@ -128,6 +143,22 @@ export class Lane {
128
143
  positionOf(slot) {
129
144
  return slot < this.at.length ? this.at[slot] - 1 : -1;
130
145
  }
146
+ /**
147
+ * Room for `n` positions and subject numbers below `cap`, for a voice that reaches every subject:
148
+ * grown as it met them, a voice replaced every frame regrew both from nothing each frame.
149
+ */
150
+ reserve(n, cap) {
151
+ if (n * 8 /* Row.STRIDE */ > this.data.length) {
152
+ const data = new Float64Array(n * 8 /* Row.STRIDE */);
153
+ data.set(this.data);
154
+ this.data = data;
155
+ }
156
+ if (cap > this.at.length) {
157
+ const at = new Int32Array(cap);
158
+ at.set(this.at);
159
+ this.at = at;
160
+ }
161
+ }
131
162
  /** Gives a subject the voice reaches a position, from what its record fixed on first sight. */
132
163
  add(slot, held) {
133
164
  const p = this.list.length;
package/dist/lanes.d.ts CHANGED
@@ -1,15 +1,16 @@
1
1
  import type { Column } from './columns.js';
2
- import { copy } from './copyout.js';
2
+ import { type Copier, copy } from './copyout.js';
3
3
  import { type Crowd, row } from './crowd.js';
4
4
  import { call, foldKeys, one, parting, readKeyed, reset, run, runEveryone, signalled, subjectAt } from './fill.js';
5
5
  import { fold, foldDelta, foldInto, foldRun, gate, gather, unplain } from './gather.js';
6
- import { type Lane, type Laned, type Locus } from './lane.js';
7
- import type { Watcher } from './motions.js';
6
+ import { type Lane, type Laned, type Role } from './lane.js';
8
7
  import { absent, Numbers } from './numbers.js';
9
8
  import { Owed } from './owed.js';
10
9
  import { Placed } from './placed.js';
10
+ import type { Watcher } from './stretch.js';
11
+ import type { Subject } from './subject.js';
11
12
  import type { Channel } from './types.js';
12
- import type { Subject, Voice } from './voice.js';
13
+ import type { Voice } from './voice.js';
13
14
  /** What a lane fill asks of the mix it belongs to. */
14
15
  export interface LaneHost<I, O> {
15
16
  readonly voices: readonly Voice<I, O>[];
@@ -79,15 +80,20 @@ export declare class Lanes<I, O> implements Watcher {
79
80
  unlinked: Float64Array<ArrayBuffer>;
80
81
  /** Whether two crowds share a channel, so their rows must interleave in voice order. */
81
82
  overlap: boolean;
82
- /** The loci whose voices are on lanes, every member of each on one. */
83
- loci: Locus<I, O>[];
83
+ /** The roles whose voices are on lanes, every member of each on one. */
84
+ roles: Role<I, O>[];
84
85
  /** The lane and record `one` is folding, whose band state a rest-less channel reads. */
85
86
  folding: Lane<I, O> | null;
87
+ /** Whether a probe meeting lanes has `one` make their first call for its subject: see `meet`. */
88
+ meeting: boolean;
89
+ /** The lanes the last subject met and `foldNow` folded at its probe, and the fill it did so in. */
90
+ folds: number[];
91
+ foldsFill: number;
86
92
  rec: Subject<unknown> | null;
87
93
  /** The numbers a fill's methods hand each other: see `Arg`. */
88
94
  readonly arg: Float64Array<ArrayBuffer>;
89
- /** While a fill gathers a locus: that locus and the member gathering, where folds go instead. */
90
- into: Locus<I, O> | null;
95
+ /** While a fill gathers a role: that role and the member gathering, where folds go instead. */
96
+ into: Role<I, O> | null;
91
97
  intoId: number;
92
98
  readonly lawsByKey: Map<string, Float64Array<ArrayBufferLike>>;
93
99
  lastLaw: Float64Array | null;
@@ -102,6 +108,8 @@ export declare class Lanes<I, O> implements Watcher {
102
108
  /** By subject number, the laned voices its probes fold on the general path this fill. */
103
109
  readonly owed: Owed;
104
110
  laned: Laned[];
111
+ /** Writes `laned`'s values for a subject into a pose; made at the first copy after they change. */
112
+ copier: Copier | null;
105
113
  /** By kit slot, the laned channel there. */
106
114
  bySlot: (Laned | undefined)[];
107
115
  /** By kit slot, whether a fill leaves the channel's values for probes to copy. */
@@ -138,6 +146,13 @@ export declare class Lanes<I, O> implements Watcher {
138
146
  subjects: (I | typeof absent | undefined)[];
139
147
  /** Whether a probe has held a subject since the last fill let them go. */
140
148
  holding: boolean;
149
+ /**
150
+ * Whether probes hold their subjects for the next fill: while the last fill looked one up. A
151
+ * fill of bare tweens never does, and holding cost a store a subject and a clear a frame.
152
+ */
153
+ holds: boolean;
154
+ /** How many times a fill or a probe has looked a subject up by number, for `holds`. */
155
+ lookups: number;
141
156
  /** While a fill runs, so a probe a patch makes from inside it takes the general path. */
142
157
  filling: boolean;
143
158
  /**
package/dist/lanes.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { lit } from './bound.js';
1
2
  import { copy } from './copyout.js';
2
3
  import { row } from './crowd.js';
3
4
  import { call, fillAll, foldKeys, one, parting, readKeyed, reset, run, runEveryone, signalled, subjectAt, } from './fill.js';
@@ -33,14 +34,19 @@ export class Lanes {
33
34
  unlinked = new Float64Array(0);
34
35
  /** Whether two crowds share a channel, so their rows must interleave in voice order. */
35
36
  overlap = false;
36
- /** The loci whose voices are on lanes, every member of each on one. */
37
- loci = [];
37
+ /** The roles whose voices are on lanes, every member of each on one. */
38
+ roles = [];
38
39
  /** The lane and record `one` is folding, whose band state a rest-less channel reads. */
39
40
  folding = null;
41
+ /** Whether a probe meeting lanes has `one` make their first call for its subject: see `meet`. */
42
+ meeting = false;
43
+ /** The lanes the last subject met and `foldNow` folded at its probe, and the fill it did so in. */
44
+ folds = [];
45
+ foldsFill = -1;
40
46
  rec = null;
41
47
  /** The numbers a fill's methods hand each other: see `Arg`. */
42
- arg = new Float64Array(6 /* Arg.SIZE */);
43
- /** While a fill gathers a locus: that locus and the member gathering, where folds go instead. */
48
+ arg = new Float64Array(8 /* Arg.SIZE */);
49
+ /** While a fill gathers a role: that role and the member gathering, where folds go instead. */
44
50
  into = null;
45
51
  intoId = 0;
46
52
  lawsByKey = new Map();
@@ -56,6 +62,8 @@ export class Lanes {
56
62
  /** By subject number, the laned voices its probes fold on the general path this fill. */
57
63
  owed = new Owed();
58
64
  laned = [];
65
+ /** Writes `laned`'s values for a subject into a pose; made at the first copy after they change. */
66
+ copier = null;
59
67
  /** By kit slot, the laned channel there. */
60
68
  bySlot = [];
61
69
  /** By kit slot, whether a fill leaves the channel's values for probes to copy. */
@@ -92,6 +100,13 @@ export class Lanes {
92
100
  subjects = [];
93
101
  /** Whether a probe has held a subject since the last fill let them go. */
94
102
  holding = false;
103
+ /**
104
+ * Whether probes hold their subjects for the next fill: while the last fill looked one up. A
105
+ * fill of bare tweens never does, and holding cost a store a subject and a clear a frame.
106
+ */
107
+ holds = true;
108
+ /** How many times a fill or a probe has looked a subject up by number, for `holds`. */
109
+ lookups = 0;
95
110
  /** While a fill runs, so a probe a patch makes from inside it takes the general path. */
96
111
  filling = false;
97
112
  /**
@@ -275,8 +290,10 @@ export class Lanes {
275
290
  if (lane) {
276
291
  per[o + 3 /* Per.LANE_PROBE */] = probe;
277
292
  per[o + 5 /* Per.LANE_FILL */] = this.fills;
278
- this.subjects[slot] = subject;
279
- this.holding = true;
293
+ if (this.holds) {
294
+ this.subjects[slot] = subject;
295
+ this.holding = true;
296
+ }
280
297
  }
281
298
  else
282
299
  per[o + 4 /* Per.GENERAL_PROBE */] = probe;
@@ -381,8 +398,12 @@ export class Lanes {
381
398
  paid(id, slot, w) {
382
399
  const lane = this.byId.get(id);
383
400
  const p = lane === undefined ? -1 : lane.positionOf(slot);
384
- if (p >= 0)
385
- lane.data[p * 8 /* Row.STRIDE */ + 2 /* Row.WEIGHT */] = w;
401
+ if (p < 0)
402
+ return;
403
+ lane.data[p * 8 /* Row.STRIDE */ + 2 /* Row.WEIGHT */] = w;
404
+ const b = lane.voice.bound;
405
+ if (b !== null && w !== 0)
406
+ lit(b, slot, this.fills);
386
407
  }
387
408
  /**
388
409
  * Whether a subject's laned values are at their channels' rest, as `atRest` reads the pose `copy`
@@ -1,5 +1,5 @@
1
- import { type Cut } from './transport.js';
2
- import type { Left, Subject } from './voice.js';
1
+ import { type Cut } from './cut.js';
2
+ import type { Left, Subject } from './subject.js';
3
3
  /**
4
4
  * Under history, the records subjects left a voice with, while a seek or a read back may reach
5
5
  * them: in the order they left, the oldest let go of from the front, and each subject's found
package/dist/leavings.js CHANGED
@@ -1,5 +1,5 @@
1
+ import { within } from './cut.js';
1
2
  import { Store } from './store.js';
2
- import { within } from './transport.js';
3
3
  /**
4
4
  * Under history, the records subjects left a voice with, while a seek or a read back may reach
5
5
  * them: in the order they left, the oldest let go of from the front, and each subject's found
@@ -0,0 +1,47 @@
1
+ import { type Cut } from './cut.js';
2
+ import type { Motions } from './motions.js';
3
+ import { type Change, type Paging, type Run } from './stretch.js';
4
+ /** A run a release kept, with when: `early` where the release came before anything read its frame. */
5
+ export type Released<I> = {
6
+ subject: I;
7
+ at: number;
8
+ seq: number;
9
+ early: boolean;
10
+ } & Run;
11
+ /**
12
+ * Forgets a subject, so a later ask starts it afresh from `from`. Given the mix time `at` and frame
13
+ * `seq`, under history, its run is kept for a rewind before then, until `reach` passes it.
14
+ */
15
+ export declare function release<I>(this: Motions<I>, subject: I, at?: number, reach?: number, seq?: number, early?: boolean): void;
16
+ /** Puts back each subject released after frame `seq`, as it stood when released. */
17
+ export declare function unrelease<I>(this: Motions<I>, seq: number): void;
18
+ /** Subject `s`'s run as it stands. */
19
+ export declare function runAt<I>(this: Motions<I>, s: number): Run;
20
+ /** A subject's run as plain data, for a voice leaving memory; undefined where it has none. */
21
+ export declare function pack<I>(this: Motions<I>, subject: I): Run | undefined;
22
+ /** Puts a subject's run back as `pack` or a release left it, replacing any it has now. */
23
+ export declare function unpack<I>(this: Motions<I>, subject: I, run: Run): void;
24
+ /** A released run a history store gave back, for a rewind to before its release. */
25
+ export declare function reclaim<I>(this: Motions<I>, subject: I, at: number, seq: number, run: Run & {
26
+ early?: boolean;
27
+ }): void;
28
+ /**
29
+ * The run `subject` had in frame `cut`: the one a release after the cut kept, else the one it has
30
+ * now; undefined where it has neither. Its changes after the cut are still on it.
31
+ */
32
+ export declare function runThen<I>(this: Motions<I>, subject: I, cut: Cut): Run | undefined;
33
+ /**
34
+ * Stretches a history store gave back, each by the frame it was in force by: each life of the
35
+ * subject, ended by a release, takes those from its own, ahead of the earliest it still keeps.
36
+ */
37
+ export declare function restretch<I>(this: Motions<I>, subject: I, back: readonly Paging[]): void;
38
+ /**
39
+ * The mix time and frame a stretch was in force by, which a store keys it under: when the change
40
+ * that ended it was made, which falls in the same life of the subject.
41
+ */
42
+ export declare function made<I>(this: Motions<I>, c: Change | undefined): {
43
+ at: number;
44
+ seq: number;
45
+ };
46
+ /** Lets go of stretches older than the one in force at `reading.horizon`. */
47
+ export declare function prune<I>(this: Motions<I>, s: number): void;
package/dist/lives.js ADDED
@@ -0,0 +1,180 @@
1
+ import { within } from './cut.js';
2
+ import { absent } from './numbers.js';
3
+ import { reading } from './reading.js';
4
+ const PENDING = 2 /* RunFlag.PENDING */;
5
+ const OLDER = 4 /* RunFlag.OLDER */;
6
+ /** A run that shares nothing with the patch's own. */
7
+ const runOf = (r) => ({
8
+ flags: r.flags,
9
+ stretches: r.stretches.map((seg) => ({ ...seg })),
10
+ pending: r.pending?.map((c) => ({ ...c })),
11
+ });
12
+ /** `segs`, in time order, ahead of `list`: those older than its first, one per time. */
13
+ function ahead(segs, list) {
14
+ const first = list[0].at;
15
+ const out = [];
16
+ for (const seg of [...segs].sort((x, y) => x.at - y.at))
17
+ if (seg.at < first && out[out.length - 1]?.at !== seg.at)
18
+ out.push(seg);
19
+ return out.length === 0 ? list : [...out, ...list];
20
+ }
21
+ /**
22
+ * Forgets a subject, so a later ask starts it afresh from `from`. Given the mix time `at` and frame
23
+ * `seq`, under history, its run is kept for a rewind before then, until `reach` passes it.
24
+ */
25
+ export function release(subject, at, reach = Number.NEGATIVE_INFINITY, seq = Number.NaN, early = false) {
26
+ const s = this.known(subject);
27
+ if (s === undefined)
28
+ return;
29
+ if (at !== undefined) {
30
+ const kept = [];
31
+ for (const e of this.released)
32
+ if (e.at >= reach)
33
+ kept.push(e);
34
+ else
35
+ this.owner?.page(this.ownerId, 'released', e.subject, [
36
+ { at: e.at, seq: e.seq, data: { ...runOf(e), early: e.early } },
37
+ ]);
38
+ this.released = kept;
39
+ this.released.push({ subject, at, seq, early, ...this.runAt(s) });
40
+ }
41
+ this.slots.delete(subject);
42
+ this.numbers.release(s);
43
+ }
44
+ /** Puts back each subject released after frame `seq`, as it stood when released. */
45
+ export function unrelease(seq) {
46
+ const back = this.released.filter((e) => e.seq > seq).sort((a, b) => a.seq - b.seq);
47
+ if (back.length === 0)
48
+ return;
49
+ this.released = this.released.filter((e) => e.seq <= seq);
50
+ const done = new Set();
51
+ for (const e of back) {
52
+ if (done.has(e.subject))
53
+ continue;
54
+ done.add(e.subject);
55
+ this.unpack(e.subject, e);
56
+ }
57
+ }
58
+ /** Subject `s`'s run as it stands. */
59
+ export function runAt(s) {
60
+ const older = this.older?.[s];
61
+ const pending = this.pending?.[s];
62
+ return {
63
+ flags: this.runs[this.base(s) + 1],
64
+ stretches: older === undefined ? [this.latest(s)] : [...older, this.latest(s)],
65
+ pending: pending === undefined ? undefined : [...pending],
66
+ };
67
+ }
68
+ /** A subject's run as plain data, for a voice leaving memory; undefined where it has none. */
69
+ export function pack(subject) {
70
+ const s = this.known(subject);
71
+ return s === undefined ? undefined : runOf(this.runAt(s));
72
+ }
73
+ /** Puts a subject's run back as `pack` or a release left it, replacing any it has now. */
74
+ export function unpack(subject, run) {
75
+ const now = this.known(subject);
76
+ if (now !== undefined) {
77
+ this.slots.delete(subject);
78
+ this.numbers.release(now);
79
+ }
80
+ if (this.n < 0)
81
+ this.size(run.stretches[0].x0.length);
82
+ const s = this.take(subject);
83
+ this.runs[this.base(s) + 1] = run.flags & ~(OLDER | PENDING);
84
+ const latest = run.stretches[run.stretches.length - 1];
85
+ this.write(s, latest);
86
+ if (run.stretches.length > 1) {
87
+ if (this.older === null)
88
+ this.older = [undefined];
89
+ this.older[s] = run.stretches.slice(0, -1);
90
+ this.flag(s, OLDER, true);
91
+ }
92
+ if (run.pending !== undefined) {
93
+ if (this.pending === null)
94
+ this.pending = [undefined];
95
+ this.pending[s] = run.pending;
96
+ this.flag(s, PENDING, true);
97
+ }
98
+ }
99
+ /** A released run a history store gave back, for a rewind to before its release. */
100
+ export function reclaim(subject, at, seq, run) {
101
+ if (this.released.some((e) => e.seq === seq && Object.is(e.subject, subject)))
102
+ return;
103
+ this.released.push({ subject, at, seq, ...run, early: run.early === true });
104
+ }
105
+ /**
106
+ * The run `subject` had in frame `cut`: the one a release after the cut kept, else the one it has
107
+ * now; undefined where it has neither. Its changes after the cut are still on it.
108
+ */
109
+ export function runThen(subject, cut) {
110
+ let life;
111
+ for (const e of this.released)
112
+ if (Object.is(e.subject, subject) &&
113
+ !within(cut, e.seq, e.early) &&
114
+ (life === undefined || e.seq < life.seq))
115
+ life = e;
116
+ if (life !== undefined)
117
+ return life;
118
+ const s = this.known(subject);
119
+ return s === undefined ? undefined : this.runAt(s);
120
+ }
121
+ /**
122
+ * Stretches a history store gave back, each by the frame it was in force by: each life of the
123
+ * subject, ended by a release, takes those from its own, ahead of the earliest it still keeps.
124
+ */
125
+ export function restretch(subject, back) {
126
+ const lives = this.released
127
+ .filter((e) => Object.is(e.subject, subject))
128
+ .sort((x, y) => x.seq - y.seq);
129
+ let from = Number.NEGATIVE_INFINITY;
130
+ const within = (to) => back.filter((r) => r.seq > from && r.seq <= to).map((r) => r.data);
131
+ for (const e of lives) {
132
+ e.stretches = ahead(within(e.seq), e.stretches);
133
+ from = e.seq;
134
+ }
135
+ const s = this.known(subject);
136
+ if (s === undefined)
137
+ return;
138
+ const older = this.older?.[s] ?? [];
139
+ const list = ahead(within(Number.POSITIVE_INFINITY), [...older, this.latest(s)]);
140
+ if (list.length === older.length + 1)
141
+ return;
142
+ if (this.older === null)
143
+ this.older = [undefined];
144
+ this.older[s] = list.slice(0, -1);
145
+ this.flag(s, OLDER, true);
146
+ }
147
+ /**
148
+ * The mix time and frame a stretch was in force by, which a store keys it under: when the change
149
+ * that ended it was made, which falls in the same life of the subject.
150
+ */
151
+ export function made(c) {
152
+ const made = c?.made;
153
+ if (made === undefined || Number.isNaN(made))
154
+ return { at: this.owner?.now() ?? Number.NaN, seq: this.owner?.seq() ?? Number.NaN };
155
+ return { at: made, seq: c?.seq ?? Number.NaN };
156
+ }
157
+ /** Lets go of stretches older than the one in force at `reading.horizon`. */
158
+ export function prune(s) {
159
+ const list = this.older?.[s];
160
+ if (list === undefined)
161
+ return;
162
+ let n = 0;
163
+ while (n < list.length &&
164
+ (n + 1 < list.length ? list[n + 1].at : this.runs[this.base(s)]) <=
165
+ reading.horizon)
166
+ n++;
167
+ if (n === 0)
168
+ return;
169
+ const out = list.splice(0, n);
170
+ const subject = this.numbers.subject(s);
171
+ if (this.owner !== null && subject !== absent)
172
+ this.owner.page(this.ownerId, 'stretch', subject, out.map((seg, i) => {
173
+ const next = out[i + 1] ?? list[0];
174
+ return { ...this.made(next === undefined ? this.causes?.[s] : next.change), data: seg };
175
+ }));
176
+ if (list.length === 0) {
177
+ this.older[s] = undefined;
178
+ this.flag(s, OLDER, false);
179
+ }
180
+ }
package/dist/meet.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import type { Lanes } from './lanes.js';
2
- import type { Subject } from './voice.js';
2
+ import type { Subject } from './subject.js';
3
3
  /**
4
4
  * Whether the lanes a probe of the subject at `slot` just met (`met`) can be folded on the general
5
- * path after the lanes' values: none is in a locus or a crowd, the subject owes none already this
5
+ * path after the lanes' values: none is in a role or a crowd, the subject owes none already this
6
6
  * fill, and each comes after every other laned voice in voice order, so folding it last folds in
7
7
  * the general path's order.
8
8
  */
package/dist/meet.js CHANGED
@@ -1,6 +1,7 @@
1
+ import { open, shut } from './everyone.js';
1
2
  /**
2
3
  * Whether the lanes a probe of the subject at `slot` just met (`met`) can be folded on the general
3
- * path after the lanes' values: none is in a locus or a crowd, the subject owes none already this
4
+ * path after the lanes' values: none is in a role or a crowd, the subject owes none already this
4
5
  * fill, and each comes after every other laned voice in voice order, so folding it last folds in
5
6
  * the general path's order.
6
7
  */
@@ -88,13 +89,98 @@ export function meet(lanes, slot, subject, head, lane) {
88
89
  // after every other laned voice, the general path folds just those onto the lanes' values;
89
90
  // otherwise it reads the general path all frame.
90
91
  const o = slot * 6 /* Per.SLOT */;
91
- if (lane && lanes.per[o + 2 /* Per.IDLE */] === 0 && owable(lanes, slot)) {
92
- lanes.owed.owe(slot, lanes.met, lanes.metN);
93
- return true;
92
+ if (lane && lanes.per[o + 2 /* Per.IDLE */] === 0) {
93
+ const again = foldsAgain(lanes, slot);
94
+ if (again || owable(lanes, slot)) {
95
+ if (!foldNow(lanes, slot, subject, again))
96
+ lanes.owed.owe(slot, lanes.met, lanes.metN);
97
+ return true;
98
+ }
94
99
  }
95
100
  lanes.per[o + 0 /* Per.FILLED */] = lanes.fills;
96
101
  return false;
97
102
  }
103
+ /**
104
+ * Whether the subject at `slot` met the lanes the last subject `foldNow` folded met, in the same
105
+ * fill: `owable` and `foldNow`'s own checks then answer as they did for that one.
106
+ */
107
+ function foldsAgain(lanes, slot) {
108
+ if (lanes.foldsFill !== lanes.fills || lanes.metCrowd || lanes.owes(slot))
109
+ return false;
110
+ const folds = lanes.folds;
111
+ const n = lanes.metN;
112
+ if (folds.length !== n)
113
+ return false;
114
+ for (let i = 0; i < n; i++)
115
+ if (folds[i] !== lanes.met[i])
116
+ return false;
117
+ return true;
118
+ }
119
+ /**
120
+ * Folds the lanes a probe of the subject at `slot` just met onto the lanes' values, making each
121
+ * one's first call or read for the subject as the general path would for this probe, where every
122
+ * one is a stateless fn or keys voice that ran this fill; false, leaving them owed, where any is
123
+ * not. The fold is last in voice order, as `owable` found, so it folds as the general path would
124
+ * after the fill.
125
+ */
126
+ function foldNow(lanes, slot, subject, again) {
127
+ if (lanes.filling)
128
+ return false;
129
+ // What `unread` answers, which `one` asks before this probe's own stamp is written.
130
+ const lately = lanes.lately;
131
+ const o = slot * 6 /* Per.SLOT */;
132
+ if (lately >= 0 &&
133
+ !(lanes.per[o + 3 /* Per.LANE_PROBE */] > lately ||
134
+ lanes.per[o + 4 /* Per.GENERAL_PROBE */] > lately))
135
+ return false;
136
+ const met = lanes.met;
137
+ const n = lanes.metN;
138
+ for (let i = 0; i < n && !again; i++) {
139
+ const lane = lanes.byId.get(met[i]);
140
+ const v = lane.voice;
141
+ if (lane.ran !== lanes.fills ||
142
+ lane.motion !== undefined ||
143
+ v.keeping ||
144
+ typeof v.spec.weight === 'function' ||
145
+ (v.sharing && v.everyone === null))
146
+ return false;
147
+ }
148
+ if (!again) {
149
+ lanes.folds = met.slice(0, n);
150
+ lanes.foldsFill = lanes.fills;
151
+ }
152
+ // The subject is at hand: `call` would look it up again by its number.
153
+ if (lanes.subjects[slot] === undefined) {
154
+ lanes.subjects[slot] = subject;
155
+ lanes.holding = true;
156
+ }
157
+ lanes.meeting = true;
158
+ try {
159
+ for (let i = 0; i < n; i++) {
160
+ const lane = lanes.byId.get(met[i]);
161
+ const v = lane.voice;
162
+ const p = lane.positionOf(slot);
163
+ if (!v.sharing) {
164
+ lanes.one(lane, p, slot);
165
+ continue;
166
+ }
167
+ const rec = v.everyone;
168
+ open(v, rec, slot);
169
+ lanes.one(lane, p, slot);
170
+ // Its patch just made kept state, and the record is now that subject's own.
171
+ if (!v.keeping)
172
+ shut(v, rec, slot, lanes.cap);
173
+ }
174
+ }
175
+ finally {
176
+ lanes.meeting = false;
177
+ }
178
+ // A patch that just made kept state takes its voice off its lane: check the next subject again.
179
+ for (let i = 0; i < n; i++)
180
+ if (lanes.byId.get(met[i]).voice.keeping)
181
+ lanes.foldsFill = -1;
182
+ return true;
183
+ }
98
184
  /** Places a crowd voice's row on the subject a probe met, as `Lane.add` adds a position. */
99
185
  function place(lanes, voice, slot, subject, from, head) {
100
186
  const c = lanes.crowdOf.get(voice.id);