diff --git a/leetcode_coach/leetcode_coach/tests/test_toolbox.py b/leetcode_coach/leetcode_coach/tests/test_toolbox.py index db9a43c..8c0eefa 100644 --- a/leetcode_coach/leetcode_coach/tests/test_toolbox.py +++ b/leetcode_coach/leetcode_coach/tests/test_toolbox.py @@ -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. diff --git a/leetcode_coach/leetcode_coach/toolbox.json b/leetcode_coach/leetcode_coach/toolbox.json index 2f0dd6d..b07668a 100644 --- a/leetcode_coach/leetcode_coach/toolbox.json +++ b/leetcode_coach/leetcode_coach/toolbox.json @@ -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": [ {