erdos-problems 0.3.5 → 0.3.7

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 (23) hide show
  1. package/package.json +1 -1
  2. package/packs/number-theory/problems/848/ANCHOR_MINIMALITY_LEDGER.md +55 -0
  3. package/packs/number-theory/problems/848/ANCHOR_OBSTRUCTION_7_32_57_82.json +293 -0
  4. package/packs/number-theory/problems/848/ANCHOR_OBSTRUCTION_RECON.md +82 -0
  5. package/packs/number-theory/problems/848/ANCHOR_SUBSET_SEARCH_PREFIX10_K3_K4.json +737 -0
  6. package/packs/number-theory/problems/848/BOUNDED_VERIFICATION_PLAN.md +3 -2
  7. package/packs/number-theory/problems/848/{EXACT_SMALL_N_1_3000_CERTIFICATE.md → EXACT_SMALL_N_1_10000_CERTIFICATE.md} +17 -14
  8. package/packs/number-theory/problems/848/EXACT_SMALL_N_1_10000_RESULTS.json +100018 -0
  9. package/packs/number-theory/problems/848/FOUR_ANCHOR_LEMMA_CANDIDATE.md +101 -0
  10. package/packs/number-theory/problems/848/FRONTIER_NOTE.md +12 -7
  11. package/packs/number-theory/problems/848/INTERVAL_WORK_QUEUE.yaml +5 -5
  12. package/packs/number-theory/problems/848/OPS_DETAILS.yaml +25 -8
  13. package/packs/number-theory/problems/848/ROUTE_HISTORY.md +4 -2
  14. package/packs/number-theory/problems/848/VERIFICATION_CERTIFICATE_SPEC.md +3 -0
  15. package/packs/number-theory/problems/848/VERIFICATION_REGIMES.md +4 -4
  16. package/packs/number-theory/problems/848/compute/problem848_anchor_obstruction_scan.mjs +120 -0
  17. package/packs/number-theory/problems/848/compute/problem848_anchor_subset_search.mjs +134 -0
  18. package/packs/number-theory/problems/848/compute/problem848_small_n_exact_scan.mjs +62 -45
  19. package/packs/number-theory/problems/848/context.yaml +13 -6
  20. package/problems/848/EVIDENCE.md +13 -5
  21. package/problems/848/ROUTES.md +4 -4
  22. package/problems/848/SHARE_READY_SUMMARY.md +2 -1
  23. package/packs/number-theory/problems/848/EXACT_SMALL_N_1_3000_RESULTS.json +0 -213791
