@mark1russell7/layout-contract 0.2.7 → 0.2.9

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/README.md CHANGED
@@ -20,6 +20,8 @@ The package has no runtime dependencies.
20
20
  | The promotion guards | `outputGroups`, `OutputGroup`, `exercised`, `ExerciseEvidence` |
21
21
  | The portfolio | `portfolio`, `Portfolio`, `PortfolioKey`, `PortfolioLevel`, `PortfolioOptions`, `PortfolioPick`, `PortfolioTrace`, `Member`, `MemberTrace`, `MemberStatus`, `pickMoves` |
22
22
  | The pick rule | `selectBest`, `SelectCandidate`, `SelectLevel`, `Selection`, `PickSpec` |
23
+ | The corpus checks | `ClaimBreak`, `checkDeclared`, `declaredCounts`, `declaredClaimOf`, `DeclaredClaim`, `Declares`, `corpusHashOf`, `CorpusEntry` |
24
+ | The split files | `checkSplit`, `splitFolds`, `leaveOneSeedOut`, `splitFileName`, `SPLIT_SCHEMA`, `CorpusSplit`, `SplitFold`, `SplitCorpus` |
23
25
  | The turn | `turnBox`, `turnPort`, `turnMargin`, `turnRegion`, `turnSide`, `turnPoint` |
24
26
  | The tolerances | `EPS`, `RECORD_EPS`, `RECORD_SCALE`, `Tolerance`, `DEFAULT_TOLERANCE` |
25
27
  | The hard rules | `countOverlaps`, `countObstacleHits`, `regionHitsRect`, `rectInRegion`, `countOutside`, `ContainerTest`, `countPortsOffBorder`, `packHard`, `PackHard`, `placedRect`, `checkInput` |
@@ -34,6 +36,7 @@ The package has no runtime dependencies.
34
36
  - **The container rule.** `in-container` counts each box that goes out of the container made smaller by `inset`. The box counts with its margin. By default, a shaped container counts as its bounding box.
35
37
  - **The exact container.** Give `{ exactContainer: true }` to `packHard` or `countOutside`. Then a box of a shaped container with the size `fixed` must stay inside the shape. A cut corner, a notch and a hole count. A container with a free side keeps the bounding test. The default changes to the exact test at the next breaking version.
36
38
  - **The output hash.** `outputHash` gives the first 16 hex characters of the SHA-256 of the canonical JSON of an output. Each number goes to the grid of `RECORD_EPS` first.
39
+ - **The corpus checks.** `checkDeclared` and `checkSplit` give a list of `ClaimBreak`. An empty list tells that each claim is true. `corpusHashOf` gives the corpus hash of `packing`.
37
40
  - **The tolerances.** `EPS` (`1e-6`) is for the float compares in an engine. `RECORD_EPS` (`1e-3`) is for the compares on a written file. A record writes the tolerance of each hard rule in its field `tolerance`. The defaults are `overlap` `0.001` and `through` `0.5`.
38
41
 
39
42
  ## An example
@@ -53,4 +56,4 @@ const placed = applyPack(input, output);
53
56
  const hard = packHard(input, output);
