"""The term -> field-of-law assignment must be even, stable and honest.""" import field_assignment as fa FIELDS = [f"Field {i:02d}" for i in range(26)] def _dense(n_terms: int) -> dict[str, set[str]]: return {f"term_{i}": set(FIELDS) for i in range(n_terms)} def test_derive_floor_is_integer_division() -> None: assert fa.derive_floor(160, 26) == 6 assert fa.derive_floor(100, 26) == 3 assert fa.derive_floor(26, 26) == 1 assert fa.derive_floor(0, 26) == 0 assert fa.derive_floor(160, 0) == 0 def test_dense_pool_is_evenly_spread() -> None: assignment = fa.assign_terms_to_fields(_dense(160)) counts = assignment.assigned assert len(assignment.by_term) == 160 assert min(counts.values()) == 6 assert max(counts.values()) == 7 assert sum(counts.values()) == 160 def test_every_term_assigned_exactly_once() -> None: assign = fa.assign_terms_to_fields(_dense(160)) assert sorted(assign.by_term) == sorted(_dense(160)) def test_never_assigns_a_field_the_term_lacks() -> None: available = { "a": {"Field 00"}, "b": {"Field 01", "Field 02"}, "c": {"Field 02"}, } assign = fa.assign_terms_to_fields(available) for term, field in assign.by_term.items(): assert field in available[term] def test_rebuild_is_identical() -> None: available = _dense(160) assert fa.assign_terms_to_fields(available) == fa.assign_terms_to_fields(available) def test_adding_terms_moves_few_existing_assignments() -> None: """This is what protects verdicts already collected. Measured across 30 term vocabularies: 12-29 moved, mean 23. The bound of 45 clears that spread while still failing a positional implementation, which moves all 160. """ before = fa.assign_terms_to_fields(_dense(160)).by_term grown = _dense(160) for i in range(10): grown[f"extra_{i}"] = set(FIELDS) after = fa.assign_terms_to_fields(grown).by_term moved = sum(1 for term in before if before[term] != after[term]) assert moved <= 45, f"{moved} of 160 moved" def test_scarce_field_keeps_all_its_candidates() -> None: available = {f"term_{i}": set(FIELDS) for i in range(160)} scarce = "Field 07" for term in available: available[term].discard(scarce) for term in ("term_0", "term_1", "term_2"): available[term].add(scarce) assign = fa.assign_terms_to_fields(available) assert assign.assigned[scarce] == 3 assert {assign.by_term[t] for t in ("term_0", "term_1", "term_2")} == {scarce} def test_scarcity_ordering_does_not_starve_a_satisfiable_field() -> None: """Two thin fields share candidates. Naive field order lets the field visited first take terms the other one needed; scarcity order does not.""" fields = ["Field A", "Field B"] + [f"Field {i:02d}" for i in range(4)] available: dict[str, set[str]] = {} for i in range(24): available[f"common_{i}"] = {f"Field {j:02d}" for j in range(4)} for i in range(4): available[f"shared_{i}"] = {"Field A", "Field B"} for i in range(4): available[f"onlyA_{i}"] = {"Field A"} assign = fa.assign_terms_to_fields(available, terms_per_field=4) assert assign.assigned["Field B"] == 4, "Field B was starved by a greedy Field A" def test_achievable_floor_is_bounded_by_the_scarcest_field() -> None: available = {f"term_{i}": set(FIELDS) for i in range(160)} for term in available: available[term].discard("Field 07") available["term_0"].add("Field 07") assert fa.assign_terms_to_fields(available).achievable_floor() == 1 def test_achievable_floor_is_bounded_by_even_division() -> None: assert fa.assign_terms_to_fields(_dense(160)).achievable_floor() == 6 def test_explicit_floor_overrides_the_derivation() -> None: assign = fa.assign_terms_to_fields(_dense(160), terms_per_field=2) assert min(assign.assigned.values()) >= 2 assert len(assign.by_term) == 160 assert assign.floor == 2 assert assign.floor_source == "explicit" def test_empty_input_returns_empty_assignment() -> None: assign = fa.assign_terms_to_fields({}) assert assign.by_term == {} assert assign.fields == () assert assign.achievable_floor() == 0 def test_assignment_carries_the_floor_it_used() -> None: """Diagnostics read these off the decision; re-deriving them from the input pool and the finished template is what let the two drift apart.""" assign = fa.assign_terms_to_fields(_dense(160)) assert assign.floor == 6 assert assign.floor_source == "derived" assert assign.n_terms == 160 assert assign.fields == tuple(FIELDS) assert assign.candidates["Field 00"] == 160 assert sum(assign.assigned.values()) == 160