@@ -0,0 +1,101 @@
1
+ # Problem 848 Four-Anchor Lemma Candidate
2
+
3
+ This note promotes the anchor recon into a theorem-shaped candidate.
4
+
5
+ ## Candidate lemma
6
+
7
+ Let
8
+
9
+ - `a1 = 7`
10
+ - `a2 = 32`
11
+ - `a3 = 57`
12
+ - `a4 = 82`
13
+
14
+ Then for every integer `n >= 30` with `n not equiv 7 (mod 25)`, at least one of
15
+
16
+ - `7n + 1`
17
+ - `32n + 1`
18
+ - `57n + 1`
19
+ - `82n + 1`
20
+
21
+ is squarefree.
22
+
23
+ Equivalently, every outsider `n >= 30` is incompatible with at least one of the
24
+ four fixed anchors `{7, 32, 57, 82}`.
25
+
26
+ ## What is already verified
27
+
28
+ - Exact clique packet through `N = 10000`:
29
+ - `EXACT_SMALL_N_1_10000_CERTIFICATE.md`
30
+ - `EXACT_SMALL_N_1_10000_RESULTS.json`
31
+ - Direct anchor scan for the candidate set:
32
+ - `ANCHOR_OBSTRUCTION_7_32_57_82.json`
33
+ - Small-anchor search ledger:
34
+ - `ANCHOR_MINIMALITY_LEDGER.md`
35
+ - `ANCHOR_SUBSET_SEARCH_PREFIX10_K3_K4.json`
36
+
37
+ The direct anchor scan currently confirms:
38
+
39
+ - no failures in `30..10000`
40
+ - witness usage counts:
41
+ - `7`: `6187`
42
+ - `32`: `2941`
43
+ - `57`: `402`
44
+ - `82`: `42`
45
+
46
+ So the obstruction is real, uneven, and highly structured.
47
+
48
+ ## Why this lemma matters
49
+
50
+ If the four-anchor lemma is proved, then the exact packet is no longer only a
51
+ large finite certificate. It becomes evidence for a more conceptual route:
52
+
53
+ 1. Every outsider is blocked by a fixed finite anchor set.
54
+ 2. That obstruction should force a breakpoint law for clique growth.
55
+ 3. The only remaining work should be a finite startup stub and an exchange or
56
+ monotonicity argument.
57
+
58
+ That would be much closer to a real closure theorem than continuing exact
59
+ verification indefinitely.
60
+
61
+ ## Proof obligations
62
+
63
+ The current candidate is **not** proved. The main obligations are:
64
+
65
+ 1. Show the four-anchor obstruction for all `n >= 30`, not just `n <= 10000`.
66
+ 2. Convert anchor obstruction into a clique-size consequence.
67
+ 3. Explain why the full `7 mod 25` class remains optimal even though the
68
+ obstruction is expressed using only four anchor elements.
69
+ 4. Close the startup range `n < 30` explicitly.
70
+
71
+ ## Most plausible routes from here
72
+
73
+ ### Route A: modular obstruction proof
74
+
75
+ Treat each condition `qn + 1` non-squarefree as a union of square-modulus
76
+ congruence classes and prove that no `n >= 30`, `n not equiv 7 (mod 25)`,
77
+ can lie in all four unions simultaneously.
78
+
79
+ ### Route B: exchange lemma first
80
+
81
+ Prove that any maximal clique not equal to the `7 mod 25` class must contain
82
+ enough of the anchor set that an outsider can be exchanged away without loss.
83
+
84
+ ### Route C: breakpoint law first
85
+
86
+ Use the exact packet to conjecture and then prove directly that
87
+
88
+ `alpha(G_N) = alpha(G_{N-1})` for `N not equiv 7 (mod 25)`
89
+
90
+ and
91
+
92
+ `alpha(G_N) = alpha(G_{N-1}) + 1` for `N equiv 7 (mod 25), N >= 7`.
93
+
94
+ The four-anchor lemma would then become the local obstruction that explains the
95
+ flat steps.
96
+
97
+ ## Current recommendation
98
+
99
+ The next serious move should be a proof attempt for the four-anchor lemma,
100
+ supported by modular residue analysis and square-divisibility ledgers, not just
101
+ another raw exact interval extension.
@@ -32,14 +32,16 @@ The current package is already internally reviewed enough for handoff:
32
32
  - tests and publish-surface checks are green
33
33
 
34
34
  Chosen next lane:
35
- - bounded finite verification under the best imported threshold currently tracked
35
+ - prove the four-anchor obstruction and turn it into a breakpoint theorem candidate
36
36
 
37
37
  Why this lane wins the next cycle:
38
- - the real objective is to close the finite remainder, not just publish a smaller `N0`
39
- - imported threshold progress already exists, so the repo needs an interval-certification
40
- surface to make use of it
41
- - the public thread already contains one verification attempt that was later corrected and
42
- criticized as difficult to verify, so trust and reproducibility have to be first-class
38
+ - the exact verifier now reaches `1..10000`, so the finite-check surface is no longer tiny
39
+ - the exact packet already exhibits a rigid breakpoint law: clique size only jumps at
40
+ `N equiv 7 (mod 25)`
41
+ - the new anchor recon suggests a fixed finite obstruction set `{7, 32, 57, 82}` beyond the
42
+ tiny startup range
43
+ - that is the first route that looks like a real theorem rather than an endless extension of
44
+ raw exact search
43
45
 
44
46
  Read next:
45
47
  - `BOUNDED_VERIFICATION_PLAN.md`
