@finbheara/domain 0.0.0-stage → 0.2.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 (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +273 -2
  3. package/dist/age.js +420 -0
  4. package/dist/ceili.js +15 -0
  5. package/dist/chunks/place-7gqecaxd.js +201 -0
  6. package/dist/chunks/place-g6yw4cq6.js +134 -0
  7. package/dist/chunks/place-kr42294h.js +267 -0
  8. package/dist/chunks/place-qbp3xfb4.js +7412 -0
  9. package/dist/clrg-regions.js +603 -0
  10. package/dist/dance-types.js +14 -0
  11. package/dist/dance.js +41 -0
  12. package/dist/noun-index.js +233 -0
  13. package/dist/place.js +36 -0
  14. package/dist/school.js +2269 -0
  15. package/dist/tune.js +23 -0
  16. package/dist/types/age-types.d.ts +31 -0
  17. package/dist/types/age.d.ts +84 -0
  18. package/dist/types/ceili.d.ts +53 -0
  19. package/dist/types/clrg-regions.d.ts +31 -0
  20. package/dist/types/dance-match.d.ts +28 -0
  21. package/dist/types/dance-types.d.ts +9 -0
  22. package/dist/types/dance.d.ts +14 -0
  23. package/dist/types/noun-index.d.ts +67 -0
  24. package/dist/types/place-tree.d.ts +4 -0
  25. package/dist/types/place-types.d.ts +24 -0
  26. package/dist/types/place.d.ts +98 -0
  27. package/dist/types/school-compare.d.ts +39 -0
  28. package/dist/types/school-encoder.d.ts +8 -0
  29. package/dist/types/school-exception-vocabulary.d.ts +23 -0
  30. package/dist/types/school-fluff-vocabulary.d.ts +16 -0
  31. package/dist/types/school-fused-prefix.d.ts +1 -0
  32. package/dist/types/school-identity.d.ts +9 -0
  33. package/dist/types/school-institution-vocabulary.d.ts +20 -0
  34. package/dist/types/school-location.d.ts +16 -0
  35. package/dist/types/school-match-key.d.ts +5 -0
  36. package/dist/types/school-name.d.ts +82 -0
  37. package/dist/types/school-tokens.d.ts +20 -0
  38. package/dist/types/school.d.ts +11 -0
  39. package/dist/types/tune.d.ts +63 -0
  40. package/package.json +73 -5
package/dist/age.js ADDED
@@ -0,0 +1,420 @@
1
+ var YOUNGEST_FLOOR = 0;
2
+ var ADULT_FLOOR = 18;
3
+ var MAJORS_FLOOR_YEARS = 3;
4
+ var UNDER_CAP = Object.freeze({ plain: 2, exceptional: 4 });
5
+ var UNDER_CAP_MAX_TOP = 21;
6
+ var YOUNG_FLOOR = Object.freeze({ hard: 2, soft: 4 });
7
+ var MIN_MAJOR_AGE = 4;
8
+ var MIN_FEIS_AGE = 3;
9
+ var DANCE_UP_YEARS = 1;
10
+ var ageOnJanuary1 = (year, born) => year - born - 1;
11
+ var yearsSinceBirth = (year, born) => year - born;
12
+ var UNDER = /^(?:u|under\s*)(\d+)$/i;
13
+ var OVER = /^(?:o|over\s*)(\d+)$/i;
14
+ var RANGE = /^u?(\d+)\s*[-–]\s*(\d+)$/i;
15
+ var ADULT_WORD = /^(?:adults?|ad|advanced|masters?)$/i;
16
+ var SENIOR_WORD = /^seniors?$/i;
17
+ var NO_LADDER = Object.freeze({ caps: [], overs: [], ranges: [] });
18
+ function maxWhere(xs, pred) {
19
+ let best;
20
+ for (const x of xs)
21
+ if (pred(x) && (best === undefined || x > best))
22
+ best = x;
23
+ return best;
24
+ }
25
+ function minWhere(xs, pred) {
26
+ let best;
27
+ for (const x of xs)
28
+ if (pred(x) && (best === undefined || x < best))
29
+ best = x;
30
+ return best;
31
+ }
32
+ function readRange(a, b, ladder) {
33
+ if (!Number.isFinite(a) || !Number.isFinite(b) || b <= a)
34
+ return {
35
+ kind: "range",
36
+ interval: null,
37
+ refusal: "backwards-range",
38
+ clauses: [],
39
+ parts: [a, b]
40
+ };
41
+ const overlapping = ladder.ranges.includes(a) || ladder.ranges.includes(b + 1);
42
+ const bottom = !ladder.caps.some((c) => c <= a);
43
+ const atAdultFloor = bottom && a >= ADULT_FLOOR;
44
+ const youngest = bottom && !atAdultFloor;
45
+ const onRung = ladder.caps.includes(a);
46
+ const floor = overlapping || atAdultFloor || onRung ? a : Math.max(YOUNGEST_FLOOR, a - 1);
47
+ const clauses = [overlapping ? "range-overlapping" : "range-inclusive"];
48
+ if (youngest)
49
+ clauses.push("youngest-absorbs");
50
+ else if (atAdultFloor)
51
+ clauses.push("range-adult-floor");
52
+ else if (!overlapping)
53
+ clauses.push(onRung ? "range-floor-on-rung" : "range-floor-widened");
54
+ return {
55
+ kind: "range",
56
+ interval: { lo: youngest ? YOUNGEST_FLOOR : floor, hi: overlapping ? b : b + 1 },
57
+ refusal: null,
58
+ clauses,
59
+ parts: [a, b]
60
+ };
61
+ }
62
+ function read(token, ladder, top) {
63
+ const r = RANGE.exec(token);
64
+ if (r !== null)
65
+ return readRange(Number(r[1]), Number(r[2]), ladder);
66
+ if (ADULT_WORD.test(token))
67
+ return {
68
+ kind: "adult",
69
+ interval: { lo: ADULT_FLOOR, hi: Number.POSITIVE_INFINITY },
70
+ refusal: null,
71
+ clauses: ["and-over-no-ceiling"],
72
+ parts: []
73
+ };
74
+ if (SENIOR_WORD.test(token)) {
75
+ const top = maxWhere(ladder.caps, () => true);
76
+ return top === undefined ? { kind: "senior", interval: null, refusal: "senior-without-ladder", clauses: [], parts: [] } : {
77
+ kind: "senior",
78
+ interval: { lo: top, hi: Number.POSITIVE_INFINITY },
79
+ refusal: null,
80
+ clauses: ["and-over-no-ceiling"],
81
+ parts: []
82
+ };
83
+ }
84
+ const u = UNDER.exec(token);
85
+ if (u !== null) {
86
+ const n = Number(u[1]);
87
+ const below = maxWhere(ladder.caps, (c) => c < n);
88
+ return {
89
+ kind: "under",
90
+ interval: { lo: below ?? YOUNGEST_FLOOR, hi: n },
91
+ refusal: null,
92
+ clauses: [below === undefined ? "youngest-absorbs" : "under-floored-on-rung"],
93
+ parts: [n]
94
+ };
95
+ }
96
+ const o = OVER.exec(token);
97
+ if (o !== null) {
98
+ const n = Number(o[1]);
99
+ const above = top ? undefined : minWhere(ladder.overs, (c) => c > n);
100
+ if (above === undefined)
101
+ return {
102
+ kind: "over",
103
+ interval: { lo: n, hi: Number.POSITIVE_INFINITY },
104
+ refusal: null,
105
+ clauses: ["and-over-no-ceiling"],
106
+ parts: [n]
107
+ };
108
+ const gapped = ladder.caps.length > 0 && !ladder.caps.some((c) => c > n && c < above);
109
+ return {
110
+ kind: "over",
111
+ interval: { lo: n, hi: gapped ? Number.POSITIVE_INFINITY : above },
112
+ refusal: null,
113
+ clauses: [gapped ? "over-ceiling-refused-gap" : "over-ceiling-from-next-over"],
114
+ parts: [n]
115
+ };
116
+ }
117
+ return { kind: null, interval: null, refusal: "not-a-band", clauses: [], parts: [] };
118
+ }
119
+
120
+ class AgeMisuseError extends Error {
121
+ misuse;
122
+ constructor(misuse, detail) {
123
+ super(`age: ${misuse}: ${detail}`);
124
+ this.name = "AgeMisuseError";
125
+ this.misuse = misuse;
126
+ }
127
+ }
128
+ var display = (iv) => `[${iv.lo},${iv.hi === Number.POSITIVE_INFINITY ? "∞" : iv.hi})`;
129
+ var currentYear = () => new Date().getUTCFullYear();
130
+ function withRelations(a) {
131
+ const self = a;
132
+ return Object.assign(self, {
133
+ fitsIn: (container) => {
134
+ if (isAveraged(container))
135
+ throw new AgeMisuseError("average-as-container", `${self.ag2} cannot fit inside an average age (${String(container)}); an average is not a dancer`);
136
+ const r = ageRelation(self, container);
137
+ return r.relation === "same" || r.reason === "b-contains-a";
138
+ },
139
+ overlaps: (other) => {
140
+ const r = ageRelation(self, other).relation;
141
+ return r === "same" || r === "contains" || r === "overlaps";
142
+ }
143
+ });
144
+ }
145
+ function fromPrint(print, context = {}) {
146
+ const ladder = context.ladder ?? NO_LADDER;
147
+ const year = context.year === undefined ? currentYear() : context.year;
148
+ const ag1 = print.trim().toLowerCase();
149
+ const reading = ag1 === "" ? { kind: null, interval: null, refusal: null, clauses: [], parts: [] } : read(print.trim(), ladder, context.top === true);
150
+ const iv = reading.interval;
151
+ const arm = ag1 === "" ? "empty" : iv === null ? "open" : "band";
152
+ const ag2 = iv !== null ? display(iv) : ag1 === "" ? "" : `${ag1}?`;
153
+ const source = sourceOf(reading);
154
+ return withRelations({
155
+ [Symbol.toStringTag]: "Age",
156
+ toString: () => ag2,
157
+ arm,
158
+ kind: reading.kind,
159
+ ag0: print,
160
+ ag1,
161
+ ag2,
162
+ parts: reading.parts,
163
+ length: ag2.length,
164
+ refusal: reading.refusal,
165
+ source,
166
+ clauses: reading.clauses,
167
+ ladder,
168
+ year,
169
+ isEmpty: () => arm === "empty",
170
+ isAge: () => iv !== null,
171
+ isExact: () => {
172
+ if (iv === null || year === null)
173
+ return false;
174
+ const b = birthYearsOfInterval(iv, year);
175
+ return b.from !== null && b.from === b.to;
176
+ },
177
+ isAndOver: () => iv !== null && iv.hi === Number.POSITIVE_INFINITY,
178
+ isAdult: () => reading.kind === "adult",
179
+ interval: () => iv,
180
+ birthYears: (y) => {
181
+ const at = y ?? year;
182
+ if (at === null)
183
+ throw new Error(`Age.from(${JSON.stringify(print)}): birthYears needs a competition year`);
184
+ return birthYearsOfInterval(iv, at);
185
+ },
186
+ floorObserved: () => reading.kind !== "under" || iv === null ? null : reading.clauses.includes("under-floored-on-rung")
187
+ });
188
+ }
189
+ function fromYOB(from, to = from) {
190
+ for (const y of [from, to])
191
+ if (y !== null && !Number.isInteger(y))
192
+ throw new RangeError(`Age.fromYOB(${from}, ${to}): a year of birth is a whole number`);
193
+ if (from === null && to === null)
194
+ throw new RangeError("Age.fromYOB(null, null): at least one end is a year");
195
+ if (from !== null && to !== null && from > to)
196
+ throw new RangeError(`Age.fromYOB(${from}, ${to}): the earliest year comes first`);
197
+ const exact = from !== null && from === to;
198
+ const ag0 = exact ? String(from) : `${from ?? ""}..${to ?? ""}`;
199
+ const ag2 = `b${ag0}`;
200
+ const years = Object.freeze({ from, to });
201
+ return withRelations({
202
+ [Symbol.toStringTag]: "Age",
203
+ toString: () => ag2,
204
+ arm: "yob",
205
+ kind: "yob",
206
+ ag0,
207
+ ag1: ag0,
208
+ ag2,
209
+ parts: exact ? [from] : [from, to].filter((y) => y !== null),
210
+ length: ag2.length,
211
+ refusal: null,
212
+ source: "printed-yob",
213
+ clauses: [],
214
+ ladder: NO_LADDER,
215
+ year: null,
216
+ isEmpty: () => false,
217
+ isAge: () => true,
218
+ isExact: () => exact,
219
+ isAndOver: () => from === null,
220
+ isAdult: () => false,
221
+ interval: () => null,
222
+ birthYears: () => years,
223
+ floorObserved: () => null
224
+ });
225
+ }
226
+ var Age = Object.freeze({ from: fromPrint, fromYOB });
227
+ function sourceOf(r) {
228
+ const iv = r.interval;
229
+ if (iv === null)
230
+ return "open";
231
+ switch (r.kind) {
232
+ case "range":
233
+ return "printed-range";
234
+ case "over":
235
+ return iv.hi === Number.POSITIVE_INFINITY ? "printed-band" : "sibling-narrowed";
236
+ case "adult":
237
+ case "senior":
238
+ return "printed-band";
239
+ case "under":
240
+ return iv.lo > YOUNGEST_FLOOR ? "sibling-narrowed" : "printed-band";
241
+ default:
242
+ return "open";
243
+ }
244
+ }
245
+ function birthYearsOfInterval(iv, year) {
246
+ if (iv === null)
247
+ return { from: null, to: null };
248
+ return {
249
+ from: iv.hi === Number.POSITIVE_INFINITY ? null : year - iv.hi,
250
+ to: Math.min(year - iv.lo - 1, year - MAJORS_FLOOR_YEARS)
251
+ };
252
+ }
253
+ var inBirthYears = (a) => a.arm === "yob" || a.year !== null;
254
+ function spanOf(a) {
255
+ if (!a.isAge())
256
+ return null;
257
+ if (inBirthYears(a)) {
258
+ const b = a.birthYears();
259
+ return {
260
+ lo: b.from ?? Number.NEGATIVE_INFINITY,
261
+ hi: b.to ?? Number.POSITIVE_INFINITY
262
+ };
263
+ }
264
+ const iv = a.interval();
265
+ return { lo: iv.lo, hi: iv.hi - 1 };
266
+ }
267
+ function ageRelation(a, b) {
268
+ for (const x of [a, b])
269
+ if (isAveraged(x))
270
+ throw new AgeMisuseError("average-in-relation", `${String(x)} is an average, not a dancer's birth years; ask its fitsIn`);
271
+ if (inBirthYears(a) !== inBirthYears(b))
272
+ throw new Error("ageRelation: one reading is in birth years and the other has no competition year; read both with a year, or neither");
273
+ if (!a.isAge())
274
+ return { relation: "silent", reason: "a-not-a-band" };
275
+ if (!b.isAge())
276
+ return { relation: "silent", reason: "b-not-a-band" };
277
+ const sa = spanOf(a);
278
+ const sb = spanOf(b);
279
+ const years = inBirthYears(a);
280
+ if (sa.hi < sb.lo)
281
+ return { relation: "disjoint", reason: years ? "a-older" : "a-younger" };
282
+ if (sb.hi < sa.lo)
283
+ return { relation: "disjoint", reason: years ? "a-younger" : "a-older" };
284
+ if (sa.lo === sb.lo && sa.hi === sb.hi)
285
+ return { relation: "same", reason: "equal" };
286
+ if (sa.lo <= sb.lo && sb.hi <= sa.hi)
287
+ return { relation: "contains", reason: "a-contains-b" };
288
+ if (sb.lo <= sa.lo && sa.hi <= sb.hi)
289
+ return { relation: "contains", reason: "b-contains-a" };
290
+ return { relation: "overlaps", reason: "partial" };
291
+ }
292
+ function canOverlap(...ages) {
293
+ for (let i = 0;i < ages.length; i++)
294
+ for (let j = i + 1;j < ages.length; j++)
295
+ if (ageRelation(ages[i], ages[j]).relation === "disjoint")
296
+ return false;
297
+ return true;
298
+ }
299
+ var AVERAGE_AGE_DECIMALS = 2;
300
+ var HUNDREDTHS = 10 ** AVERAGE_AGE_DECIMALS;
301
+ function roundedMeanHundredths(k, n) {
302
+ if (!Number.isInteger(k) || !Number.isInteger(n) || n <= 0)
303
+ throw new RangeError(`roundedMeanHundredths(${k}, ${n}): whole numbers, n > 0`);
304
+ return Math.floor((2 * HUNDREDTHS * k + n) / (2 * n));
305
+ }
306
+ function isAveraged(a) {
307
+ return a[Symbol.toStringTag] === "AverageAge";
308
+ }
309
+ function averageAge(ages) {
310
+ if (ages.length === 0)
311
+ throw new AgeMisuseError("empty-average", "averageAge needs at least one exact age");
312
+ let sum = 0;
313
+ for (const [i, a] of ages.entries()) {
314
+ if (isAveraged(a))
315
+ throw new AgeMisuseError("average-of-averages", `input ${i} (${String(a)}) is already an average; average the members instead`);
316
+ if (!a.isExact())
317
+ throw new AgeMisuseError("not-exact", `input ${i} (${String(a)}) is not one birth year; an average takes exact ages only`);
318
+ sum += a.birthYears().from;
319
+ }
320
+ const n = ages.length;
321
+ const members = Object.freeze([...ages]);
322
+ const hundredths = (year) => roundedMeanHundredths(n * (year - 1) - sum, n);
323
+ const self = {
324
+ [Symbol.toStringTag]: "AverageAge",
325
+ provenance: `average of ${n}`,
326
+ members,
327
+ n,
328
+ birthYearSum: sum,
329
+ meanBirthYear: sum / n,
330
+ toString: () => `avg(${n}):b${sum / n}`,
331
+ isExact: () => false,
332
+ meanAgeHundredths: hundredths,
333
+ meanAge: (year) => hundredths(year) / HUNDREDTHS,
334
+ fitsIn: (container) => {
335
+ if (isAveraged(container))
336
+ throw new AgeMisuseError("average-as-container", `nothing fits inside an average age (${String(container)})`);
337
+ if (container.year === null)
338
+ throw new AgeMisuseError("average-container-no-year", `${String(container)} has no competition year to take the average age in`);
339
+ const iv = container.interval();
340
+ if (iv === null)
341
+ return false;
342
+ const h = hundredths(container.year);
343
+ return iv.lo * HUNDREDTHS <= h && h < iv.hi * HUNDREDTHS;
344
+ },
345
+ birthYears: () => {
346
+ throw new AgeMisuseError("average-birth-years", `an average of ${n} has no year of birth; read meanBirthYear, which is not a dancer's`);
347
+ }
348
+ };
349
+ return self;
350
+ }
351
+ function birthYearsDisjoint(a, b) {
352
+ if (a.to !== null && b.from !== null && a.to < b.from)
353
+ return true;
354
+ if (b.to !== null && a.from !== null && b.to < a.from)
355
+ return true;
356
+ return false;
357
+ }
358
+ function birthYearsMeet(card, other, slack = 0) {
359
+ if (card.from !== null && other.to !== null && card.from > other.to)
360
+ return false;
361
+ if (card.to !== null && other.from !== null && card.to + slack < other.from)
362
+ return false;
363
+ return true;
364
+ }
365
+ function underCapApplies(bandFrom, year) {
366
+ return bandFrom !== null && year - bandFrom <= UNDER_CAP_MAX_TOP;
367
+ }
368
+ function underCappedYears(b, year, n = UNDER_CAP.plain) {
369
+ if (n > UNDER_CAP.exceptional)
370
+ throw new Error(`under-cap: ${n} is over the exceptional cap of ${UNDER_CAP.exceptional}`);
371
+ if (!underCapApplies(b.from, year))
372
+ return b;
373
+ const cap = b.from + n;
374
+ if (b.to !== null && b.to <= cap)
375
+ return b;
376
+ return { from: b.from, to: cap };
377
+ }
378
+ function underCapRefuses(bandFrom, identityFrom, n) {
379
+ return identityFrom > bandFrom + n;
380
+ }
381
+ function youngFloor(year, oldest) {
382
+ const s = yearsSinceBirth(year, oldest);
383
+ if (s >= YOUNG_FLOOR.soft)
384
+ return "clear";
385
+ if (s < YOUNG_FLOOR.hard)
386
+ return "too-young";
387
+ return "same-school-only";
388
+ }
389
+ var tooYoungForMajor = (year, oldest) => yearsSinceBirth(year, oldest) < MIN_MAJOR_AGE;
390
+ var tooYoungForFeis = (year, oldest) => yearsSinceBirth(year, oldest) < MIN_FEIS_AGE;
391
+ export {
392
+ ADULT_FLOOR,
393
+ AVERAGE_AGE_DECIMALS,
394
+ Age,
395
+ AgeMisuseError,
396
+ DANCE_UP_YEARS,
397
+ MAJORS_FLOOR_YEARS,
398
+ MIN_FEIS_AGE,
399
+ MIN_MAJOR_AGE,
400
+ UNDER_CAP,
401
+ UNDER_CAP_MAX_TOP,
402
+ YOUNGEST_FLOOR,
403
+ YOUNG_FLOOR,
404
+ ageOnJanuary1,
405
+ ageRelation,
406
+ averageAge,
407
+ birthYearsDisjoint,
408
+ birthYearsMeet,
409
+ birthYearsOfInterval,
410
+ canOverlap,
411
+ isAveraged,
412
+ roundedMeanHundredths,
413
+ tooYoungForFeis,
414
+ tooYoungForMajor,
415
+ underCapApplies,
416
+ underCapRefuses,
417
+ underCappedYears,
418
+ yearsSinceBirth,
419
+ youngFloor
420
+ };
package/dist/ceili.js ADDED
@@ -0,0 +1,15 @@
1
+ import {
2
+ CEILI_NODES2,
3
+ allCeilis2,
4
+ ceiliById2,
5
+ ceili2,
6
+ ceiliRelation2
7
+ } from "./chunks/place-7gqecaxd.js";
8
+ import"./chunks/place-g6yw4cq6.js";
9
+ export {
10
+ CEILI_NODES2 as CEILI_NODES,
11
+ allCeilis2 as allCeilis,
12
+ ceili2 as ceili,
13
+ ceiliById2 as ceiliById,
14
+ ceiliRelation2 as ceiliRelation
15
+ };
@@ -0,0 +1,201 @@
1
+ import {
2
+ foldDancePrint,
3
+ matchNodes,
4
+ candidateRelation,
5
+ isDanceTypePrint2
6
+ } from "./place-g6yw4cq6.js";
7
+
8
+ function node(id, name, irish, family, formation, opts = {}) {
9
+ const kind = "round" in formation ? "round" : ("long" in formation) ? "long" : "set";
10
+ return Object.freeze({
11
+ id,
12
+ name,
13
+ irish,
14
+ prints: Object.freeze(opts.prints ?? []),
15
+ irishPrints: Object.freeze(opts.irishPrints ?? []),
16
+ formation: kind,
17
+ hands: "hands" in formation ? formation.hands : null,
18
+ setHands: "setHands" in formation ? formation.setHands : null,
19
+ family
20
+ });
21
+ }
22
+ var CEILI_NODES2 = Object.freeze([
23
+ node("walls-of-limerick", "The Walls of Limerick", "Ballaí Luimní", "handbook", { long: true, setHands: 4 }, {
24
+ prints: ["Walls of Limerick"],
25
+ irishPrints: ["Balla Luimní"]
26
+ }),
27
+ node("four-hand-reel", "The Four-Hand Reel", "Cor Ceathrar", "four-hand", { hands: 4 }, {
28
+ prints: ["Four Hand Reel", "4 Hand Reel"],
29
+ irishPrints: ["Cor Ceathrair", "An Ríl Ceathrair"]
30
+ }),
31
+ node("eight-hand-reel", "The Eight Hand Reel", "Cor Ochtar", "eight-hand", { hands: 8 }, {
32
+ prints: ["8-Hand Reel", "Eight-Hand Reel"],
33
+ irishPrints: ["An Cor Octair"]
34
+ }),
35
+ node("morris-reel", "Morris Reel", "Cor Muirghéis", "eight-hand", { hands: 8 }, {
36
+ prints: ["The Morris Reel"]
37
+ }),
38
+ node("high-cauled-cap", "High Cauled Cap", "Cadhp an Chúil Áird", "eight-hand", { hands: 8 }, {
39
+ prints: ["High-Cauled Cap"],
40
+ irishPrints: ["Caip an Chúil Aird"]
41
+ }),
42
+ node("sixteen-hand-reel", "The Sixteen Hand Reel", "Cor Sé Dhuine Dhéag", "handbook", { hands: 16 }, {
43
+ prints: ["Sixteen-Hand Reel"]
44
+ }),
45
+ node("humours-of-bandon", "Humours of Bandon", "Pléaracha na Bandan", "four-hand", { hands: 4 }),
46
+ node("eight-hand-jig", "Eight Hand Jig", "Port Ochtar", "eight-hand", { hands: 8 }, {
47
+ prints: ["8-Hand Jig", "The Eight Hand Jig"],
48
+ irishPrints: ["An Port Ochtair"]
49
+ }),
50
+ node("siege-of-ennis", "Siege of Ennis", "Ionsaí na hInse", "handbook", {
51
+ long: true,
52
+ setHands: 8
53
+ }),
54
+ node("harvest-time-jig", "Harvest-Time Jig", "Port an Fhómhair", "handbook", { long: true, setHands: 6 }, {
55
+ prints: ["Harvest Time Jig"]
56
+ }),
57
+ node("rince-fada", "Rince Fada", "Rince Fada", "handbook", { long: true, setHands: 4 }, {
58
+ prints: ["Long Dance"]
59
+ }),
60
+ node("bridge-of-athlone", "The Bridge of Athlone", "Droichead Átha Luain", "handbook", {
61
+ long: true,
62
+ setHands: 4
63
+ }),
64
+ node("haste-to-the-wedding", "Haste to the Wedding", "Deifir na Bainise", "handbook", {
65
+ long: true,
66
+ setHands: 4
67
+ }),
68
+ node("siege-of-carrick", "The Siege of Carrick", "Briseadh na Carraige", "handbook", {
69
+ long: true,
70
+ setHands: 4
71
+ }),
72
+ node("antrim-reel", "Antrim Reel", "Cor Aontroma", "handbook", { long: true, setHands: 4 }),
73
+ node("glencar-reel", "Glencar Reel", "Cor Gleann cearr", "handbook", { hands: 6 }),
74
+ node("three-tunes", "The Three Tunes", "Na Trí Foinn", "eight-hand", { hands: 8 }),
75
+ node("st-patricks-day", "St. Patrick's Day", "Lá Fhéile Phádraig", "eight-hand", { hands: 8 }, {
76
+ irishPrints: ["Lá Fhéíle Phádraig"]
77
+ }),
78
+ node("trip-to-the-cottage", "Trip to the Cottage", "Turas Chun An Tigh", "eight-hand", { hands: 8 }, {
79
+ irishPrints: ["Turas go dti an Teachin"]
80
+ }),
81
+ node("rince-mor", "An Rince Mór", "An Rince Mór", "handbook", { round: true }, {
82
+ prints: ["Big Dance"]
83
+ }),
84
+ node("haymakers-jig", "Haymakers' Jig", "Baint an Fhéir", "handbook", { hands: 10, long: true }, {
85
+ prints: ["The Haymakers Jig"]
86
+ }),
87
+ node("fairy-reel", "The Fairy Reel", "Cor na Sióg", "handbook", { long: true, setHands: 6 }, {
88
+ irishPrints: ["Cor na Siog"]
89
+ }),
90
+ node("duke-reel", "The Duke Reel", "Cor an Diuic", "handbook", { hands: 6 }),
91
+ node("lannigans-ball", "Lannigan's Ball", "Bainis Uí Lonagáín", "eight-hand", { hands: 12 }, {
92
+ irishPrints: ["Baineis Ui Lonagain"]
93
+ }),
94
+ node("cross-reel", "The Cross Reel", "An Cor Casta", "eight-hand", { hands: 8 }, {
95
+ prints: ["Cross Reel"]
96
+ }),
97
+ node("waves-of-tory", "The Waves of Tory", "Tonnaí Thoraigh", "handbook", {
98
+ long: true,
99
+ setHands: 4
100
+ }),
101
+ node("rakes-of-mallow", "The Rakes of Mallow", "Reici Mhala", "handbook", {
102
+ long: true,
103
+ setHands: 6
104
+ }),
105
+ node("gates-of-derry", "The Gates of Derry", "Geataí Dhoire", "eight-hand", { hands: 8 }, {
106
+ prints: ["Gates of Derry"]
107
+ }),
108
+ node("sweets-of-may", "The Sweets of May", "Aoibhneas na Bealtaine", "eight-hand", { hands: 8 }, {
109
+ irishPrints: ["Aoidhneas na Bealtaine"]
110
+ }),
111
+ node("bonfire-dance", "Bonfire Dance", "Rince Mór na Tine", "handbook", { round: true })
112
+ ]);
113
+ function makeCeili(n) {
114
+ return Object.freeze({
115
+ [Symbol.toStringTag]: "Ceili",
116
+ id: n.id,
117
+ name: n.name,
118
+ irish: n.irish,
119
+ node: n,
120
+ toString: () => n.name,
121
+ hands: () => n.hands ?? (n.formation === "round" ? "round" : "long"),
122
+ setHands: () => n.setHands,
123
+ isLong: () => n.formation === "long",
124
+ family: () => n.family,
125
+ inHandbook: () => true
126
+ });
127
+ }
128
+ var CEILIS = Object.freeze(CEILI_NODES2.map(makeCeili));
129
+ var CEILI_BY_ID = new Map(CEILIS.map((c) => [c.id, c]));
130
+ var MATCHABLE = CEILIS.map((c) => ({
131
+ id: c.id,
132
+ ceili: c,
133
+ aliases: [c.name, ...c.node.prints, ...c.irish ? [c.irish] : [], ...c.node.irishPrints]
134
+ }));
135
+ function allCeilis2() {
136
+ return CEILIS;
137
+ }
138
+ function ceiliById2(id) {
139
+ return CEILI_BY_ID.get(id);
140
+ }
141
+ function narrow(cands, ctx, misses) {
142
+ let out = cands;
143
+ const step = (label, keep) => {
144
+ const next = out.filter(keep);
145
+ if (next.length > 0)
146
+ out = next;
147
+ else if (out.length > 0)
148
+ misses.push(label);
149
+ };
150
+ if (ctx.list)
151
+ step(`list:${ctx.list}`, (c) => c.family() === ctx.list);
152
+ if (ctx.hands !== undefined) {
153
+ const h = ctx.hands;
154
+ step(`hands:${h}`, (c) => {
155
+ const own = c.hands();
156
+ if (typeof own === "number")
157
+ return own === h || c.family() === "eight-hand" && h === 8;
158
+ return true;
159
+ });
160
+ }
161
+ return out;
162
+ }
163
+ function reading(print, folded, candidates, score, match, refusal, misses) {
164
+ return Object.freeze({
165
+ [Symbol.toStringTag]: "CeiliReading",
166
+ print,
167
+ folded,
168
+ candidates: Object.freeze([...candidates]),
169
+ score,
170
+ match,
171
+ refusal,
172
+ misses: Object.freeze([...misses]),
173
+ ceili: candidates.length === 1 ? candidates[0] : undefined,
174
+ isCeili: () => candidates.length > 0,
175
+ isSingular: () => candidates.length === 1,
176
+ isAmbiguous: () => candidates.length > 1,
177
+ refine(ctx) {
178
+ const m = [...misses];
179
+ return reading(print, folded, narrow(candidates, ctx, m), score, match, refusal, m);
180
+ },
181
+ toString: () => candidates.length === 1 ? candidates[0].name : `${print}?`
182
+ });
183
+ }
184
+ function ceili2(print, ctx = {}) {
185
+ const folded = foldDancePrint(print);
186
+ if (folded === "")
187
+ return reading(print, folded, [], 0, null, "empty", []);
188
+ if (isDanceTypePrint2(print))
189
+ return reading(print, folded, [], 0, null, "dance-type", []);
190
+ const m = matchNodes(folded, MATCHABLE);
191
+ if (m.nodes.length === 0)
192
+ return reading(print, folded, [], m.score, null, "unmatched", []);
193
+ const misses = [];
194
+ const cands = narrow(m.nodes.map((x) => x.ceili), ctx, misses);
195
+ return reading(print, folded, cands, m.score, m.kind, null, misses);
196
+ }
197
+ function ceiliRelation2(a, b) {
198
+ return candidateRelation(a.candidates.map((c) => c.id), b.candidates.map((c) => c.id));
199
+ }
200
+
201
+ export { CEILI_NODES2, allCeilis2, ceiliById2, ceili2, ceiliRelation2 };