# -*- coding: utf-8 -*- """The reinforcing-suggestion matcher: set cover over opaque feature records. Every panel here is invented. Marks are `PNL...`, which is the project's placeholder convention -- no real panel mark, job or client name appears in this file, including in the case rebuilt from the worked example, where the three real marks are replaced by PNL501 (target) and PNL502 / PNL503 (donors). Two kinds of test live here. Most use bare rectangles and the module's own reference `rect_distance`, because the point of the seam is that the selection logic can be exercised with no feature schema at all. A handful inject a hand-written `covers` that returns canned numbers, because the ranking rules are about the NUMBERS and it is clearer to state them directly than to derive them from geometry. """ # pylint: disable=import-error,invalid-name,broad-except import pytest from vibe import reinforcematch as rm # ---------------------------------------------------------------- fixtures def canned(table): """A `covers` built from {(donor_feature, target_feature): distance}. Anything not in the table is not covered. Feature records are plain strings; the matcher never looks at them, which is the whole point. """ def covers(donor_feature, target_feature): return table.get((donor_feature, target_feature)) return covers # Rebuilt from the worked example, with invented marks. Rectangles are # (x0, x1, y0, y1) in absolute feet from the start of the piece -- absolute, # never a fraction of length, which is the thing the measurement settled. # The three pieces are three different lengths and the big openings still land # at the identical feet-from-start rectangle. PNL501 = [ # target, 27.625 ft long, six features (24.250, 27.625, 6.250, 11.500), # 0 right-end blockout (6.500, 10.583, 0.354, 4.479), # 1 big opening (14.344, 18.427, 0.354, 4.479), # 2 big opening (0.000, 0.875, 1.125, 3.000), # 3 left notch (24.042, 25.875, 1.125, 3.000), # 4 small right notch (0.000, 0.667, 11.292, 11.500), # 5 left top-corner recess ] PNL502 = [ # donor, 24.917 ft: the two big openings (6.500, 10.583, 0.354, 4.479), (14.344, 18.427, 0.354, 4.479), (0.000, 0.875, 1.125, 3.000), (24.042, 24.917, 1.125, 3.000), (0.000, 0.667, 11.292, 11.500), (24.250, 24.917, 11.292, 11.500), ] PNL503 = [ # donor, 27.208 ft: the blockout end (24.250, 27.208, 6.917, 11.500), (0.000, 0.875, 1.125, 3.000), (24.042, 25.875, 1.125, 3.000), (0.000, 0.667, 11.292, 11.500), ] # ------------------------------------------------- the reference comparison def test_rect_distance_is_zero_for_an_identical_rectangle(): r = (6.5, 10.583, 0.354, 4.479) assert rm.rect_distance(r, r) == 0.0 def test_rect_distance_grows_with_the_corner_offsets(): target = (24.250, 27.625, 6.250, 11.500) donor = (24.250, 27.208, 6.917, 11.500) got = rm.rect_distance(donor, target) assert got == pytest.approx(0.417 + 0.667, abs=0.001) def test_rect_distance_refuses_a_feature_in_a_different_band(): """A single-axis check calls these the same feature. They are not. Both sit near the right end of the piece and share x-range, but one is a full-height blockout and the other a shallow top-corner recess. """ blockout = (24.250, 27.625, 6.250, 11.500) recess = (24.250, 24.917, 11.292, 11.500) assert rm.rect_distance(recess, blockout) is None def test_rect_distance_is_not_proportional(): """Same absolute rectangle on two panels of different length is exact. Nothing here divides by a length, so there is nothing for the panels' different lengths to change. """ assert rm.rect_distance(PNL502[0], PNL501[1]) == 0.0 assert rm.rect_distance(PNL502[1], PNL501[2]) == 0.0 def test_rect_distance_returns_none_for_unreadable_input(): assert rm.rect_distance(None, (0, 1, 0, 1)) is None assert rm.rect_distance(('a', 'b', 'c', 'd'), (0, 1, 0, 1)) is None # --------------------------------------------------- could not judge vs none def test_no_target_features_is_could_not_judge(): """None, not an empty result. There is nothing to cover, so no judgement was made -- and that must not read like 'searched, found nothing'.""" assert rm.suggest([], [('PNL502', PNL502)], rm.rect_distance) is None assert rm.suggest(None, [('PNL502', PNL502)], rm.rect_distance) is None def test_no_donors_reads_differently_from_no_coverage(): nothing_offered = rm.suggest(PNL501, [], rm.rect_distance) assert nothing_offered.status == rm.NO_DONORS assert nothing_offered.proposals == () assert nothing_offered.donor_count == 0 assert nothing_offered.donors_needed is None assert 'nothing was judged' in nothing_offered.summary() useless = [('PNL900', [(90.0, 91.0, 0.0, 1.0)])] judged = rm.suggest(PNL501, useless, rm.rect_distance) assert judged.status == rm.NO_COVERAGE assert judged.proposals == () assert judged.donor_count == 1 assert judged.uncovered == (0, 1, 2, 3, 4, 5) assert 'not one of them covers' in judged.summary() assert nothing_offered.summary() != judged.summary() def test_a_covers_that_blows_up_is_not_swallowed(): """An exception in the comparison must not become 'covers nothing'.""" def boom(donor_feature, target_feature): raise ZeroDivisionError('bad geometry') with pytest.raises(ZeroDivisionError): rm.suggest(PNL501, [('PNL502', PNL502)], boom) def test_contract_violations_are_raised_not_absorbed(): with pytest.raises(ValueError): rm.suggest(['t'], [('D', ['d'])], canned({('d', 't'): -1.0})) with pytest.raises(ValueError): rm.suggest(['t'], [('D', ['d'])], canned({('d', 't'): 'close'})) with pytest.raises(ValueError): rm.suggest(['t'], [('D', ['d'])], covers=None) # ---------------------------------------------------------- single-donor cover def test_one_donor_covering_everything_exactly(): target = ['a', 'b', 'c'] donors = [('PNL601', ['a', 'b', 'c'])] table = dict(((f, f), 0.0) for f in target) got = rm.suggest(target, donors, canned(table)) assert got.status == rm.COMPLETE assert got.donors_needed == 1 assert got.best.donors == ('PNL601',) assert got.best.is_exact assert got.best.exact_count == 3 assert got.best.total_distance == 0.0 assert got.uncovered == () def test_a_single_exact_donor_outranks_a_single_loose_one(): target = ['a', 'b'] donors = [('PNL_LOOSE', ['x', 'y']), ('PNL_EXACT', ['a', 'b'])] table = {('x', 'a'): 1.0, ('y', 'b'): 1.0, ('a', 'a'): 0.0, ('b', 'b'): 0.0} got = rm.suggest(target, donors, canned(table)) assert got.donors_needed == 1 assert got.cover_count == 2 assert [p.donors[0] for p in got.proposals] == ['PNL_EXACT', 'PNL_LOOSE'] # ------------------------------------------------------------ two-donor cover def test_two_donors_are_needed_when_neither_alone_will_do(): target = ['a', 'b', 'c', 'd'] donors = [('PNL701', ['a', 'b']), ('PNL702', ['c', 'd'])] table = {('a', 'a'): 0.0, ('b', 'b'): 0.0, ('c', 'c'): 0.0, ('d', 'd'): 0.0} got = rm.suggest(target, donors, canned(table)) assert got.status == rm.COMPLETE assert got.donors_needed == 2 assert got.best.donors == ('PNL701', 'PNL702') def test_fewest_donors_wins_even_when_more_donors_would_be_more_exact(): """Fewest donors is the objective; exactness only breaks ties. PNL802+PNL803 would be exact on both features. PNL801 alone is loose on both. One donor still wins -- that is the owner's stated objective and the matcher does not get to relitigate it. """ target = ['a', 'b'] donors = [('PNL801', ['w']), ('PNL802', ['a']), ('PNL803', ['b'])] table = {('w', 'a'): 3.0, ('w', 'b'): 3.0, ('a', 'a'): 0.0, ('b', 'b'): 0.0} got = rm.suggest(target, donors, canned(table)) assert got.donors_needed == 1 assert got.best.donors == ('PNL801',) def test_overlap_between_donors_is_not_penalised(): """Both donors carry feature 'a'. That is expected, not a conflict.""" target = ['a', 'b', 'c'] donors = [('PNL811', ['a', 'b']), ('PNL812', ['a', 'c'])] table = {('a', 'a'): 0.0, ('b', 'b'): 0.0, ('c', 'c'): 0.0} got = rm.suggest(target, donors, canned(table)) assert got.status == rm.COMPLETE assert got.donors_needed == 2 assert got.best.exact_count == 3 # ---------------------------------------------------------- partial coverage def test_a_feature_nothing_covers_still_returns_the_cover_of_the_rest(): """'Nothing found' and 'here is everything but feature 3' are different answers, and only the second tells the engineer what to detail by hand.""" target = ['a', 'b', 'c', 'orphan'] donors = [('PNL901', ['a', 'b']), ('PNL902', ['c'])] table = {('a', 'a'): 0.0, ('b', 'b'): 0.0, ('c', 'c'): 0.0} got = rm.suggest(target, donors, canned(table)) assert got.status == rm.PARTIAL assert got.uncovered == (3,) assert got.best.donors == ('PNL901', 'PNL902') assert got.best.covered_count == 3 assert got.best.uncovered == (3,) assert got.best.assignment[3] is None assert 'no donor covers at all' in got.summary() def test_partial_and_complete_summaries_do_not_read_alike(): target = ['a', 'orphan'] complete = rm.suggest(['a'], [('PNL911', ['a'])], canned({('a', 'a'): 0.0})) partial = rm.suggest(target, [('PNL911', ['a'])], canned({('a', 'a'): 0.0})) assert complete.status == rm.COMPLETE assert partial.status == rm.PARTIAL assert complete.summary() != partial.summary() # ------------------------------------------------------------------ ranking def test_exactness_breaks_a_tie_between_equal_sized_covers(): target = ['a', 'b'] donors = [('PNL_NEAR', ['n1', 'n2']), ('PNL_SAME', ['a', 'b'])] table = {('n1', 'a'): 0.25, ('n2', 'b'): 0.25, ('a', 'a'): 0.0, ('b', 'b'): 0.0} got = rm.suggest(target, donors, canned(table)) assert [p.donors[0] for p in got.proposals] == ['PNL_SAME', 'PNL_NEAR'] def test_exactness_beats_evenness_and_a_sum_would_have_tied_them(): """The documented consequence of ranking on the sorted distance vector. Both covers total 2.0, so a sum calls them equal. The owner's ruling is that the exact shapes go in front, so the cover with four exact features wins even though its fifth is far off. """ target = ['a', 'b', 'c', 'd', 'e'] donors = [ ('PNL_EVEN', ['v0', 'v1', 'v2', 'v3', 'v4']), ('PNL_SHARP', ['w0', 'w1', 'w2', 'w3', 'w4']), ] table = {} for i, f in enumerate(target): table[('v{0}'.format(i), f)] = 0.4 table[('w{0}'.format(i), f)] = 0.0 if i < 4 else 2.0 got = rm.suggest(target, donors, canned(table)) assert got.proposals[0].total_distance == pytest.approx(2.0) assert got.proposals[1].total_distance == pytest.approx(2.0) assert got.proposals[0].donors == ('PNL_SHARP',) assert got.proposals[0].exact_count == 4 assert got.proposals[1].exact_count == 0 def test_the_best_donor_feature_within_a_donor_wins(): """A donor carrying two features that both cover one target feature contributes its closer one, not whichever happens to be looked at first.""" target = ['a'] donors = [('PNL_TWO', ['far', 'near'])] got = rm.suggest(target, donors, canned({('far', 'a'): 5.0, ('near', 'a'): 0.5})) assert got.best.assignment[0][2] == 0.5 assert got.best.donor_for(0) == ('PNL_TWO', 1, 0.5) def test_max_proposals_trims_but_the_count_still_reports_the_truth(): target = ['a'] donors = [('PNL{0}'.format(i), ['a']) for i in range(8)] got = rm.suggest(target, donors, canned({('a', 'a'): 0.0}), max_proposals=3) assert len(got.proposals) == 3 assert got.cover_count == 8 # ------------------------------------------------------------- determinism def test_dead_even_ties_break_by_input_order(): """Three indistinguishable donors. Input order decides, so the caller controls it -- e.g. by listing same-elevation donors first.""" target = ['a'] table = {('a', 'a'): 0.0} first = rm.suggest(target, [('PNL_A', ['a']), ('PNL_B', ['a']), ('PNL_C', ['a'])], canned(table)) assert [p.donors[0] for p in first.proposals] == ['PNL_A', 'PNL_B', 'PNL_C'] reordered = rm.suggest(target, [('PNL_C', ['a']), ('PNL_A', ['a']), ('PNL_B', ['a'])], canned(table)) assert [p.donors[0] for p in reordered.proposals] == ['PNL_C', 'PNL_A', 'PNL_B'] def test_the_same_inputs_give_the_same_answer_every_run(): donors = [('PNL502', PNL502), ('PNL503', PNL503)] shape = None for _ in range(25): got = rm.suggest(PNL501, donors, rm.rect_distance) this = (got.status, got.donors_needed, got.cover_count, tuple([(p.donors, p.distances, p.assignment) for p in got.proposals])) if shape is None: shape = this assert this == shape def test_determinism_survives_a_donor_list_full_of_equivalents(): target = ['a', 'b', 'c'] table = {('a', 'a'): 0.0, ('b', 'b'): 0.0, ('c', 'c'): 0.5} donors = [('PNL{0}'.format(i), ['a', 'b', 'c']) for i in range(12)] runs = set() for _ in range(10): got = rm.suggest(target, donors, canned(table)) runs.add(tuple([p.donors for p in got.proposals])) assert len(runs) == 1 # --------------------------------------- the worked example, invented names def test_the_worked_example_splits_into_exactly_two_donors(): """The shape of the owner's own example, rebuilt with synthetic marks. PNL502 is the only donor with the two big central openings; PNL503 is the only one with the right-end blockout. Neither alone will do, and the answer is the pair -- which is the claim the whole feature rests on. """ donors = [('PNL502', PNL502), ('PNL503', PNL503)] got = rm.suggest(PNL501, donors, rm.rect_distance) assert got.status == rm.COMPLETE assert got.uncovered == () assert got.donors_needed == 2 assert got.best.donors == ('PNL502', 'PNL503') def test_the_worked_example_is_exact_everywhere_but_the_blockout(): """Five of six features match exactly; the blockout is close, not equal. That asymmetry is the measurement, and it is what the ranking is built to express -- the exact features go in front, the approximate one carries a number rather than a verdict. """ donors = [('PNL502', PNL502), ('PNL503', PNL503)] best = rm.suggest(PNL501, donors, rm.rect_distance).best assert best.exact_count == 5 assert best.covered_count == 6 assert not best.is_exact # feature 0, the blockout, is the only inexact one, and it comes from the # blockout donor. assert best.donor_for(0)[0] == 'PNL503' assert best.donor_for(0)[2] == pytest.approx(1.084, abs=0.001) for i in (1, 2, 3, 4, 5): assert best.donor_for(i)[2] == 0.0 def test_the_worked_example_big_openings_come_from_the_opening_donor(): donors = [('PNL502', PNL502), ('PNL503', PNL503)] best = rm.suggest(PNL501, donors, rm.rect_distance).best assert best.donor_for(1)[0] == 'PNL502' assert best.donor_for(2)[0] == 'PNL502' def test_a_donor_offering_only_the_common_end_hardware_does_not_get_chosen(): """The end notches sit identically on every piece, so they discriminate between nothing -- a donor carrying only them adds nothing to the cover.""" common_only = [PNL501[3], PNL501[4], PNL501[5]] donors = [('PNL502', PNL502), ('PNL503', PNL503), ('PNL504', common_only)] got = rm.suggest(PNL501, donors, rm.rect_distance) assert got.donors_needed == 2 for proposal in got.proposals: assert 'PNL504' not in proposal.donors def test_a_third_donor_that_makes_a_smaller_cover_possible_takes_over(): """One donor that carries the whole target beats the two-donor split.""" whole = list(PNL501) donors = [('PNL502', PNL502), ('PNL503', PNL503), ('PNL505', whole)] got = rm.suggest(PNL501, donors, rm.rect_distance) assert got.donors_needed == 1 assert got.best.donors == ('PNL505',) assert got.best.is_exact # ------------------------------------------------------ greedy's known shape def test_restarting_from_every_donor_beats_the_first_bite_trap(): """The failure documented above `_greedy_covers`, run. Nine features; PNL_DIAG is tied for the biggest single bite and is listed first, so a single greedy pass takes it and then needs three more. The restarts find the three-donor answer. """ target = [str(i) for i in range(9)] diag = ['d2', 'd5', 'd8'] a, b, c = ['a0', 'a1', 'a2'], ['b3', 'b4', 'b5'], ['c6', 'c7', 'c8'] table = {('d2', '2'): 0.0, ('d5', '5'): 0.0, ('d8', '8'): 0.0} for group, base in ((a, 0), (b, 3), (c, 6)): for offset, name in enumerate(group): table[(name, str(base + offset))] = 0.0 donors = [('PNL_DIAG', diag), ('PNL_A', a), ('PNL_B', b), ('PNL_C', c)] got = rm.suggest(target, donors, canned(table)) assert got.status == rm.COMPLETE assert got.donors_needed == 3 assert got.best.donors == ('PNL_A', 'PNL_B', 'PNL_C') def test_a_donor_that_covers_nothing_is_simply_ignored(): target = ['a', 'b'] donors = [('PNL_EMPTY', []), ('PNL_NONE', ['z']), ('PNL_GOOD', ['a', 'b'])] got = rm.suggest(target, donors, canned({('a', 'a'): 0.0, ('b', 'b'): 0.0})) assert got.donor_count == 3 assert got.donors_needed == 1 assert got.best.donors == ('PNL_GOOD',) def test_a_donor_with_no_features_does_not_crash_the_run(): got = rm.suggest(['a'], [('PNL_EMPTY', None)], canned({})) assert got.status == rm.NO_COVERAGE # --------------------------------------------------- the matcher stays blind def test_the_matcher_never_touches_a_feature_record(): """Feature records that raise on ANY attribute access still work. This is the seam, asserted. If the matcher ever grows a peek at a field, this test is what fails. """ class Opaque(object): def __init__(self, tag): object.__setattr__(self, '_tag', tag) def __getattr__(self, name): raise AssertionError( 'the matcher read field {0!r} off a feature record'.format( name)) def covers(donor_feature, target_feature): d = object.__getattribute__(donor_feature, '_tag') t = object.__getattribute__(target_feature, '_tag') return 0.0 if d == t else None target = [Opaque('a'), Opaque('b')] donors = [('PNL_X', [Opaque('a')]), ('PNL_Y', [Opaque('b')])] got = rm.suggest(target, donors, covers) assert got.status == rm.COMPLETE assert got.best.donors == ('PNL_X', 'PNL_Y') def test_donor_keys_may_be_anything_hashable(): target = ['a'] donors = [((123456, 'assembly'), ['a'])] got = rm.suggest(target, donors, canned({('a', 'a'): 0.0})) assert got.best.donors == ((123456, 'assembly'),) # ------------------------------------------------------- MASTER as tie-break # # Owner's ruling, 2026-08-30: a donor typed MASTER in the model has been # REVIEWED, and reviewed donors are preferred. Asked how that interacts with # exactness -- a loosely-fitting MASTER against a perfectly exact non-MASTER # -- he ruled 拟合更好的排在前面: fit wins. MASTER breaks ties and nothing more. # # So there are exactly two things to prove: that it DOES decide a dead-even # tie, and that it NEVER moves anything the distance vector has separated. def test_master_breaks_a_tie_the_distances_cannot(): """Two covers, identical distance vectors. The reviewed one goes first. Without the signal these two are dead level and only input order separates them -- and PNL801 is listed first, so a matcher that ignored MASTER would put PNL801 on top. That is what makes this test load-bearing rather than decorative. """ covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5}) donors = [('PNL801', ['a']), ('PNL802', ['b'])] plain = rm.suggest(['t'], donors, covers) assert plain.proposals[0].donors == ('PNL801',) ranked = rm.suggest(['t'], donors, covers, masters=['PNL802']) assert ranked.proposals[0].donors == ('PNL802',) assert ranked.proposals[0].master_count == 1 assert ranked.proposals[1].master_count == 0 def test_master_never_beats_a_better_fit(): """The ruling, as an executable statement. PNL811 is a MASTER and fits loosely (0.5 off). PNL812 is not reviewed at all and is exact. Fit wins, every time, and the reviewed donor comes second -- it is not promoted past a cover the geometry has already put in front of it. """ covers = canned({('a', 't'): 0.5, ('b', 't'): 0.0}) donors = [('PNL811', ['a']), ('PNL812', ['b'])] ranked = rm.suggest(['t'], donors, covers, masters=['PNL811']) assert ranked.proposals[0].donors == ('PNL812',) assert ranked.proposals[0].master_count == 0 assert ranked.proposals[1].donors == ('PNL811',) def test_master_never_beats_a_better_fit_even_a_hair_better(): """The margin does not matter. 0.001 ft of fit still outranks a review.""" covers = canned({('a', 't'): 0.001, ('b', 't'): 0.0}) donors = [('PNL821', ['a']), ('PNL822', ['b'])] ranked = rm.suggest(['t'], donors, covers, masters=['PNL821']) assert ranked.proposals[0].donors == ('PNL822',) def test_master_never_beats_fewer_donors(): """Donor count is the objective and MASTER sits below that too. Only covers of the smallest size are built at all, so a two-donor set of masters is never even offered against a one-donor non-master cover. """ covers = canned({('a', 't0'): 0.0, ('a', 't1'): 0.0, ('b', 't0'): 0.0, ('c', 't1'): 0.0}) donors = [('PNL831', ['a']), ('PNL832', ['b']), ('PNL833', ['c'])] ranked = rm.suggest(['t0', 't1'], donors, covers, masters=['PNL832', 'PNL833']) assert ranked.donors_needed == 1 assert ranked.proposals[0].donors == ('PNL831',) def test_more_masters_ranks_above_fewer_when_everything_else_is_level(): """A cover carrying a reviewed donor outranks one carrying none. Four donors, two features, every distance 0.5, so the distance vectors of all three covers found are identical and only the tie-breaks separate them. PNL844 is the only master IN A COVER, and its cover is not the one input order would have chosen -- (PNL841, PNL842) is. So this fails if the master count is dropped from the key or put below input order. (Only three covers exist rather than all four pairings because the search is greedy-with-restarts and seeds each walk from one donor; that is the matcher's documented approximation and is not what is under test here.) """ covers = canned({('a', 't0'): 0.5, ('b', 't1'): 0.5, ('c', 't0'): 0.5, ('d', 't1'): 0.5}) donors = [('PNL841', ['a']), ('PNL842', ['b']), ('PNL843', ['c']), ('PNL844', ['d'])] plain = rm.suggest(['t0', 't1'], donors, covers) assert plain.proposals[0].donors == ('PNL841', 'PNL842') ranked = rm.suggest(['t0', 't1'], donors, covers, masters=['PNL843', 'PNL844']) assert ranked.proposals[0].donors == ('PNL841', 'PNL844') assert ranked.proposals[0].master_count == 1 assert ranked.proposals[-1].master_count == 0 def test_master_is_never_a_filter(): """A pool with one MASTER still offers every other cover it found.""" covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5, ('c', 't'): 0.5}) donors = [('PNL851', ['a']), ('PNL852', ['b']), ('PNL853', ['c'])] ranked = rm.suggest(['t'], donors, covers, masters=['PNL852']) assert len(ranked.proposals) == 3 assert set(p.donors[0] for p in ranked.proposals) == { 'PNL851', 'PNL852', 'PNL853'} def test_a_master_key_that_is_not_in_the_pool_is_ignored(): covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5}) donors = [('PNL861', ['a']), ('PNL862', ['b'])] ranked = rm.suggest(['t'], donors, covers, masters=['PNL999']) assert ranked.proposals[0].donors == ('PNL861',) assert all(p.master_count == 0 for p in ranked.proposals) def test_no_masters_ranks_exactly_as_before(): """The signal off is the old matcher, byte for byte in its ordering.""" covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5}) donors = [('PNL871', ['a']), ('PNL872', ['b'])] a = rm.suggest(['t'], donors, covers) b = rm.suggest(['t'], donors, covers, masters=[]) c = rm.suggest(['t'], donors, covers, masters=None) assert ([p.donors for p in a.proposals] == [p.donors for p in b.proposals] == [p.donors for p in c.proposals]) def test_a_proposal_reports_which_of_its_donors_are_reviewed(): covers = canned({('a', 't0'): 0.0, ('b', 't1'): 0.0}) donors = [('PNL881', ['a']), ('PNL882', ['b'])] ranked = rm.suggest(['t0', 't1'], donors, covers, masters=['PNL882']) best = ranked.proposals[0] assert best.masters == ('PNL882',) assert best.is_master('PNL882') and not best.is_master('PNL881') # ---------------------------------------------------- SAME ELEVATION as tie-break # # Owner, 2026-09-02, on WP425: five donors tied on every criterion above -- # 1 donor, 5/5 covered, 4 exact, 0 reviewed, 0.04 in total distance, all five # identical -- and the tie fell through to mark order, so WP133 printed # first though WP234 shares WP425's own building elevation: # "WP234明明在同一个elevation,为什么排名没有提前?" Approved as briefed # ("同意,就这么做"): elevation is an AFFINITY, ranked LAST -- below MASTER, # above input order -- never a filter. The proof obligations mirror the # MASTER section exactly: it DOES decide a dead-even tie, and it NEVER moves # anything the distance vector or MASTER have already separated. def test_same_elevation_breaks_a_tie_the_distances_and_master_cannot(): """Two covers, identical distance vectors and no MASTER either. The donor sharing the target's elevation goes first. Without the signal these two are dead level and only input order separates them -- and PNL901 is listed first, so a matcher that ignored elevation would put PNL901 on top. That is what makes this test load-bearing rather than decorative. """ covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5}) donors = [('PNL901', ['a']), ('PNL902', ['b'])] plain = rm.suggest(['t'], donors, covers) assert plain.proposals[0].donors == ('PNL901',) ranked = rm.suggest(['t'], donors, covers, same_elevation=['PNL902']) assert ranked.proposals[0].donors == ('PNL902',) assert ranked.proposals[0].elevation_count == 1 assert ranked.proposals[1].elevation_count == 0 def test_same_elevation_never_beats_a_better_fit(): """The ruling, as an executable statement. PNL911 shares the target's elevation but fits loosely (0.5 off). PNL912 is on a different elevation and is exact. Fit wins, every time, and the same-elevation donor comes second -- it is not promoted past a cover the geometry has already put in front of it. """ covers = canned({('a', 't'): 0.5, ('b', 't'): 0.0}) donors = [('PNL911', ['a']), ('PNL912', ['b'])] ranked = rm.suggest(['t'], donors, covers, same_elevation=['PNL911']) assert ranked.proposals[0].donors == ('PNL912',) assert ranked.proposals[0].elevation_count == 0 assert ranked.proposals[1].donors == ('PNL911',) def test_same_elevation_never_beats_fewer_donors(): """Donor count is the objective and elevation sits below that too.""" covers = canned({('a', 't0'): 0.0, ('a', 't1'): 0.0, ('b', 't0'): 0.0, ('c', 't1'): 0.0}) donors = [('PNL921', ['a']), ('PNL922', ['b']), ('PNL923', ['c'])] ranked = rm.suggest(['t0', 't1'], donors, covers, same_elevation=['PNL922', 'PNL923']) assert ranked.donors_needed == 1 assert ranked.proposals[0].donors == ('PNL921',) def test_same_elevation_sits_below_master(): """MASTER decides first; elevation only breaks what MASTER leaves tied. PNL931 is reviewed but on a different elevation; PNL932 shares the target's elevation but is not reviewed. Both fit identically (0.5 off). MASTER wins -- exactly the priority `_proposal_sort_key` states. """ covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5}) donors = [('PNL931', ['a']), ('PNL932', ['b'])] ranked = rm.suggest(['t'], donors, covers, masters=['PNL931'], same_elevation=['PNL932']) assert ranked.proposals[0].donors == ('PNL931',) assert ranked.proposals[0].master_count == 1 assert ranked.proposals[0].elevation_count == 0 def test_same_elevation_is_never_a_filter(): """A pool with one same-elevation donor still offers every cover found.""" covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5, ('c', 't'): 0.5}) donors = [('PNL941', ['a']), ('PNL942', ['b']), ('PNL943', ['c'])] ranked = rm.suggest(['t'], donors, covers, same_elevation=['PNL942']) assert len(ranked.proposals) == 3 assert set(p.donors[0] for p in ranked.proposals) == { 'PNL941', 'PNL942', 'PNL943'} def test_a_same_elevation_key_that_is_not_in_the_pool_is_ignored(): covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5}) donors = [('PNL951', ['a']), ('PNL952', ['b'])] ranked = rm.suggest(['t'], donors, covers, same_elevation=['PNL999']) assert ranked.proposals[0].donors == ('PNL951',) assert all(p.elevation_count == 0 for p in ranked.proposals) def test_no_same_elevation_ranks_exactly_as_before(): """The signal off is the old matcher, byte for byte in its ordering.""" covers = canned({('a', 't'): 0.5, ('b', 't'): 0.5}) donors = [('PNL961', ['a']), ('PNL962', ['b'])] a = rm.suggest(['t'], donors, covers) b = rm.suggest(['t'], donors, covers, same_elevation=[]) c = rm.suggest(['t'], donors, covers, same_elevation=None) assert ([p.donors for p in a.proposals] == [p.donors for p in b.proposals] == [p.donors for p in c.proposals]) def test_a_proposal_reports_which_of_its_donors_share_elevation(): covers = canned({('a', 't0'): 0.0, ('b', 't1'): 0.0}) donors = [('PNL971', ['a']), ('PNL972', ['b'])] ranked = rm.suggest(['t0', 't1'], donors, covers, same_elevation=['PNL972']) best = ranked.proposals[0] assert best.same_elevation == ('PNL972',) # ------------------------------------------------------------- the donor cap # # Owner's ruling, 2026-08-30: "一个方案,只要找到足够覆盖全部特征的母版,就够了。 # 我期待是 1-4 个,最多不超过 4 个。" Four donors in one proposal, hard. # # The OVERFLOW rule -- what happens when four cannot cover everything -- was # NOT ruled and is inferred: return the best four and name what they miss, # never relax to a fifth. These tests are what pins that reading, so if the # owner overrules it they are the place to start. def _one_each(n): """n target features, n donors, each donor carrying exactly one of them. The worst case for a cap: the only way to cover everything is to take every donor, so any cap below n has to bite. """ target = [str(i) for i in range(n)] donors = [('PNL{0}'.format(600 + i), [str(i)]) for i in range(n)] table = dict(((str(i), str(i)), 0.0) for i in range(n)) return target, donors, table def test_a_proposal_never_names_more_than_four_donors(): target, donors, table = _one_each(8) got = rm.suggest(target, donors, canned(table)) assert got.donor_cap == 4 for proposal in got.proposals: assert len(proposal.donors) <= 4 def test_the_cap_does_not_relax_to_a_fifth_donor(): """The inferred boundary, stated as a test. Eight features, eight donors with one feature each: five donors would cover more and the search may not take them.""" target, donors, table = _one_each(8) got = rm.suggest(target, donors, canned(table)) assert got.donors_needed == 4 assert len(got.best.donors) == 4 assert got.best.covered_count == 4 def test_the_cap_names_what_it_could_not_reach(): """A partial answer that says what it missed is useful; one that quietly covers half the panel is not.""" target, donors, table = _one_each(8) got = rm.suggest(target, donors, canned(table)) assert got.status == rm.CAPPED assert len(got.capped_out) == 4 assert set(got.capped_out) == set(got.best.uncovered) assert 'not within' in got.summary() or 'not by any' in got.summary() def test_capped_out_is_never_confused_with_nobody_has_it(): """Two different findings with opposite remedies. A feature the cap left out HAS a donor -- go and open it. A feature nobody has must be detailed from scratch. They are reported in separate fields and never merged.""" target, donors, table = _one_each(8) got = rm.suggest(target, donors, canned(table)) assert got.uncovered == () # somebody has every one of them assert got.capped_out # the cap is why they are missing assert got.status == rm.CAPPED assert got.status != rm.PARTIAL def test_the_cap_and_a_feature_nobody_has_are_reported_apart(): """Both can be true at once. The status reports the harder fact, and the two sets stay disjoint so the report can print two tables.""" target, donors, table = _one_each(8) target = target + ['orphan'] got = rm.suggest(target, donors, canned(table)) assert got.status == rm.PARTIAL # nobody has 'orphan' assert got.uncovered == (8,) assert len(got.capped_out) == 4 assert not set(got.capped_out) & set(got.uncovered) def test_four_donors_that_do_cover_everything_are_a_complete_answer(): """The cap only bites when it has to. At exactly four, nothing changes.""" target, donors, table = _one_each(4) got = rm.suggest(target, donors, canned(table)) assert got.status == rm.COMPLETE assert got.donors_needed == 4 assert got.capped_out == () def test_the_cap_is_not_the_proposal_limit(): """THE TWO AXES. `max_donors` bounds the size of one answer; `max_proposals` bounds how many answers come back. Conflating them gives either one giant proposal or four tiny ones.""" target, donors, table = _one_each(8) got = rm.suggest(target, donors, canned(table), max_proposals=2) assert len(got.proposals) == 2 # the proposal limit bit assert got.cover_count > 2 # and there were more of them for proposal in got.proposals: assert len(proposal.donors) == 4 # the donor cap bit separately def test_under_the_cap_covering_more_beats_using_fewer_donors(): """Fewest donors is the objective only among answers that cover the same ground. Once the cap can stop a walk short, a two-donor set covering two features must not outrank a four-donor set covering six.""" target = ['a', 'b', 'c', 'd', 'e', 'f'] donors = [ ('PNL_SMALL_1', ['a']), ('PNL_SMALL_2', ['b']), ('PNL_BIG_1', ['c', 'd']), ('PNL_BIG_2', ['e', 'f']), ] table = {('a', 'a'): 0.0, ('b', 'b'): 0.0, ('c', 'c'): 0.0, ('d', 'd'): 0.0, ('e', 'e'): 0.0, ('f', 'f'): 0.0} got = rm.suggest(target, donors, canned(table), max_donors=2) assert got.best.covered_count == 4 assert got.best.donors == ('PNL_BIG_1', 'PNL_BIG_2') assert got.status == rm.CAPPED def test_the_cap_can_be_lifted_and_the_old_answer_comes_back(): """`max_donors=None` is the uncapped matcher, unchanged. Kept so the ruling stays a parameter rather than a rewrite.""" target, donors, table = _one_each(8) got = rm.suggest(target, donors, canned(table), max_donors=None) assert got.status == rm.COMPLETE assert got.donors_needed == 8 assert got.capped_out == () def test_a_better_fit_still_wins_under_the_cap(): """THE LOAD-BEARING RULE, re-checked with the cap in place: a better fit outranks everything below it, MASTER included. Both covers here are of legal size, so the cap has nothing to say and must not interfere.""" target = ['t'] donors = [('PNL811', ['a']), ('PNL812', ['b'])] covers = canned({('a', 't'): 0.5, ('b', 't'): 0.0}) got = rm.suggest(target, donors, covers, masters=['PNL811']) assert got.best.donors == ('PNL812',) # the exact one, not the MASTER assert got.best.exact_count == 1 def test_the_cap_does_not_reorder_covers_of_equal_reach(): """Among covers that reach equally far the ranking is the old one, in order: fewest donors, then the distance vector, then MASTER, then input order. The cap adds a term above all of that and it is constant here.""" target = ['t0', 't1'] donors = [('PNL821', ['a', 'b']), ('PNL822', ['c', 'd'])] covers = canned({('a', 't0'): 1.0, ('b', 't1'): 1.0, ('c', 't0'): 0.0, ('d', 't1'): 0.0}) got = rm.suggest(target, donors, covers) assert [p.donors[0] for p in got.proposals] == ['PNL822', 'PNL821']