@@ -47,4 +49,7 @@ Read next:
47
49
  - `VERIFICATION_CERTIFICATE_SPEC.md`
48
50
  - `EXTERNAL_VERIFICATION_LEDGER.md`
49
51
  - `INTERVAL_WORK_QUEUE.yaml`
50
- - `EXACT_SMALL_N_1_3000_CERTIFICATE.md`
52
+ - `EXACT_SMALL_N_1_10000_CERTIFICATE.md`
53
+ - `ANCHOR_OBSTRUCTION_RECON.md`
54
+ - `ANCHOR_MINIMALITY_LEDGER.md`
55
+ - `FOUR_ANCHOR_LEMMA_CANDIDATE.md`
@@ -7,22 +7,22 @@ operational_threshold:
7
7
  intervals:
8
8
  - interval_id: N848.V1
9
9
  label: exact_small_n_base_interval
10
- range: "1..3000"
10
+ range: "1..10000"
11
11
  status: done
12
12
  method_class: exact_small_n
13
13
  claim_level: verified
14
- certificate: EXACT_SMALL_N_1_3000_CERTIFICATE.md
15
- data_packet: EXACT_SMALL_N_1_3000_RESULTS.json
14
+ certificate: EXACT_SMALL_N_1_10000_CERTIFICATE.md
15
+ data_packet: EXACT_SMALL_N_1_10000_RESULTS.json
16
16
  next_move: Use this as the current trusted base interval and decide whether the next extension is another exact interval or an imported-computation audit.
17
17
  - interval_id: N848.V2
18
18
  label: exact_small_n_extension
19
- range: "3001..?"
19
+ range: "10001..?"
20
20
  status: ready
21
21
  method_class: exact_small_n
22
22
  claim_level: target
23
23
  certificate: ""
24
24
  data_packet: ""
25
- next_move: Decide whether exact coverage should continue in smaller certified steps beyond `3000` or whether the next gain should come from a different method class.
25
+ next_move: Decide whether exact coverage should continue directly beyond `10000` or whether the next gain should come from a different method class.
26
26
  - interval_id: N848.V3
27
27
  label: imported_computation_audit
28
28
  range: "public_claimed_interval"
@@ -6,7 +6,7 @@ routes:
6
6
  status: active
7
7
  summary: Convert the sufficiently-large-N theorem into a complete all-N resolution without overstating what is already closed or confusing imported thresholds with repo-owned claims.
8
8
  why_now: The share package is already committed, the bounded-verification lane is now frozen, and the repo has a first verified interval.
9
- next_move: Decide whether to extend exact verified coverage beyond `3000` in smaller steps or switch method class.
9
+ next_move: Promote the four-anchor obstruction from recon to a proof-shaped lemma candidate.
10
10
  - route_id: external_threshold_tracking
11
11
  title: External Threshold Tracking
12
12
  status: support
@@ -18,7 +18,7 @@ routes:
18
18
  status: active_support
19
19
  summary: Reduce the finite remainder directly once a threshold is trusted enough to be operationally useful.
20
20
  why_now: Lowering `N0` matters because it reduces this lane, not because "smallest threshold" is the whole problem.
21
- next_move: Build on the exact `1..3000` base interval and decide the next extension rule.
21
+ next_move: Build on the exact `1..10000` base interval by extracting a structural theorem rather than defaulting to another raw interval.
22
22
  - route_id: formalization_coverage_audit
23
23
  title: Formalization Coverage Audit
24
24
  status: support
@@ -30,9 +30,9 @@ tickets:
30
30
  title: Close the decidable gap without confusing imported threshold progress and repo-owned candidate work
31
31
  route_id: finite_check_gap_closure
32
32
  status: active
