Toolbox property meanings no longer name an entry or operator

The incomplete-list note shows meanings before code, so "a ^ x ^ x = a"
or "x & -x" handed over the answer. Rewrite the six leaking meanings plus
two that said "sorted", and pin it with
test_no_property_meaning_names_an_entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi
This commit is contained in:
Krzysztof Rudnicki 2026-10-23 15:57:25 +02:00
parent 6343499ed1
commit 2d2d8a41f8
2 changed files with 22 additions and 8 deletions

View File

@ -47,6 +47,20 @@ def test_names_and_aliases_are_unique() -> None:
assert len(names) == len(set(names))
def test_no_property_meaning_names_an_entry() -> None:
# The incomplete-list note shows meanings to kuhy before code; one that
# says "a ^ x ^ x = a" hands over the answer it is asking him to find.
box = toolbox.load()
names = {n.casefold() for e in box["entries"] for n in [e["name"], *e["aliases"]]}
leaks = {
prop: hit
for prop, meaning in box["properties"].items()
for hit in names | {"^", "&", "sort"}
if hit in meaning.casefold()
}
assert leaks == {}
def test_no_name_is_a_cluster_problem_title() -> None:
# The leak bounce fires on any entry name or alias before the first code
# event, so one that is a problem's own title bounces its restatement.

View File

@ -1,17 +1,17 @@
{
"properties": {
"self-inverse": "applying the same value twice undoes it: a ^ x ^ x = a",
"self-inverse": "applying the same value twice undoes it: combine a with x, then with x again, and you are back at a",
"invertible": "every step has a known undo: a + x - x = a",
"commutative": "operand order does not matter: a + b = b + a",
"associative": "grouping does not matter, so a running fold works: (a + b) + c = a + (b + c)",
"identity element": "a neutral value e with a op e = a: 0 for + ^ gcd, -Infinity for max, +Infinity for min",
"identity element": "a neutral value e that leaves anything unchanged when combined with it (a op e = a)",
"idempotent": "combining a value with itself changes nothing: max(a, a) = a",
"selective": "the result is always one of the operands: max(a, b) is a or b",
"divides its inputs": "the result divides every operand, so folding more values can only shrink it",
"works per bit": "each bit position is computed independently of the others",
"carries between bits": "bit positions interact: a carry moves into the next position",
"cancels any k-fold repeat": "a value that occurs exactly k times leaves no trace, for any chosen k",
"isolates one bit": "keeps a single set bit and clears the rest: x & -x keeps the lowest one",
"isolates one bit": "keeps a single set bit of a number and clears all the others",
"O(1) extra space": "a fixed number of variables besides the input, however large the input",
"single pass": "reads the input once, front to back",
"O(1) lookup": "asks 'seen x?' or 'what is stored for x?' in (expected) constant time",
@ -23,23 +23,23 @@
"from both ends": "the two indexes start at opposite ends and move toward each other",
"window moves right": "both indexes move left to right; the range between them is the current window",
"monotone condition": "only correct when validity changes once as a range grows (no negatives in a sum)",
"needs sorted input": "only correct once the input is sorted",
"needs sorted input": "only correct once the input is in ascending (or descending) order",
"O(n log n) time": "costs about n log n comparisons, more than one linear pass",
"makes equal values adjacent": "afterwards equal (and close) values sit next to each other",
"O(log n) per query": "each question halves the remaining range, so it costs about log n steps",
"range query in O(1)": "after O(n) setup, any contiguous range's total is one subtraction",
"needs an inverse": "only works over an operation that can be undone (+ with -, ^ with ^); max has none",
"needs an inverse": "only works over an operation whose effect can be undone afterwards; one that loses information has none",
"keeps every prefix": "stores all n prefix values, so any (l, r) range can be asked later",
"handles negatives": "stays correct when values can be negative (a sliding window does not)",
"handles negatives": "stays correct when values can be negative, where a grow/shrink-by-running-total approach breaks",
"needs a majority": "only correct when one value occurs more than n/2 times",
"last-in-first-out": "the most recently added element is the first one taken out",
"next-greater lookup": "for every element, finds the nearest later element that is larger (flip for smaller)",
"amortized O(n)": "each element is added and removed at most once, so the total work is linear",
"overlaps become adjacent": "after sorting by start, every interval that overlaps the current group comes right after it",
"overlaps become adjacent": "once items are put in order of their start, everything overlapping the current group comes right after it",
"earliest end first": "choosing the interval that ends first leaves the most room for the rest",
"greedy": "commits to the locally best choice and never revisits it",
"O(log n) push and pop-min": "insert and remove-the-smallest each cost about log n; flip the comparison for largest",
"partial order": "only the top element is guaranteed to be in place; the rest is not sorted"
"partial order": "only the top element is guaranteed to be in place; the rest is in no particular order"
},
"entries": [
{