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.
- package/package.json +1 -1
- package/packs/number-theory/problems/848/ANCHOR_MINIMALITY_LEDGER.md +55 -0
- package/packs/number-theory/problems/848/ANCHOR_OBSTRUCTION_7_32_57_82.json +293 -0
- package/packs/number-theory/problems/848/ANCHOR_OBSTRUCTION_RECON.md +82 -0
- package/packs/number-theory/problems/848/ANCHOR_SUBSET_SEARCH_PREFIX10_K3_K4.json +737 -0
- package/packs/number-theory/problems/848/BOUNDED_VERIFICATION_PLAN.md +3 -2
- package/packs/number-theory/problems/848/{EXACT_SMALL_N_1_3000_CERTIFICATE.md → EXACT_SMALL_N_1_10000_CERTIFICATE.md} +17 -14
- package/packs/number-theory/problems/848/EXACT_SMALL_N_1_10000_RESULTS.json +100018 -0
- package/packs/number-theory/problems/848/FOUR_ANCHOR_LEMMA_CANDIDATE.md +101 -0
- package/packs/number-theory/problems/848/FRONTIER_NOTE.md +12 -7
- package/packs/number-theory/problems/848/INTERVAL_WORK_QUEUE.yaml +5 -5
- package/packs/number-theory/problems/848/OPS_DETAILS.yaml +25 -8
- package/packs/number-theory/problems/848/ROUTE_HISTORY.md +4 -2
- package/packs/number-theory/problems/848/VERIFICATION_CERTIFICATE_SPEC.md +3 -0
- package/packs/number-theory/problems/848/VERIFICATION_REGIMES.md +4 -4
- package/packs/number-theory/problems/848/compute/problem848_anchor_obstruction_scan.mjs +120 -0
- package/packs/number-theory/problems/848/compute/problem848_anchor_subset_search.mjs +134 -0
- package/packs/number-theory/problems/848/compute/problem848_small_n_exact_scan.mjs +62 -45
- package/packs/number-theory/problems/848/context.yaml +13 -6
- package/problems/848/EVIDENCE.md +13 -5
- package/problems/848/ROUTES.md +4 -4
- package/problems/848/SHARE_READY_SUMMARY.md +2 -1
- 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
|
-
-
|
|
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
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
- the
|
|
42
|
-
|
|
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
|
-
- `
|
|
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..
|
|
10
|
+
range: "1..10000"
|
|
11
11
|
status: done
|
|
12
12
|
method_class: exact_small_n
|
|
13
13
|
claim_level: verified
|
|
14
|
-
certificate:
|
|
15
|
-
data_packet:
|
|
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: "
|
|
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
|
|
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:
|
|
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..
|
|
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,
|
|
34
|
-
current_blocker: The repo has not yet
|
|
35
|
-
next_move: Close `N848.G1.
|
|
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:
|
|
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
|
|
197
|
-
next_move:
|
|
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
|
-
-
|
|
27
|
-
-
|
|
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..
|
|
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
|
|
82
|
-
|
|
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 `
|
|
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
|
+
));
|