33
- summary: The repo now has a committed review package for its audited `exp(1420)` candidate, a chosen bounded-verification lane, and an exact verified base interval `1..3000`. The live question is how to extend coverage from that foothold without pretending brute-force still scales cheaply.
34
- current_blocker: The repo has not yet chosen whether the next interval extension should stay in the exact clique-scan regime in smaller certified steps or switch to a different verification method.
35
- next_move: Close `N848.G1.A22`.
33
+ summary: The repo now has a committed review package for its audited `exp(1420)` candidate, a chosen bounded-verification lane, an exact verified base interval `1..10000`, and a four-anchor obstruction candidate. The live question is how to turn that structure into a proof.
34
+ current_blocker: The repo has not yet promoted the four-anchor obstruction from empirical recon into a proof-shaped lemma with clear obligations and a breakpoint consequence.
35
+ next_move: Close `N848.G1.A24`.
36
36
  - ticket_id: N848F
37
37
  title: Audit public formalization coverage
38
38
  route_id: formalization_coverage_audit
@@ -189,9 +189,26 @@ atoms:
189
189
  summary: The repo now has a second exact certificate extending the bounded-verification base interval from `1..2000` to `1..3000`, while also showing that a blind jump to `1..5000` is no longer cheap enough to count as the default next move.
190
190
  next_move: Choose the post-`3000` extension policy.
191
191
  - atom_id: N848.G1.A22
192
- title: Choose the post-`3000` verified interval policy
192
+ title: Rebuild the exact verifier and remove the false post-`3000` policy wall
193
+ route_id: finite_check_gap_closure
194
+ ticket_id: N848
195
+ status: done
196
+ summary: The exact verifier now uses an incremental update rule, the compact certificate format keeps packets small, and the exact bounded-verification base interval now reaches `1..10000`.
197
+ next_move: Choose the post-`10000` interval policy.
198
+ - atom_id: N848.G1.A23
199
+ title: Choose the post-`10000` verified interval policy
200
+ route_id: finite_check_gap_closure
201
+ ticket_id: N848
202
+ status: done
203
+ summary: >-
204
+ The repo has chosen the structure-first route: use the `1..10000` exact
205
+ packet as evidence for a four-anchor obstruction and a breakpoint law,
206
+ rather than defaulting immediately to a larger raw interval.
207
+ next_move: Formalize the four-anchor lemma candidate.
208
+ - atom_id: N848.G1.A24
209
+ title: Formalize the four-anchor obstruction lemma candidate
193
210
  route_id: finite_check_gap_closure
194
211
  ticket_id: N848
195
212
  status: ready
196
- summary: The bounded-verification lane now starts from a real certified base interval `1..3000`. The next choice is whether exact coverage should continue in smaller certified steps, or whether the next gain should come from breakpoint or audit methods.
197
- next_move: Decide whether to extend exact verified coverage beyond `3000` in smaller steps or switch method class.
213
+ summary: The repo now has a four-anchor obstruction candidate with no failures in `30..10000`, plus a minimality ledger showing that `{7, 32, 57, 82}` is unusually strong among small anchor sets. The next move is to package that into a proof-shaped lemma with explicit obligations.
214
+ next_move: Write the theorem candidate and proof-obligation surface for the four-anchor obstruction.
@@ -23,5 +23,7 @@
23
23
  - Twentieth refinement: freeze the operational threshold posture so imported `N0` claims can size the finite remainder without being silently promoted to canonical repo theorem truth.
24
24
  - Twenty-first refinement: certify the exact interval `1..2000` by a reproducible maximum-clique scan, giving the bounded-verification lane its first genuine covered interval.
25
25
  - Twenty-second refinement: extend the exact certificate to `1..3000`, and record that a direct jump to `1..5000` no longer looks cheap enough to treat as the obvious default next move.