54
57
  ```
55
58
 
56
- The formats are in `docs/FORMAT.md` of `packing` and `docs/LAYOUT-RECORDS.md` of `highways`. The joint measures are in `docs/JOINT-MEASURES.md` of this repository, and the joint corpus is in `docs/JOINT-CORPUS.md`. The promotion guards are in `docs/PROMOTION.md`. The portfolio and its pick rule are in `docs/PORTFOLIO.md`.
59
+ The formats are in `docs/FORMAT.md` of `packing` and `docs/LAYOUT-RECORDS.md` of `highways`. The joint measures are in `docs/JOINT-MEASURES.md` of this repository, and the joint corpus is in `docs/JOINT-CORPUS.md`. The promotion guards are in `docs/PROMOTION.md`. The portfolio and its pick rule are in `docs/PORTFOLIO.md`. The corpus checks and the split files are in `docs/CORPUS-CHECKS.md`.
package/build/index.d.ts CHANGED
@@ -595,6 +595,11 @@ export interface PortfolioOptions {
595
595
  readonly maxMembers?: number;
596
596
  /** The clock in milliseconds. The default is `performance.now`, or `Date.now` without `performance`. */
597
597
  readonly now?: () => number;
598
+ /**
599
+ * The members that get an output hash. The default `all` hashes each member with the status `ran`. A hash that throws then gives the status `error`.
600
+ * With `pick`, only member 0 and the winner get a hash, and only when the winner is not member 0. Then a hash that throws makes `pick` throw.
601
+ */
602
+ readonly hashes?: "all" | "pick";
598
603
  }
599
604
  /** The status of a member in a pick. */
600
605
  export type MemberStatus = "ran" | "cut" | "error";
@@ -606,7 +611,10 @@ export interface MemberTrace {
606
611
  readonly values?: Readonly<Record<string, number>>;
607
612
  /** The time of the run of the member, in milliseconds. */
608
613
  readonly ms?: number;
609
- /** The hash of the output of the member. */
614
+ /**
615
+ * The hash of the output of the member. Under the option `hashes` "all" (the default), each member with the status `ran` has it.
616
+ * Under `hashes` "pick", it is absent, except for member 0 and the winner when the winner is not member 0.
617
+ */
610
618
  readonly outputHash?: string;
611
619
  /** The message of the error of a member with the status `error`. */
612
620
  readonly error?: string;
@@ -689,4 +697,97 @@ export declare function pickMoves(trace: Pick<PortfolioTrace, "picked">): {
689
697
  * It also throws a `RangeError` for an empty list of members, or for two members with the same id.
690
698
  */
691
699
  export declare function portfolio<I, O>(id: string, members: readonly Member<I, O>[] | ((input: I) => readonly Member<I, O>[]), key: PortfolioKey<I, O>, options?: PortfolioOptions): Portfolio<I, O>;
700
+ /** One broken claim of a corpus, with the ids of the inputs that break it. */
701
+ export interface ClaimBreak {
702
+ /** The name of the claim, for example `declared`, `split` or `index`. */
703
+ readonly claim: string;
704
+ /** The text of the break. */
705
+ readonly detail: string;
706
+ /** The ids of the inputs that break the claim. The list is empty when the break has no input. */
707
+ readonly inputs: readonly string[];
708
+ }
709
+ /** The claim `claims.declared` of a manifest: the count of the inputs for each declared property. */
710
+ export interface DeclaredClaim {
711
+ /** A property with fewer inputs than this count is thin. */
712
+ readonly minCount: number;
713
+ /** The ids of the properties with a count below `minCount`. */
714
+ readonly thin: readonly string[];
715
+ /** The count of the inputs for each property id. */
716
+ readonly counts: Readonly<Record<string, number>>;
717
+ }
718
+ /** The hook that gives the ids of the properties that one input declares. */
719
+ export type Declares<I> = (input: I) => readonly string[];
720
+ /**
721
+ * This function gives the count of the inputs for each property id. Each id of `ids` has a count, also when the count is 0.
722
+ * An id from `declares` that is not in `ids` comes after the ids of `ids`, in the order of the first input that declares it.
723
+ */
724
+ export declare function declaredCounts<I>(inputs: Iterable<I>, declares: Declares<I>, ids?: readonly string[]): Record<string, number>;
725
+ /**
726
+ * This function reads the field `claims.declared` of a manifest. A manifest without it gives `undefined`.
727
+ * It throws an `Error` for a claim without a whole `minCount`, a list `thin` of ids and a whole count for each id.
728
+ */
729
+ export declare function declaredClaimOf(manifest: object): DeclaredClaim | undefined;
730
+ /**
731
+ * This function checks the claim `claims.declared` against the inputs. It gives one break `declared` for each problem. An empty list tells that the claim is true.
732
+ *
733
+ * - `ids` are the property ids. The default is the ids of `claim.counts`.
734
+ * - Each id of `ids` has a count in `claim.counts`, and `claim.counts` has no other id. Each count is the count of the inputs.
735
+ * - An id with a count below `minCount` is in `thin`. Each id of `thin` is an id of `ids`.
736
+ * - Each id that `declares` gives is an id of `ids`.
737
+ */
738
+ export declare function checkDeclared<I>(inputs: Iterable<I>, declares: Declares<I>, claim: DeclaredClaim, ids?: readonly string[]): ClaimBreak[];
739
+ /** The id and the hash of one input of a corpus. */
740
+ export interface CorpusEntry {
741
+ readonly id: string;
742
+ readonly hash: string;
743
+ }
744
+ /**
745
+ * This function gives the hash of a corpus: the first 16 hex characters of a SHA-256. The text of the SHA-256 is `canonicalJson`
746
+ * of the corpus id and the `[id, hash]` of each input, in the order of the manifest. A change of one input changes the hash.
747
+ */
748
+ export declare function corpusHashOf(corpus: string, inputs: readonly CorpusEntry[]): string;
749
+ /** The schema of a split file. */
750
+ export declare const SPLIT_SCHEMA = "corpus-split/1";
751
+ /** One fold of a split: the seeds that fit a selector and the seeds that score it. */
752
+ export interface SplitFold {
753
+ readonly train: readonly number[];
754
+ readonly heldOut: readonly number[];
755
+ }
756
+ /** A split file `corpus-split/1`. Its name is `<corpus id>.<corpus hash>.json`. It is beside the corpus, so the corpus does not change. */
757
+ export interface CorpusSplit {
758
+ readonly schema: typeof SPLIT_SCHEMA;
759
+ readonly corpus: string;
760
+ /** The hash of the corpus: `corpusHashOf` of its manifest. */
761
+ readonly corpusHash: string;
762
+ /** The splits by name. Each split has one fold or more. */
763
+ readonly splits: Readonly<Record<string, {
764
+ readonly folds: readonly SplitFold[];
765
+ }>>;
766
+ }
767
+ /** The fields of a manifest that the split check reads. */
768
+ export interface SplitCorpus {
769
+ readonly corpus: string;
770
+ readonly specs: readonly {
771
+ readonly seeds: readonly number[];
772
+ }[];
773
+ readonly inputs: readonly CorpusEntry[];
774
+ }
775
+ /** This function gives the name of the split file of a corpus. */
776
+ export declare function splitFileName(corpus: string, hash: string): string;
777
+ /**
778
+ * This function checks a split file against a manifest. It gives one break `split` for each problem. An empty list tells that the file is good.
779
+ *
780
+ * - The schema is `corpus-split/1`, the corpus is the id of the manifest and `corpusHash` is `corpusHashOf` of the manifest.
781
+ * - Each split has one fold or more. In each fold, `train` and `heldOut` are lists of seeds, not empty and with no common seed.
782
+ * - Each seed is a seed of a spec of the manifest.
783
+ * - With `name`, the file has the split `name`.
784
+ */
785
+ export declare function checkSplit(split: unknown, corpus: SplitCorpus, name?: string): ClaimBreak[];
786
+ /**
787
+ * This function gives a copy of the folds of the split `name` of a split file. `where` names the file in the text of an error.
788
+ * It throws an `Error` for each problem of `checkSplit`, and then for an unknown split name.
789
+ */
790
+ export declare function splitFolds(split: unknown, corpus: SplitCorpus, name: string, where?: string): SplitFold[];
791
+ /** This function gives the folds that hold out one seed each, in the order of the seeds. Each fold trains on the other seeds. */
792
+ export declare function leaveOneSeedOut(seeds: readonly number[]): SplitFold[];
692
793
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/geometry.ts","../src/tolerance.ts","../src/input.ts","../src/output.ts","../src/record.ts","../src/rules.ts","../src/turn.ts","../src/measures.ts","../src/strategy.ts","../src/random.ts","../src/edges.ts","../src/hash.ts","../src/portfolio.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/geometry.ts","../src/tolerance.ts","../src/input.ts","../src/output.ts","../src/record.ts","../src/rules.ts","../src/turn.ts","../src/measures.ts","../src/strategy.ts","../src/random.ts","../src/edges.ts","../src/hash.ts","../src/portfolio.ts","../src/corpus.ts"],"mappings":"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"}
package/build/index.js CHANGED
@@ -1233,9 +1233,10 @@ function portfolio(id, members, key, options = {}) {
1233
1233
  if (levelIds.has(level.id)) throw new RangeError(`The portfolio "${id}" has two levels with the id "${level.id}".`);
1234
1234
  levelIds.add(level.id);
1235
1235
  }
1236
- const { budgetMs, maxMembers } = options;
1236
+ const { budgetMs, maxMembers, hashes = "all" } = options;
1237
1237
  if (budgetMs !== void 0 && !(budgetMs >= 0)) throw new RangeError(`The portfolio "${id}" has the budgetMs ${budgetMs}. It must be 0 or more.`);
1238
1238
  if (maxMembers !== void 0 && !(Number.isInteger(maxMembers) && maxMembers >= 1)) throw new RangeError(`The portfolio "${id}" has the maxMembers ${maxMembers}. It must be a whole number of 1 or more.`);
1239
+ if (hashes !== "all" && hashes !== "pick") throw new RangeError(`The portfolio "${id}" has the hashes ${String(hashes)}. It must be "all" or "pick".`);
1239
1240
  if (typeof members !== "function") checkMembers(id, members);
1240
1241
  const levels = [{
1241
1242
  id: "hard",
@@ -1248,6 +1249,14 @@ function portfolio(id, members, key, options = {}) {
1248
1249
  spec: l.spec
1249
1250
  }))];
1250
1251
  const now = options.now ?? defaultClock();
1252
+ /** This function gives the hash of the output of a member. A hash that throws causes a `RangeError`. */
1253
+ const hashOf = (m, output) => {
1254
+ try {
1255
+ return key.hash ? key.hash(output) : outputHash(output);
1256
+ } catch (e) {
1257
+ throw new RangeError(`The hash of the member "${m.id}" of the portfolio "${id}" throws: ${messageOf(e)}`, { cause: e });
1258
+ }
1259
+ };
1251
1260
  const evaluate = (m, input, output) => {
1252
1261
  const values = {};
1253
1262
  const measure = (level, f) => {
@@ -1263,17 +1272,16 @@ function portfolio(id, members, key, options = {}) {
1263
1272
  try {
1264
1273
  measure("hard", () => key.hard(input, output));
1265
1274
  for (const level of key.levels) measure(level.id, () => level.value(input, output));
1266
- let hash;
1267
- try {
1268
- hash = key.hash ? key.hash(output) : outputHash(output);
1269
- } catch (e) {
1270
- throw new RangeError(`The hash of the member "${m.id}" of the portfolio "${id}" throws: ${messageOf(e)}`, { cause: e });
1271
- }
1275
+ if (hashes === "pick") return {
1276
+ ok: true,
1277
+ output,
1278
+ values
1279
+ };
1272
1280
  return {
1273
1281
  ok: true,
1274
1282
  output,
1275
1283
  values,
1276
- hash
1284
+ hash: hashOf(m, output)
1277
1285
  };
1278
1286
  } catch (e) {
1279
1287
  return {
@@ -1326,7 +1334,12 @@ function portfolio(id, members, key, options = {}) {
1326
1334
  };
1327
1335
  }
1328
1336
  if (result.ok) {
1329
- traces.push({
1337
+ traces.push(result.hash === void 0 ? {
1338
+ id: m.id,
1339
+ status: "ran",
1340
+ values: result.values,
1341
+ ms
1342
+ } : {
1330
1343
  id: m.id,
1331
1344
  status: "ran",
1332
1345
  values: result.values,
@@ -1351,6 +1364,10 @@ function portfolio(id, members, key, options = {}) {
1351
1364
  if (ran.length === 0) throw firstError.error;
1352
1365
  const { winner, kept } = selectBest(ran, levels);
1353
1366
  const winnerIndex = traces.findIndex((t) => t.id === winner);
1367
+ if (hashes === "pick" && winnerIndex > 0 && traces[0].status === "ran") for (const i of [0, winnerIndex]) traces[i] = {
1368
+ ...traces[i],
1369
+ outputHash: hashOf(list[i], outputs.get(traces[i].id))
1370
+ };
1354
1371
  const base = traces[0];
1355
1372
  const trace = {
1356
1373
  members: traces,
@@ -1376,6 +1393,153 @@ function portfolio(id, members, key, options = {}) {
1376
1393
  run: (input) => pick(input).output
1377
1394
  };
1378
1395
  }
1379
- export { DEFAULT_TOLERANCE, EDGE_DENSITY, EDGE_STEP_VERSION, EPS, JOINT_MEASURES, JOINT_MOVED_MIN, RECORD_EPS, RECORD_SCALE, SIDES, applyPack, boundsOf, canonicalJson, checkInput, compareMeasure, countObstacleHits, countOutside, countOverlaps, countPortsOffBorder, documentHash, edgeStep, exercised, fnv1a32, grow, marginSides, minEffectOf, mulberry32, outputGroups, outputHash, overlapOf, packHard, pickMoves, placedRect, portfolio, randomInt, rectInRegion, regionHitsRect, selectBest, sha256Hex, stepRandom, toLayoutRecord, toPackInput, translate, turnBox, turnMargin, turnPoint, turnPort, turnRegion, turnSide };
1396
+ // src/corpus.ts
1397
+ /**
1398
+ * This function gives the count of the inputs for each property id. Each id of `ids` has a count, also when the count is 0.
1399
+ * An id from `declares` that is not in `ids` comes after the ids of `ids`, in the order of the first input that declares it.
1400
+ */
1401
+ function declaredCounts(inputs, declares, ids = []) {
1402
+ const out = Object.fromEntries(ids.map((id) => [id, 0]));
1403
+ for (const input of inputs) for (const id of declares(input)) out[id] = (out[id] ?? 0) + 1;
1404
+ return out;
1405
+ }
1406
+ /** This function tells if a value is a plain object. */
1407
+ const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
1408
+ /**
1409
+ * This function reads the field `claims.declared` of a manifest. A manifest without it gives `undefined`.
1410
+ * It throws an `Error` for a claim without a whole `minCount`, a list `thin` of ids and a whole count for each id.
1411
+ */
1412
+ function declaredClaimOf(manifest) {
1413
+ const claims = manifest["claims"];
1414
+ if (claims === void 0) return void 0;
1415
+ if (!isObject(claims)) throw new Error("The field claims of a corpus file is an object.");
1416
+ const declared = claims["declared"];
1417
+ if (declared === void 0) return void 0;
1418
+ if (!isObject(declared)) throw new Error("The field claims.declared of a corpus file is an object.");
1419
+ const { minCount, thin, counts } = declared;
1420
+ if (!Number.isInteger(minCount) || minCount < 0) throw new Error("The field claims.declared.minCount is a whole number of 0 or more.");
1421
+ if (!Array.isArray(thin) || !thin.every((id) => typeof id === "string")) throw new Error("The field claims.declared.thin is a list of property ids.");
1422
+ if (!isObject(counts) || !Object.values(counts).every((n) => Number.isInteger(n) && n >= 0)) throw new Error("The field claims.declared.counts gives a whole count for each property id.");
1423
+ return {
1424
+ minCount,
1425
+ thin: [...thin],
1426
+ counts: { ...counts }
1427
+ };
1428
+ }
1429
+ /**
1430
+ * This function checks the claim `claims.declared` against the inputs. It gives one break `declared` for each problem. An empty list tells that the claim is true.
1431
+ *
1432
+ * - `ids` are the property ids. The default is the ids of `claim.counts`.
1433
+ * - Each id of `ids` has a count in `claim.counts`, and `claim.counts` has no other id. Each count is the count of the inputs.
1434
+ * - An id with a count below `minCount` is in `thin`. Each id of `thin` is an id of `ids`.
1435
+ * - Each id that `declares` gives is an id of `ids`.
1436
+ */
1437
+ function checkDeclared(inputs, declares, claim, ids = Object.keys(claim.counts)) {
1438
+ const computed = declaredCounts(inputs, declares, ids);
1439
+ const out = [];
1440
+ for (const id of ids) if (!(id in claim.counts)) out.push(`the property "${id}" has no count`);
1441
+ else if (claim.counts[id] !== computed[id]) out.push(`the property "${id}" has the count ${claim.counts[id]}, but the inputs give ${computed[id]}`);
1442
+ for (const id of Object.keys(claim.counts)) if (!ids.includes(id)) out.push(`"${id}" is not a declared property`);
1443
+ for (const id of ids) if ((computed[id] ?? 0) < claim.minCount && !claim.thin.includes(id)) out.push(`the property "${id}" has ${computed[id]} inputs, below minCount ${claim.minCount}, and it is not in thin`);
1444
+ for (const id of claim.thin) if (!ids.includes(id)) out.push(`"${id}" of thin is not a declared property`);
1445
+ for (const id of Object.keys(computed)) if (!ids.includes(id)) out.push(`${computed[id]} inputs declare "${id}", which is not a declared property`);
1446
+ return out.map((detail) => ({
1447
+ claim: "declared",
1448
+ detail,
1449
+ inputs: []
1450
+ }));
1451
+ }
1452
+ /**
1453
+ * This function gives the hash of a corpus: the first 16 hex characters of a SHA-256. The text of the SHA-256 is `canonicalJson`
1454
+ * of the corpus id and the `[id, hash]` of each input, in the order of the manifest. A change of one input changes the hash.
1455
+ */
1456
+ function corpusHashOf(corpus, inputs) {
1457
+ return sha256Hex(canonicalJson({
1458
+ corpus,
1459
+ inputs: inputs.map((i) => [i.id, i.hash])
1460
+ })).slice(0, 16);
1461
+ }
1462
+ /** The schema of a split file. */
1463
+ const SPLIT_SCHEMA = "corpus-split/1";
1464
+ /** This function gives the name of the split file of a corpus. */
1465
+ function splitFileName(corpus, hash) {
1466
+ return `${corpus}.${hash}.json`;
1467
+ }
1468
+ /** This function tells if a value is a list of whole numbers. */
1469
+ const isSeeds = (value) => Array.isArray(value) && value.every((s) => Number.isInteger(s));
1470
+ /**
1471
+ * This function checks a split file against a manifest. It gives one break `split` for each problem. An empty list tells that the file is good.
1472
+ *
1473
+ * - The schema is `corpus-split/1`, the corpus is the id of the manifest and `corpusHash` is `corpusHashOf` of the manifest.
1474
+ * - Each split has one fold or more. In each fold, `train` and `heldOut` are lists of seeds, not empty and with no common seed.
1475
+ * - Each seed is a seed of a spec of the manifest.
1476
+ * - With `name`, the file has the split `name`.
1477
+ */
1478
+ function checkSplit(split, corpus, name) {
1479
+ const out = [];
1480
+ const done = () => out.map((detail) => ({
1481
+ claim: "split",
1482
+ detail,
1483
+ inputs: []
1484
+ }));
1485
+ const f = split;
1486
+ if (f === null || typeof f !== "object") {
1487
+ out.push("the split file is not an object");
1488
+ return done();
1489
+ }
1490
+ if (f.schema !== "corpus-split/1") out.push(`the schema is ${String(f.schema)}, not ${SPLIT_SCHEMA}`);
1491
+ if (f.corpus !== corpus.corpus) out.push(`the corpus is ${String(f.corpus)}, not ${corpus.corpus}`);
1492
+ const hash = corpusHashOf(corpus.corpus, corpus.inputs);
1493
+ if (f.corpusHash !== hash) out.push(`the corpusHash is ${String(f.corpusHash)}, but the corpus gives ${hash}`);
1494
+ const seeds = new Set(corpus.specs.flatMap((s) => s.seeds));
1495
+ if (f.splits === null || typeof f.splits !== "object") {
1496
+ out.push("the field splits is not an object");
1497
+ return done();
1498
+ }
1499
+ for (const [key, value] of Object.entries(f.splits)) {
1500
+ const folds = value?.folds;
1501
+ if (!Array.isArray(folds) || folds.length === 0) {
1502
+ out.push(`the split ${key} has no folds`);
1503
+ continue;
1504
+ }
1505
+ folds.forEach((fold, k) => {
1506
+ const at = `the fold ${k} of the split ${key}`;
1507
+ const { train, heldOut } = fold ?? {};
1508
+ if (!isSeeds(train) || train.length === 0) out.push(`${at} has no train seeds`);
1509
+ if (!isSeeds(heldOut) || heldOut.length === 0) out.push(`${at} has no heldOut seeds`);
1510
+ if (!isSeeds(train) || !isSeeds(heldOut)) return;
1511
+ const common = train.filter((s) => heldOut.includes(s));
1512
+ if (common.length > 0) out.push(`${at} has the seeds ${common.join(", ")} in train and in heldOut`);
1513
+ const unknown = [...train, ...heldOut].filter((s) => !seeds.has(s));
1514
+ if (unknown.length > 0) out.push(`${at} has the seeds ${unknown.join(", ")}, which are not in the corpus`);
1515
+ });
1516
+ }
1517
+ if (name !== void 0 && !Object.hasOwn(f.splits, name)) out.push(`the split file has no split ${name}. Its splits are ${Object.keys(f.splits).join(", ")}`);
1518
+ return done();
1519
+ }
1520
+ /**
1521
+ * This function gives a copy of the folds of the split `name` of a split file. `where` names the file in the text of an error.
1522
+ * It throws an `Error` for each problem of `checkSplit`, and then for an unknown split name.
1523
+ */
1524
+ function splitFolds(split, corpus, name, where = "The split file") {
1525
+ const breaks = checkSplit(split, corpus);
1526
+ if (breaks.length > 0) throw new Error(`${where} is not good: ${breaks.map((b) => b.detail).join("; ")}.`);
1527
+ const splits = split.splits;
1528
+ const found = Object.hasOwn(splits, name) ? splits[name] : void 0;
1529
+ if (found === void 0) throw new Error(`${where} has no split ${name}. Its splits are ${Object.keys(splits).join(", ")}.`);
1530
+ return found.folds.map((f) => ({
1531
+ train: [...f.train],
1532
+ heldOut: [...f.heldOut]
1533
+ }));
1534
+ }
1535
+ /** This function gives the folds that hold out one seed each, in the order of the seeds. Each fold trains on the other seeds. */
1536
+ function leaveOneSeedOut(seeds) {
1537
+ const sorted = [...new Set(seeds)].sort((a, b) => a - b);
1538
+ return sorted.map((s) => ({
1539
+ train: sorted.filter((t) => t !== s),
1540
+ heldOut: [s]
1541
+ }));
1542
+ }
1543
+ export { DEFAULT_TOLERANCE, EDGE_DENSITY, EDGE_STEP_VERSION, EPS, JOINT_MEASURES, JOINT_MOVED_MIN, RECORD_EPS, RECORD_SCALE, SIDES, SPLIT_SCHEMA, applyPack, boundsOf, canonicalJson, checkDeclared, checkInput, checkSplit, compareMeasure, corpusHashOf, countObstacleHits, countOutside, countOverlaps, countPortsOffBorder, declaredClaimOf, declaredCounts, documentHash, edgeStep, exercised, fnv1a32, grow, leaveOneSeedOut, marginSides, minEffectOf, mulberry32, outputGroups, outputHash, overlapOf, packHard, pickMoves, placedRect, portfolio, randomInt, rectInRegion, regionHitsRect, selectBest, sha256Hex, splitFileName, splitFolds, stepRandom, toLayoutRecord, toPackInput, translate, turnBox, turnMargin, turnPoint, turnPort, turnRegion, turnSide };
1380
1544
 
1381
1545
  //# sourceMappingURL=index.js.map