26
- - Current public pack posture: active route `finite_check_gap_closure`, with asymptotic theorem already in hand, a committed audited candidate package in the repo, and a real exact base interval `1..3000` now covered in the bounded-verification lane.
27
- - Next maturity threshold: choose the post-`3000` extension policy in the most efficient trustworthy way.
26
+ - Twenty-third refinement: rebuild the exact verifier incrementally, compact the witness packet format, and extend the exact certificate all the way to `1..10000`.
27
+ - Twenty-fourth refinement: mine the exact packet for structure, isolate the four-anchor obstruction `{7, 32, 57, 82}` with no failures in `30..10000`, and record that this is the first route that looks theorem-shaped rather than purely computational.
28
+ - Current public pack posture: active route `finite_check_gap_closure`, with asymptotic theorem already in hand, a committed audited candidate package in the repo, a real exact base interval `1..10000`, and a four-anchor theorem candidate now visible in the bounded-verification lane.
29
+ - Next maturity threshold: formalize the four-anchor obstruction lemma and connect it to the breakpoint law suggested by the exact packet.
@@ -40,6 +40,9 @@ Any bounded-verification claim for Problem `848` should attach a certificate wit
40
40
  - a second script, proof note, or reviewer confirmation
41
41
  - `cost_profile`
42
42
  - rough runtime or proof complexity
43
+ - `witness_encoding`
44
+ - if the certificate omits raw witness lists because they are derivable, state that
45
+ encoding explicitly, for example "pure residue class mod 25 when possible"
43
46
 
44
47
  ## Rejection triggers
45
48
 
@@ -76,10 +76,10 @@ Desired certificate:
76
76
 
77
77
  ## Current honest posture
78
78
 
79
- - Regime A: first exact interval `1..3000` is now frozen in the repo with a reproducible
79
+ - Regime A: first exact interval `1..10000` is now frozen in the repo with a reproducible
80
80
  maximum-clique certificate
81
- - Regime A warning: a direct jump to `1..5000` did not finish cheaply enough to treat blind
82
- brute-force extension as the only serious next move
81
+ - Regime A note: an incremental verifier removed the earlier false cost wall caused by
82
+ rebuilding the graph and clique search from scratch for each `N`
83
83
  - Regime B: not frozen in this repo
84
84
  - Regime C: public attempts exist, but some were explicitly criticized on the forum as
85
85
  difficult to verify or likely incorrect
@@ -87,6 +87,6 @@ Desired certificate:
87
87
  yet repo-audited
88
88
 
89
89
  So the next honest move is to decide whether exact-small-`N` coverage should be extended
90
- directly beyond `3000` in smaller certified steps, or whether the next gain comes from
90
+ directly beyond `10000`, or whether the next gain comes from
91
91
  auditing imported computation or
92
92
  switching method class.
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+
6
+ function parseArgs(argv) {
7
+ const args = {
8
+ max: 10000,
9
+ threshold: 1,
10
+ anchors: [7, 32, 57, 82],
11
+ jsonOutput: null,
12
+ };
13
+ for (let i = 0; i < argv.length; i += 1) {
14
+ const token = argv[i];
15
+ if (token === '--max') {
16
+ args.max = Number(argv[++i]);
17
+ } else if (token === '--threshold') {
18
+ args.threshold = Number(argv[++i]);
19
+ } else if (token === '--anchors') {
20
+ args.anchors = argv[++i].split(',').map((value) => Number(value.trim())).filter(Boolean);
21
+ } else if (token === '--json-output') {
22
+ args.jsonOutput = argv[++i];
23
+ } else {
24
+ throw new Error(`Unknown argument: ${token}`);
25
+ }
26
+ }
27
+ if (!Number.isInteger(args.max) || args.max < 1) {
28
+ throw new Error('--max must be a positive integer');
29
+ }
30
+ if (!Number.isInteger(args.threshold) || args.threshold < 1 || args.threshold > args.max) {
31
+ throw new Error('--threshold must be an integer between 1 and --max');
32
+ }
33
+ if (!Array.isArray(args.anchors) || args.anchors.length === 0) {
34
+ throw new Error('--anchors must provide at least one positive integer');
35
+ }
36
+ return args;
37
+ }
38
+
39
+ const squarefreeCache = new Map();
40
+
41
+ function isSquarefree(n) {
42
+ if (squarefreeCache.has(n)) return squarefreeCache.get(n);
43
+ let x = n;
44
+ for (let p = 2; p * p <= x; p += 1) {
45
+ if (x % p !== 0) continue;
46
+ x /= p;
47
+ if (x % p === 0) {
48
+ squarefreeCache.set(n, false);
49
+ return false;
50
+ }
51
+ while (x % p === 0) x /= p;
52
+ }
53
+ squarefreeCache.set(n, true);
54
+ return true;
55
+ }
56
+
57
+ function scanAnchors({ anchors, threshold, max }) {
58
+ const failures = [];
59
+ const witnessCounts = new Map();
60
+ const residueStats = new Map();
61
+
62
+ for (let residue = 0; residue < 25; residue += 1) {
63
+ if (residue === 7) continue;
64
+ residueStats.set(residue, {
65
+ residue,
66
+ witnessCounts: Object.fromEntries(anchors.map((anchor) => [anchor, 0])),
67
+ firstFailure: null,
68
+ failureCount: 0,
69
+ });
70
+ }
71
+
72
+ for (let n = threshold; n <= max; n += 1) {
73
+ if (n % 25 === 7) continue;
74
+ const usableAnchors = anchors.filter((anchor) => anchor <= n);
75
+ const witness = usableAnchors.find((anchor) => isSquarefree(anchor * n + 1)) ?? null;
76
+ if (witness === null) {
77
+ failures.push(n);
78
+ const residueRow = residueStats.get(n % 25);
79
+ residueRow.failureCount += 1;
80
+ if (residueRow.firstFailure === null) residueRow.firstFailure = n;
81
+ continue;
82
+ }
83
+ witnessCounts.set(witness, (witnessCounts.get(witness) ?? 0) + 1);
84
+ residueStats.get(n % 25).witnessCounts[witness] += 1;
85
+ }
86
+
87
+ return {
88
+ failures,
89
+ witnessCounts: Object.fromEntries(anchors.map((anchor) => [anchor, witnessCounts.get(anchor) ?? 0])),
90
+ residueStats: [...residueStats.values()],
91
+ };
92
+ }
93
+
94
+ const args = parseArgs(process.argv.slice(2));
95
+ const result = scanAnchors(args);
96
+ const payload = {
97
+ generatedAt: new Date().toISOString(),
98
+ method: 'anchor_obstruction_scan',
99
+ problemId: '848',
100
+ parameters: {
101
+ anchors: args.anchors,
102
+ threshold: args.threshold,
103
+ max: args.max,
104
+ },
105
+ summary: {
106
+ failureCount: result.failures.length,
107
+ tailStart: result.failures.length ? Math.max(...result.failures) + 1 : args.threshold,
108
+ allCoveredFromThreshold: result.failures.length === 0,
109
+ witnessCounts: result.witnessCounts,
110
+ },
111
+ failures: result.failures,
112
+ residueStats: result.residueStats,
113
+ };
114
+
115
+ if (args.jsonOutput) {
116
+ fs.mkdirSync(path.dirname(args.jsonOutput), { recursive: true });
117
+ fs.writeFileSync(args.jsonOutput, `${JSON.stringify(payload, null, 2)}\n`);
118
+ }
119
+
120
+ console.log(JSON.stringify(payload.summary, null, 2));
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+
6
+ function parseArgs(argv) {
7
+ const args = {
8
+ max: 10000,
9
+ top: 15,
10
+ candidateCount: 10,
11
+ setSizes: [3, 4],
12
+ jsonOutput: null,
13
+ };
14
+ for (let i = 0; i < argv.length; i += 1) {
15
+ const token = argv[i];
16
+ if (token === '--max') {
17
+ args.max = Number(argv[++i]);
18
+ } else if (token === '--top') {
19
+ args.top = Number(argv[++i]);
20
+ } else if (token === '--candidate-count') {
21
+ args.candidateCount = Number(argv[++i]);
22
+ } else if (token === '--set-sizes') {
23
+ args.setSizes = argv[++i].split(',').map((value) => Number(value.trim())).filter(Boolean);
24
+ } else if (token === '--json-output') {
25
+ args.jsonOutput = argv[++i];
26
+ } else {
27
+ throw new Error(`Unknown argument: ${token}`);
28
+ }
29
+ }
30
+ if (!Number.isInteger(args.max) || args.max < 1) {
31
+ throw new Error('--max must be a positive integer');
32
+ }
33
+ if (!Number.isInteger(args.top) || args.top < 1) {
34
+ throw new Error('--top must be a positive integer');
35
+ }
36
+ if (!Number.isInteger(args.candidateCount) || args.candidateCount < 1) {
37
+ throw new Error('--candidate-count must be a positive integer');
38
+ }
39
+ if (!Array.isArray(args.setSizes) || args.setSizes.length === 0) {
40
+ throw new Error('--set-sizes must provide at least one set size');
41
+ }
42
+ return args;
43
+ }
44
+
45
+ const squarefreeCache = new Map();
46
+
47
+ function isSquarefree(n) {
48
+ if (squarefreeCache.has(n)) return squarefreeCache.get(n);
49
+ let x = n;
50
+ for (let p = 2; p * p <= x; p += 1) {
51
+ if (x % p !== 0) continue;
52
+ x /= p;
53
+ if (x % p === 0) {
54
+ squarefreeCache.set(n, false);
55
+ return false;
56
+ }
57
+ while (x % p === 0) x /= p;
58
+ }
59
+ squarefreeCache.set(n, true);
60
+ return true;
61
+ }
62
+
63
+ function choose(list, size, start = 0, prefix = [], out = []) {
64
+ if (prefix.length === size) {
65
+ out.push(prefix);
66
+ return out;
67
+ }
68
+ for (let i = start; i <= list.length - (size - prefix.length); i += 1) {
69
+ choose(list, size, i + 1, [...prefix, list[i]], out);
70
+ }
71
+ return out;
72
+ }
73
+
74
+ function failuresFor(set, max) {
75
+ const bad = [];
76
+ for (let n = 1; n <= max; n += 1) {
77
+ if (n % 25 === 7) continue;
78
+ let ok = false;
79
+ for (const q of set) {
80
+ if (q <= n && isSquarefree(n * q + 1)) {
81
+ ok = true;
82
+ break;
83
+ }
84
+ }
85
+ if (!ok) bad.push(n);
86
+ }
87
+ return bad;
88
+ }
89
+
90
+ const args = parseArgs(process.argv.slice(2));
91
+ const candidates = Array.from({ length: args.candidateCount }, (_, index) => 7 + 25 * index);
92
+ const bySize = {};
93
+
94
+ for (const size of args.setSizes) {
95
+ bySize[size] = choose(candidates, size)
96
+ .map((set) => {
97
+ const failures = failuresFor(set, args.max);
98
+ return {
99
+ anchors: set,
100
+ failureCount: failures.length,
101
+ tailStart: failures.length ? Math.max(...failures) + 1 : 1,
102
+ worstFailures: failures.slice(-12),
103
+ };
104
+ })
105
+ .sort((left, right) => left.tailStart - right.tailStart || left.failureCount - right.failureCount)
106
+ .slice(0, args.top);
107
+ }
108
+
109
+ const payload = {
110
+ generatedAt: new Date().toISOString(),
111
+ method: 'anchor_subset_search',
112
+ problemId: '848',
113
+ parameters: {
114
+ max: args.max,
115
+ top: args.top,
116
+ candidateCount: args.candidateCount,
117
+ setSizes: args.setSizes,
118
+ candidates,
119
+ },
120
+ results: bySize,
121
+ };
122
+
123
+ if (args.jsonOutput) {
124
+ fs.mkdirSync(path.dirname(args.jsonOutput), { recursive: true });
125
+ fs.writeFileSync(args.jsonOutput, `${JSON.stringify(payload, null, 2)}\n`);
126
+ }
127
+
128
+ console.log(JSON.stringify(
129
+ Object.fromEntries(
130
+ Object.entries(bySize).map(([size, rows]) => [size, rows[0] ?? null]),
131
+ ),
132
+ null,
133
+ 2,
134
+ ));