problemreductions/rules/unitdiskmapping/ksg/
gadgets_weighted.rs

1//! KSG weighted square lattice gadgets for resolving crossings.
2//!
3//! This module contains weighted gadget implementations for the King's SubGraph (KSG)
4//! weighted mapping. Each weighted gadget implements the Pattern trait directly with
5//! actual weight methods, following Julia's formula: mis_overhead(weighted) = mis_overhead(unweighted) * 2.
6
7use super::super::grid::{CellState, MappingGrid};
8use super::super::traits::{apply_gadget, pattern_matches, Pattern, PatternCell};
9use super::gadgets::{KsgReflectedGadget, KsgRotatedGadget, Mirror};
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12
13/// Type alias for weighted pattern factory function used in crossing gadget matching.
14type WeightedPatternFactory = Box<dyn Fn() -> Box<dyn WeightedKsgPatternBoxed>>;
15
16/// Type alias for source graph representation: (locations, pin_edges, source_pins).
17pub type SourceGraph = (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>);
18
19// ============================================================================
20// Weighted Crossing Gadgets
21// ============================================================================
22
23/// Weighted crossing gadget for resolving two crossing copy-lines.
24///
25/// `WeightedKsgCross<true>`: connected crossing (edges share a vertex), size (3,3)
26/// `WeightedKsgCross<false>`: disconnected crossing, size (4,5)
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
28pub struct WeightedKsgCross<const CON: bool>;
29
30impl Pattern for WeightedKsgCross<true> {
31    fn size(&self) -> (usize, usize) {
32        (3, 3)
33    }
34
35    fn cross_location(&self) -> (usize, usize) {
36        (2, 2)
37    }
38
39    fn is_connected(&self) -> bool {
40        true
41    }
42
43    fn is_cross_gadget(&self) -> bool {
44        true
45    }
46
47    fn connected_nodes(&self) -> Vec<usize> {
48        vec![0, 5]
49    }
50
51    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
52        let locs = vec![(2, 1), (2, 2), (2, 3), (1, 2), (2, 2), (3, 2)];
53        let edges = vec![(0, 1), (1, 2), (3, 4), (4, 5), (0, 5)];
54        let pins = vec![0, 3, 5, 2];
55        (locs, edges, pins)
56    }
57
58    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
59        let locs = vec![(2, 1), (2, 2), (2, 3), (1, 2), (3, 2)];
60        let pins = vec![0, 3, 4, 2];
61        (locs, pins)
62    }
63
64    fn mis_overhead(&self) -> i32 {
65        -2 // 2x unweighted value (-1 * 2)
66    }
67
68    fn source_weights(&self) -> Vec<i32> {
69        vec![2; 6]
70    }
71
72    fn mapped_weights(&self) -> Vec<i32> {
73        vec![2; 5]
74    }
75
76    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
77        [
78            (5, 5),
79            (12, 12),
80            (8, 0),
81            (1, 0),
82            (0, 0),
83            (6, 6),
84            (11, 11),
85            (9, 9),
86            (14, 14),
87            (3, 3),
88            (7, 7),
89            (4, 0),
90            (13, 13),
91            (15, 15),
92            (2, 0),
93            (10, 10),
94        ]
95        .into_iter()
96        .collect()
97    }
98
99    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
100        let mut map = HashMap::new();
101        map.insert(0, vec![vec![false, true, false, false, true, false]]);
102        map.insert(1, vec![vec![true, false, false, false, true, false]]);
103        map.insert(3, vec![vec![true, false, false, true, false, false]]);
104        map.insert(4, vec![vec![false, true, false, false, false, true]]);
105        map.insert(6, vec![vec![false, true, false, true, false, true]]);
106        map.insert(8, vec![vec![false, false, true, false, true, false]]);
107        map.insert(9, vec![vec![true, false, true, false, true, false]]);
108        map.insert(10, vec![vec![false, false, true, true, false, false]]);
109        map.insert(11, vec![vec![true, false, true, true, false, false]]);
110        map.insert(12, vec![vec![false, false, true, false, false, true]]);
111        map.insert(14, vec![vec![false, false, true, true, false, true]]);
112        map.insert(5, vec![]);
113        map.insert(7, vec![]);
114        map.insert(13, vec![]);
115        map.insert(15, vec![]);
116        map.insert(2, vec![vec![false, true, false, true, false, false]]);
117        map
118    }
119}
120
121impl Pattern for WeightedKsgCross<false> {
122    fn size(&self) -> (usize, usize) {
123        (4, 5)
124    }
125
126    fn cross_location(&self) -> (usize, usize) {
127        (2, 3)
128    }
129
130    fn is_connected(&self) -> bool {
131        false
132    }
133
134    fn is_cross_gadget(&self) -> bool {
135        true
136    }
137
138    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
139        let locs = vec![
140            (2, 1),
141            (2, 2),
142            (2, 3),
143            (2, 4),
144            (2, 5),
145            (1, 3),
146            (2, 3),
147            (3, 3),
148            (4, 3),
149        ];
150        let edges = vec![(0, 1), (1, 2), (2, 3), (3, 4), (5, 6), (6, 7), (7, 8)];
151        let pins = vec![0, 5, 8, 4];
152        (locs, edges, pins)
153    }
154
155    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
156        let locs = vec![
157            (2, 1),
158            (2, 2),
159            (2, 3),
160            (2, 4),
161            (2, 5),
162            (1, 3),
163            (3, 3),
164            (4, 3),
165            (3, 2),
166            (3, 4),
167        ];
168        let pins = vec![0, 5, 7, 4];
169        (locs, pins)
170    }
171
172    fn mis_overhead(&self) -> i32 {
173        -2 // 2x unweighted value (-1 * 2)
174    }
175
176    fn source_weights(&self) -> Vec<i32> {
177        vec![2; 9]
178    }
179
180    fn mapped_weights(&self) -> Vec<i32> {
181        vec![2; 10]
182    }
183
184    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
185        [
186            (5, 4),
187            (12, 4),
188            (8, 0),
189            (1, 0),
190            (0, 0),
191            (6, 0),
192            (11, 11),
193            (9, 9),
194            (14, 2),
195            (3, 2),
196            (7, 2),
197            (4, 4),
198            (13, 13),
199            (15, 11),
200            (2, 2),
201            (10, 2),
202        ]
203        .into_iter()
204        .collect()
205    }
206
207    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
208        let mut map = HashMap::new();
209        map.insert(
210            0,
211            vec![
212                vec![false, true, false, true, false, false, false, true, false],
213                vec![false, true, false, true, false, false, true, false, false],
214            ],
215        );
216        map.insert(
217            2,
218            vec![vec![
219                false, true, false, true, false, true, false, true, false,
220            ]],
221        );
222        map.insert(
223            4,
224            vec![vec![
225                false, true, false, true, false, false, true, false, true,
226            ]],
227        );
228        map.insert(
229            9,
230            vec![
231                vec![true, false, true, false, true, false, false, true, false],
232                vec![true, false, true, false, true, false, true, false, false],
233            ],
234        );
235        map.insert(
236            11,
237            vec![vec![
238                true, false, true, false, true, true, false, true, false,
239            ]],
240        );
241        map.insert(
242            13,
243            vec![vec![
244                true, false, true, false, true, false, true, false, true,
245            ]],
246        );
247        for i in [1, 3, 5, 6, 7, 8, 10, 12, 14, 15] {
248            map.entry(i).or_insert_with(Vec::new);
249        }
250        map
251    }
252}
253
254/// Weighted turn gadget for 90-degree turns in copy-lines.
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
256pub struct WeightedKsgTurn;
257
258impl Pattern for WeightedKsgTurn {
259    fn size(&self) -> (usize, usize) {
260        (4, 4)
261    }
262    fn cross_location(&self) -> (usize, usize) {
263        (3, 2)
264    }
265    fn is_connected(&self) -> bool {
266        false
267    }
268
269    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
270        let locs = vec![(1, 2), (2, 2), (3, 2), (3, 3), (3, 4)];
271        let edges = vec![(0, 1), (1, 2), (2, 3), (3, 4)];
272        let pins = vec![0, 4];
273        (locs, edges, pins)
274    }
275
276    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
277        let locs = vec![(1, 2), (2, 3), (3, 4)];
278        let pins = vec![0, 2];
279        (locs, pins)
280    }
281
282    fn mis_overhead(&self) -> i32 {
283        -2 // 2x unweighted value (-1 * 2)
284    }
285
286    fn source_weights(&self) -> Vec<i32> {
287        vec![2; 5]
288    }
289
290    fn mapped_weights(&self) -> Vec<i32> {
291        vec![2; 3]
292    }
293
294    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
295        [(0, 0), (2, 0), (3, 3), (1, 0)].into_iter().collect()
296    }
297
298    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
299        let mut map = HashMap::new();
300        map.insert(0, vec![vec![false, true, false, true, false]]);
301        map.insert(
302            1,
303            vec![
304                vec![true, false, true, false, false],
305                vec![true, false, false, true, false],
306            ],
307        );
308        map.insert(
309            2,
310            vec![
311                vec![false, true, false, false, true],
312                vec![false, false, true, false, true],
313            ],
314        );
315        map.insert(3, vec![vec![true, false, true, false, true]]);
316        map
317    }
318}
319
320/// Weighted W-shaped turn gadget.
321#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
322pub struct WeightedKsgWTurn;
323
324impl Pattern for WeightedKsgWTurn {
325    fn size(&self) -> (usize, usize) {
326        (4, 4)
327    }
328    fn cross_location(&self) -> (usize, usize) {
329        (2, 2)
330    }
331    fn is_connected(&self) -> bool {
332        false
333    }
334
335    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
336        let locs = vec![(2, 3), (2, 4), (3, 2), (3, 3), (4, 2)];
337        let edges = vec![(0, 1), (0, 3), (2, 3), (2, 4)];
338        let pins = vec![1, 4];
339        (locs, edges, pins)
340    }
341
342    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
343        let locs = vec![(2, 4), (3, 3), (4, 2)];
344        let pins = vec![0, 2];
345        (locs, pins)
346    }
347
348    fn mis_overhead(&self) -> i32 {
349        -2 // 2x unweighted value (-1 * 2)
350    }
351
352    fn source_weights(&self) -> Vec<i32> {
353        vec![2; 5]
354    }
355
356    fn mapped_weights(&self) -> Vec<i32> {
357        vec![2; 3]
358    }
359
360    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
361        [(0, 0), (2, 0), (3, 3), (1, 0)].into_iter().collect()
362    }
363
364    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
365        let mut map = HashMap::new();
366        map.insert(0, vec![vec![true, false, true, false, false]]);
367        map.insert(
368            1,
369            vec![
370                vec![false, true, false, true, false],
371                vec![false, true, true, false, false],
372            ],
373        );
374        map.insert(
375            2,
376            vec![
377                vec![false, false, false, true, true],
378                vec![true, false, false, false, true],
379            ],
380        );
381        map.insert(3, vec![vec![false, true, false, true, true]]);
382        map
383    }
384}
385
386/// Weighted branch gadget for T-junctions.
387#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
388pub struct WeightedKsgBranch;
389
390impl Pattern for WeightedKsgBranch {
391    fn size(&self) -> (usize, usize) {
392        (5, 4)
393    }
394    fn cross_location(&self) -> (usize, usize) {
395        (3, 2)
396    }
397    fn is_connected(&self) -> bool {
398        false
399    }
400
401    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
402        let locs = vec![
403            (1, 2),
404            (2, 2),
405            (3, 2),
406            (3, 3),
407            (3, 4),
408            (4, 3),
409            (4, 2),
410            (5, 2),
411        ];
412        let edges = vec![(0, 1), (1, 2), (2, 3), (3, 4), (3, 5), (5, 6), (6, 7)];
413        let pins = vec![0, 4, 7];
414        (locs, edges, pins)
415    }
416
417    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
418        let locs = vec![(1, 2), (2, 3), (3, 2), (3, 4), (4, 3), (5, 2)];
419        let pins = vec![0, 3, 5];
420        (locs, pins)
421    }
422
423    fn mis_overhead(&self) -> i32 {
424        -2 // 2x unweighted value (-1 * 2)
425    }
426
427    // Weighted version: node 3 (0-indexed) has weight 3, others have weight 2
428    fn source_weights(&self) -> Vec<i32> {
429        vec![2, 2, 2, 3, 2, 2, 2, 2]
430    }
431
432    // Weighted version: node 1 (0-indexed) has weight 3, others have weight 2
433    fn mapped_weights(&self) -> Vec<i32> {
434        vec![2, 3, 2, 2, 2, 2]
435    }
436
437    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
438        [
439            (0, 0),
440            (4, 0),
441            (5, 5),
442            (6, 6),
443            (2, 0),
444            (7, 7),
445            (3, 3),
446            (1, 0),
447        ]
448        .into_iter()
449        .collect()
450    }
451
452    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
453        let mut map = HashMap::new();
454        map.insert(
455            0,
456            vec![vec![false, true, false, true, false, false, true, false]],
457        );
458        map.insert(
459            3,
460            vec![
461                vec![true, false, true, false, true, false, true, false],
462                vec![true, false, true, false, true, true, false, false],
463            ],
464        );
465        map.insert(
466            5,
467            vec![vec![true, false, true, false, false, true, false, true]],
468        );
469        map.insert(
470            6,
471            vec![
472                vec![false, false, true, false, true, true, false, true],
473                vec![false, true, false, false, true, true, false, true],
474            ],
475        );
476        map.insert(
477            7,
478            vec![vec![true, false, true, false, true, true, false, true]],
479        );
480        for i in [1, 2, 4] {
481            map.insert(i, vec![]);
482        }
483        map
484    }
485}
486
487/// Weighted branch fix gadget for simplifying branches.
488#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
489pub struct WeightedKsgBranchFix;
490
491impl Pattern for WeightedKsgBranchFix {
492    fn size(&self) -> (usize, usize) {
493        (4, 4)
494    }
495    fn cross_location(&self) -> (usize, usize) {
496        (2, 2)
497    }
498    fn is_connected(&self) -> bool {
499        false
500    }
501
502    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
503        let locs = vec![(1, 2), (2, 2), (2, 3), (3, 3), (3, 2), (4, 2)];
504        let edges = vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)];
505        let pins = vec![0, 5];
506        (locs, edges, pins)
507    }
508
509    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
510        let locs = vec![(1, 2), (2, 2), (3, 2), (4, 2)];
511        let pins = vec![0, 3];
512        (locs, pins)
513    }
514
515    fn mis_overhead(&self) -> i32 {
516        -2 // 2x unweighted value (-1 * 2)
517    }
518
519    fn source_weights(&self) -> Vec<i32> {
520        vec![2; 6]
521    }
522
523    fn mapped_weights(&self) -> Vec<i32> {
524        vec![2; 4]
525    }
526
527    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
528        [(0, 0), (2, 2), (3, 1), (1, 1)].into_iter().collect()
529    }
530
531    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
532        let mut map = HashMap::new();
533        map.insert(
534            0,
535            vec![
536                vec![false, true, false, true, false, false],
537                vec![false, true, false, false, true, false],
538                vec![false, false, true, false, true, false],
539            ],
540        );
541        map.insert(1, vec![vec![true, false, true, false, true, false]]);
542        map.insert(2, vec![vec![false, true, false, true, false, true]]);
543        map.insert(
544            3,
545            vec![
546                vec![true, false, false, true, false, true],
547                vec![true, false, true, false, false, true],
548            ],
549        );
550        map
551    }
552}
553
554/// Weighted T-connection gadget.
555#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
556pub struct WeightedKsgTCon;
557
558impl Pattern for WeightedKsgTCon {
559    fn size(&self) -> (usize, usize) {
560        (3, 4)
561    }
562    fn cross_location(&self) -> (usize, usize) {
563        (2, 2)
564    }
565    fn is_connected(&self) -> bool {
566        true
567    }
568    fn connected_nodes(&self) -> Vec<usize> {
569        vec![0, 1]
570    }
571
572    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
573        let locs = vec![(1, 2), (2, 1), (2, 2), (3, 2)];
574        let edges = vec![(0, 1), (0, 2), (2, 3)];
575        let pins = vec![0, 1, 3];
576        (locs, edges, pins)
577    }
578
579    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
580        let locs = vec![(1, 2), (2, 1), (2, 3), (3, 2)];
581        let pins = vec![0, 1, 3];
582        (locs, pins)
583    }
584
585    fn mis_overhead(&self) -> i32 {
586        0 // 2x unweighted value (0 * 2)
587    }
588
589    // Weighted version: node 1 (0-indexed) has weight 1, others have weight 2
590    fn source_weights(&self) -> Vec<i32> {
591        vec![2, 1, 2, 2]
592    }
593
594    // Weighted version: node 1 (0-indexed) has weight 1, others have weight 2
595    fn mapped_weights(&self) -> Vec<i32> {
596        vec![2, 1, 2, 2]
597    }
598
599    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
600        [
601            (0, 0),
602            (4, 0),
603            (5, 5),
604            (6, 6),
605            (2, 2),
606            (7, 7),
607            (3, 3),
608            (1, 0),
609        ]
610        .into_iter()
611        .collect()
612    }
613
614    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
615        let mut map = HashMap::new();
616        map.insert(0, vec![vec![false, false, true, false]]);
617        map.insert(1, vec![vec![true, false, false, false]]);
618        map.insert(2, vec![vec![false, true, true, false]]);
619        map.insert(4, vec![vec![false, false, false, true]]);
620        map.insert(5, vec![vec![true, false, false, true]]);
621        map.insert(6, vec![vec![false, true, false, true]]);
622        map.insert(3, vec![]);
623        map.insert(7, vec![]);
624        map
625    }
626}
627
628/// Weighted trivial turn gadget for simple diagonal turns.
629#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
630pub struct WeightedKsgTrivialTurn;
631
632impl Pattern for WeightedKsgTrivialTurn {
633    fn size(&self) -> (usize, usize) {
634        (2, 2)
635    }
636    fn cross_location(&self) -> (usize, usize) {
637        (2, 2)
638    }
639    fn is_connected(&self) -> bool {
640        true
641    }
642    fn connected_nodes(&self) -> Vec<usize> {
643        vec![0, 1]
644    }
645
646    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
647        let locs = vec![(1, 2), (2, 1)];
648        let edges = vec![(0, 1)];
649        let pins = vec![0, 1];
650        (locs, edges, pins)
651    }
652
653    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
654        let locs = vec![(1, 2), (2, 1)];
655        let pins = vec![0, 1];
656        (locs, pins)
657    }
658
659    fn mis_overhead(&self) -> i32 {
660        0 // 2x unweighted value (0 * 2)
661    }
662
663    // Weighted version: both nodes have weight 1
664    fn source_weights(&self) -> Vec<i32> {
665        vec![1, 1]
666    }
667
668    // Weighted version: both nodes have weight 1
669    fn mapped_weights(&self) -> Vec<i32> {
670        vec![1, 1]
671    }
672
673    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
674        [(0, 0), (2, 2), (3, 3), (1, 1)].into_iter().collect()
675    }
676
677    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
678        let mut map = HashMap::new();
679        map.insert(0, vec![vec![false, false]]);
680        map.insert(1, vec![vec![true, false]]);
681        map.insert(2, vec![vec![false, true]]);
682        map.insert(3, vec![]);
683        map
684    }
685}
686
687/// Weighted end turn gadget for line terminations.
688#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
689pub struct WeightedKsgEndTurn;
690
691impl Pattern for WeightedKsgEndTurn {
692    fn size(&self) -> (usize, usize) {
693        (3, 4)
694    }
695    fn cross_location(&self) -> (usize, usize) {
696        (2, 2)
697    }
698    fn is_connected(&self) -> bool {
699        false
700    }
701
702    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
703        let locs = vec![(1, 2), (2, 2), (2, 3)];
704        let edges = vec![(0, 1), (1, 2)];
705        let pins = vec![0];
706        (locs, edges, pins)
707    }
708
709    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
710        let locs = vec![(1, 2)];
711        let pins = vec![0];
712        (locs, pins)
713    }
714
715    fn mis_overhead(&self) -> i32 {
716        -2 // 2x unweighted value (-1 * 2)
717    }
718
719    // Weighted version: node 2 (0-indexed) has weight 1, others have weight 2
720    fn source_weights(&self) -> Vec<i32> {
721        vec![2, 2, 1]
722    }
723
724    // Weighted version: node 0 (0-indexed) has weight 1
725    fn mapped_weights(&self) -> Vec<i32> {
726        vec![1]
727    }
728
729    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
730        [(0, 0), (1, 1)].into_iter().collect()
731    }
732
733    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
734        let mut map = HashMap::new();
735        map.insert(0, vec![vec![false, false, true], vec![false, true, false]]);
736        map.insert(1, vec![vec![true, false, true]]);
737        map
738    }
739}
740
741/// Weighted alternate branch fix gadget.
742#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
743pub struct WeightedKsgBranchFixB;
744
745impl Pattern for WeightedKsgBranchFixB {
746    fn size(&self) -> (usize, usize) {
747        (4, 4)
748    }
749    fn cross_location(&self) -> (usize, usize) {
750        (2, 2)
751    }
752    fn is_connected(&self) -> bool {
753        false
754    }
755
756    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
757        let locs = vec![(2, 3), (3, 2), (3, 3), (4, 2)];
758        let edges = vec![(0, 2), (1, 2), (1, 3)];
759        let pins = vec![0, 3];
760        (locs, edges, pins)
761    }
762
763    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
764        let locs = vec![(3, 2), (4, 2)];
765        let pins = vec![0, 1];
766        (locs, pins)
767    }
768
769    fn mis_overhead(&self) -> i32 {
770        -2 // 2x unweighted value (-1 * 2)
771    }
772
773    // Weighted version: node 0 (0-indexed) has weight 1, others have weight 2
774    fn source_weights(&self) -> Vec<i32> {
775        vec![1, 2, 2, 2]
776    }
777
778    // Weighted version: node 0 (0-indexed) has weight 1, node 1 has weight 2
779    fn mapped_weights(&self) -> Vec<i32> {
780        vec![1, 2]
781    }
782
783    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
784        [(0, 0), (2, 2), (3, 3), (1, 1)].into_iter().collect()
785    }
786
787    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
788        let mut map = HashMap::new();
789        map.insert(
790            0,
791            vec![
792                vec![false, false, true, false],
793                vec![false, true, false, false],
794            ],
795        );
796        map.insert(1, vec![vec![true, true, false, false]]);
797        map.insert(2, vec![vec![false, false, true, true]]);
798        map.insert(3, vec![vec![true, false, false, true]]);
799        map
800    }
801}
802
803/// Weighted dangling leg simplifier pattern.
804#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
805pub struct WeightedKsgDanglingLeg;
806
807impl Pattern for WeightedKsgDanglingLeg {
808    fn size(&self) -> (usize, usize) {
809        (4, 3)
810    }
811    fn cross_location(&self) -> (usize, usize) {
812        (2, 1)
813    }
814    fn is_connected(&self) -> bool {
815        false
816    }
817
818    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
819        let locs = vec![(2, 2), (3, 2), (4, 2)];
820        let edges = vec![(0, 1), (1, 2)];
821        let pins = vec![2];
822        (locs, edges, pins)
823    }
824
825    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
826        let locs = vec![(4, 2)];
827        let pins = vec![0];
828        (locs, pins)
829    }
830
831    fn mis_overhead(&self) -> i32 {
832        -2 // 2x unweighted value (-1 * 2)
833    }
834
835    // Weighted version: node 0 (0-indexed) has weight 1, others have weight 2
836    fn source_weights(&self) -> Vec<i32> {
837        vec![1, 2, 2]
838    }
839
840    // Weighted version: node 0 (0-indexed) has weight 1
841    fn mapped_weights(&self) -> Vec<i32> {
842        vec![1]
843    }
844
845    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
846        [(0, 0), (1, 1)].into_iter().collect()
847    }
848
849    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
850        let mut map = HashMap::new();
851        map.insert(0, vec![vec![true, false, false], vec![false, true, false]]);
852        map.insert(1, vec![vec![true, false, true]]);
853        map
854    }
855}
856
857// ============================================================================
858// WeightedKsgPattern Enum for Dynamic Dispatch
859// ============================================================================
860
861/// Enum wrapping all weighted KSG square lattice patterns for dynamic dispatch during unapply.
862#[derive(Debug, Clone)]
863pub enum WeightedKsgPattern {
864    CrossFalse(WeightedKsgCross<false>),
865    CrossTrue(WeightedKsgCross<true>),
866    Turn(WeightedKsgTurn),
867    WTurn(WeightedKsgWTurn),
868    Branch(WeightedKsgBranch),
869    BranchFix(WeightedKsgBranchFix),
870    TCon(WeightedKsgTCon),
871    TrivialTurn(WeightedKsgTrivialTurn),
872    EndTurn(WeightedKsgEndTurn),
873    BranchFixB(WeightedKsgBranchFixB),
874    DanglingLeg(WeightedKsgDanglingLeg),
875    RotatedTCon1(KsgRotatedGadget<WeightedKsgTCon>),
876    ReflectedCrossTrue(KsgReflectedGadget<WeightedKsgCross<true>>),
877    ReflectedTrivialTurn(KsgReflectedGadget<WeightedKsgTrivialTurn>),
878    ReflectedRotatedTCon1(KsgReflectedGadget<KsgRotatedGadget<WeightedKsgTCon>>),
879    DanglingLegRot1(KsgRotatedGadget<WeightedKsgDanglingLeg>),
880    DanglingLegRot2(KsgRotatedGadget<KsgRotatedGadget<WeightedKsgDanglingLeg>>),
881    DanglingLegRot3(KsgRotatedGadget<KsgRotatedGadget<KsgRotatedGadget<WeightedKsgDanglingLeg>>>),
882    DanglingLegReflX(KsgReflectedGadget<WeightedKsgDanglingLeg>),
883    DanglingLegReflY(KsgReflectedGadget<WeightedKsgDanglingLeg>),
884}
885
886impl WeightedKsgPattern {
887    /// Get pattern from tape index.
888    pub fn from_tape_idx(idx: usize) -> Option<Self> {
889        match idx {
890            0 => Some(Self::CrossFalse(WeightedKsgCross::<false>)),
891            1 => Some(Self::Turn(WeightedKsgTurn)),
892            2 => Some(Self::WTurn(WeightedKsgWTurn)),
893            3 => Some(Self::Branch(WeightedKsgBranch)),
894            4 => Some(Self::BranchFix(WeightedKsgBranchFix)),
895            5 => Some(Self::TCon(WeightedKsgTCon)),
896            6 => Some(Self::TrivialTurn(WeightedKsgTrivialTurn)),
897            7 => Some(Self::RotatedTCon1(KsgRotatedGadget::new(
898                WeightedKsgTCon,
899                1,
900            ))),
901            8 => Some(Self::ReflectedCrossTrue(KsgReflectedGadget::new(
902                WeightedKsgCross::<true>,
903                Mirror::Y,
904            ))),
905            9 => Some(Self::ReflectedTrivialTurn(KsgReflectedGadget::new(
906                WeightedKsgTrivialTurn,
907                Mirror::Y,
908            ))),
909            10 => Some(Self::BranchFixB(WeightedKsgBranchFixB)),
910            11 => Some(Self::EndTurn(WeightedKsgEndTurn)),
911            12 => Some(Self::ReflectedRotatedTCon1(KsgReflectedGadget::new(
912                KsgRotatedGadget::new(WeightedKsgTCon, 1),
913                Mirror::Y,
914            ))),
915            100 => Some(Self::DanglingLeg(WeightedKsgDanglingLeg)),
916            101 => Some(Self::DanglingLegRot1(KsgRotatedGadget::new(
917                WeightedKsgDanglingLeg,
918                1,
919            ))),
920            102 => Some(Self::DanglingLegRot2(KsgRotatedGadget::new(
921                KsgRotatedGadget::new(WeightedKsgDanglingLeg, 1),
922                1,
923            ))),
924            103 => Some(Self::DanglingLegRot3(KsgRotatedGadget::new(
925                KsgRotatedGadget::new(KsgRotatedGadget::new(WeightedKsgDanglingLeg, 1), 1),
926                1,
927            ))),
928            104 => Some(Self::DanglingLegReflX(KsgReflectedGadget::new(
929                WeightedKsgDanglingLeg,
930                Mirror::X,
931            ))),
932            105 => Some(Self::DanglingLegReflY(KsgReflectedGadget::new(
933                WeightedKsgDanglingLeg,
934                Mirror::Y,
935            ))),
936            _ => None,
937        }
938    }
939
940    /// Apply map_config_back_pattern for this pattern.
941    pub fn map_config_back(&self, gi: usize, gj: usize, config: &mut [Vec<usize>]) {
942        match self {
943            Self::CrossFalse(p) => map_config_back_pattern(p, gi, gj, config),
944            Self::CrossTrue(p) => map_config_back_pattern(p, gi, gj, config),
945            Self::Turn(p) => map_config_back_pattern(p, gi, gj, config),
946            Self::WTurn(p) => map_config_back_pattern(p, gi, gj, config),
947            Self::Branch(p) => map_config_back_pattern(p, gi, gj, config),
948            Self::BranchFix(p) => map_config_back_pattern(p, gi, gj, config),
949            Self::TCon(p) => map_config_back_pattern(p, gi, gj, config),
950            Self::TrivialTurn(p) => map_config_back_pattern(p, gi, gj, config),
951            Self::EndTurn(p) => map_config_back_pattern(p, gi, gj, config),
952            Self::BranchFixB(p) => map_config_back_pattern(p, gi, gj, config),
953            Self::DanglingLeg(p) => map_config_back_pattern(p, gi, gj, config),
954            Self::RotatedTCon1(p) => map_config_back_pattern(p, gi, gj, config),
955            Self::ReflectedCrossTrue(p) => map_config_back_pattern(p, gi, gj, config),
956            Self::ReflectedTrivialTurn(p) => map_config_back_pattern(p, gi, gj, config),
957            Self::ReflectedRotatedTCon1(p) => map_config_back_pattern(p, gi, gj, config),
958            Self::DanglingLegRot1(p) => map_config_back_pattern(p, gi, gj, config),
959            Self::DanglingLegRot2(p) => map_config_back_pattern(p, gi, gj, config),
960            Self::DanglingLegRot3(p) => map_config_back_pattern(p, gi, gj, config),
961            Self::DanglingLegReflX(p) => map_config_back_pattern(p, gi, gj, config),
962            Self::DanglingLegReflY(p) => map_config_back_pattern(p, gi, gj, config),
963        }
964    }
965}
966
967// ============================================================================
968// Weighted Tape Entry and Apply Functions
969// ============================================================================
970
971/// A tape entry recording a weighted gadget application.
972#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
973pub struct WeightedKsgTapeEntry {
974    pub pattern_idx: usize,
975    pub row: usize,
976    pub col: usize,
977}
978
979/// Calculate MIS overhead for a weighted tape entry.
980pub fn weighted_tape_entry_mis_overhead(entry: &WeightedKsgTapeEntry) -> i32 {
981    match entry.pattern_idx {
982        0 => WeightedKsgCross::<false>.mis_overhead(),
983        1 => WeightedKsgTurn.mis_overhead(),
984        2 => WeightedKsgWTurn.mis_overhead(),
985        3 => WeightedKsgBranch.mis_overhead(),
986        4 => WeightedKsgBranchFix.mis_overhead(),
987        5 => WeightedKsgTCon.mis_overhead(),
988        6 => WeightedKsgTrivialTurn.mis_overhead(),
989        7 => KsgRotatedGadget::new(WeightedKsgTCon, 1).mis_overhead(),
990        8 => KsgReflectedGadget::new(WeightedKsgCross::<true>, Mirror::Y).mis_overhead(),
991        9 => KsgReflectedGadget::new(WeightedKsgTrivialTurn, Mirror::Y).mis_overhead(),
992        10 => WeightedKsgBranchFixB.mis_overhead(),
993        11 => WeightedKsgEndTurn.mis_overhead(),
994        12 => KsgReflectedGadget::new(KsgRotatedGadget::new(WeightedKsgTCon, 1), Mirror::Y)
995            .mis_overhead(),
996        100..=105 => WeightedKsgDanglingLeg.mis_overhead(),
997        _ => 0,
998    }
999}
1000
1001/// Trait for boxed weighted pattern operations.
1002pub trait WeightedKsgPatternBoxed {
1003    fn size_boxed(&self) -> (usize, usize);
1004    fn cross_location(&self) -> (usize, usize);
1005    fn source_matrix(&self) -> Vec<Vec<PatternCell>>;
1006    fn mapped_matrix(&self) -> Vec<Vec<PatternCell>>;
1007    fn source_graph_boxed(&self) -> SourceGraph;
1008    fn mapped_graph_boxed(&self) -> (Vec<(usize, usize)>, Vec<usize>);
1009    fn source_weights_boxed(&self) -> Vec<i32>;
1010    fn mapped_weights_boxed(&self) -> Vec<i32>;
1011    fn pattern_matches_boxed(&self, grid: &MappingGrid, i: usize, j: usize) -> bool;
1012    fn apply_gadget_boxed(&self, grid: &mut MappingGrid, i: usize, j: usize);
1013    fn apply_weighted_gadget_boxed(&self, grid: &mut MappingGrid, i: usize, j: usize);
1014}
1015
1016impl<P: Pattern> WeightedKsgPatternBoxed for P {
1017    fn size_boxed(&self) -> (usize, usize) {
1018        self.size()
1019    }
1020    fn cross_location(&self) -> (usize, usize) {
1021        Pattern::cross_location(self)
1022    }
1023    fn source_matrix(&self) -> Vec<Vec<PatternCell>> {
1024        Pattern::source_matrix(self)
1025    }
1026    fn mapped_matrix(&self) -> Vec<Vec<PatternCell>> {
1027        Pattern::mapped_matrix(self)
1028    }
1029    fn source_graph_boxed(&self) -> SourceGraph {
1030        Pattern::source_graph(self)
1031    }
1032    fn mapped_graph_boxed(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
1033        Pattern::mapped_graph(self)
1034    }
1035    fn source_weights_boxed(&self) -> Vec<i32> {
1036        Pattern::source_weights(self)
1037    }
1038    fn mapped_weights_boxed(&self) -> Vec<i32> {
1039        Pattern::mapped_weights(self)
1040    }
1041    fn pattern_matches_boxed(&self, grid: &MappingGrid, i: usize, j: usize) -> bool {
1042        pattern_matches(self, grid, i, j)
1043    }
1044    fn apply_gadget_boxed(&self, grid: &mut MappingGrid, i: usize, j: usize) {
1045        apply_gadget(self, grid, i, j);
1046    }
1047    fn apply_weighted_gadget_boxed(&self, grid: &mut MappingGrid, i: usize, j: usize) {
1048        apply_weighted_gadget(self, grid, i, j);
1049    }
1050}
1051
1052/// Apply a weighted gadget pattern at position (i, j) with proper weights.
1053/// Uses mapped_graph locations and mapped_weights for each node.
1054#[allow(clippy::needless_range_loop)]
1055pub fn apply_weighted_gadget<P: Pattern>(pattern: &P, grid: &mut MappingGrid, i: usize, j: usize) {
1056    let (m, n) = pattern.size();
1057    let (mapped_locs, _) = pattern.mapped_graph();
1058    let mapped_weights = pattern.mapped_weights();
1059
1060    // First clear the gadget area
1061    for r in 0..m {
1062        for c in 0..n {
1063            let grid_r = i + r;
1064            let grid_c = j + c;
1065            grid.set(grid_r, grid_c, CellState::Empty);
1066        }
1067    }
1068
1069    // Build a map of (row, col) -> accumulated weight for doubled nodes
1070    let mut weight_map: HashMap<(usize, usize), i32> = HashMap::new();
1071    for (idx, &(r, c)) in mapped_locs.iter().enumerate() {
1072        let weight = mapped_weights.get(idx).copied().unwrap_or(2);
1073        *weight_map.entry((r, c)).or_insert(0) += weight;
1074    }
1075
1076    // Count occurrences to detect doubled nodes
1077    let mut count_map: HashMap<(usize, usize), usize> = HashMap::new();
1078    for &(r, c) in &mapped_locs {
1079        *count_map.entry((r, c)).or_insert(0) += 1;
1080    }
1081
1082    // Set cells with proper weights
1083    for (&(r, c), &total_weight) in &weight_map {
1084        let grid_r = i + r - 1; // Convert 1-indexed to 0-indexed
1085        let grid_c = j + c - 1;
1086        let count = count_map.get(&(r, c)).copied().unwrap_or(1);
1087
1088        let state = if count > 1 {
1089            CellState::Doubled {
1090                weight: total_weight,
1091            }
1092        } else {
1093            CellState::Occupied {
1094                weight: total_weight,
1095            }
1096        };
1097        grid.set(grid_r, grid_c, state);
1098    }
1099}
1100
1101/// Apply all weighted crossing gadgets to the grid.
1102pub fn apply_weighted_crossing_gadgets(
1103    grid: &mut MappingGrid,
1104    copylines: &[super::super::copyline::CopyLine],
1105) -> Vec<WeightedKsgTapeEntry> {
1106    let mut tape = Vec::new();
1107    let n = copylines.len();
1108
1109    for j in 0..n {
1110        for i in 0..n {
1111            let (cross_row, cross_col) = crossat(grid, copylines, i, j);
1112            if let Some((pattern_idx, row, col)) =
1113                try_match_and_apply_weighted_crossing(grid, cross_row, cross_col)
1114            {
1115                tape.push(WeightedKsgTapeEntry {
1116                    pattern_idx,
1117                    row,
1118                    col,
1119                });
1120            }
1121        }
1122    }
1123    tape
1124}
1125
1126/// Calculate crossing point for two copylines.
1127fn crossat(
1128    grid: &MappingGrid,
1129    copylines: &[super::super::copyline::CopyLine],
1130    v: usize,
1131    w: usize,
1132) -> (usize, usize) {
1133    let line_v = copylines.get(v);
1134    let line_w = copylines.get(w);
1135
1136    match (line_v, line_w) {
1137        (Some(lv), Some(lw)) => {
1138            let (line_first, line_second) = if lv.vslot < lw.vslot {
1139                (lv, lw)
1140            } else {
1141                (lw, lv)
1142            };
1143            grid.cross_at(line_first.vslot, line_second.vslot, line_first.hslot)
1144        }
1145        _ => (0, 0),
1146    }
1147}
1148
1149fn try_match_and_apply_weighted_crossing(
1150    grid: &mut MappingGrid,
1151    cross_row: usize,
1152    cross_col: usize,
1153) -> Option<(usize, usize, usize)> {
1154    // Try each pattern in order
1155    let patterns: Vec<(usize, WeightedPatternFactory)> = vec![
1156        (0, Box::new(|| Box::new(WeightedKsgCross::<false>))),
1157        (1, Box::new(|| Box::new(WeightedKsgTurn))),
1158        (2, Box::new(|| Box::new(WeightedKsgWTurn))),
1159        (3, Box::new(|| Box::new(WeightedKsgBranch))),
1160        (4, Box::new(|| Box::new(WeightedKsgBranchFix))),
1161        (5, Box::new(|| Box::new(WeightedKsgTCon))),
1162        (6, Box::new(|| Box::new(WeightedKsgTrivialTurn))),
1163        (
1164            7,
1165            Box::new(|| Box::new(KsgRotatedGadget::new(WeightedKsgTCon, 1))),
1166        ),
1167        (
1168            8,
1169            Box::new(|| Box::new(KsgReflectedGadget::new(WeightedKsgCross::<true>, Mirror::Y))),
1170        ),
1171        (
1172            9,
1173            Box::new(|| Box::new(KsgReflectedGadget::new(WeightedKsgTrivialTurn, Mirror::Y))),
1174        ),
1175        (10, Box::new(|| Box::new(WeightedKsgBranchFixB))),
1176        (11, Box::new(|| Box::new(WeightedKsgEndTurn))),
1177        (
1178            12,
1179            Box::new(|| {
1180                Box::new(KsgReflectedGadget::new(
1181                    KsgRotatedGadget::new(WeightedKsgTCon, 1),
1182                    Mirror::Y,
1183                ))
1184            }),
1185        ),
1186    ];
1187
1188    for (idx, make_pattern) in patterns {
1189        let pattern = make_pattern();
1190        let cl = pattern.cross_location();
1191        if cross_row + 1 >= cl.0 && cross_col + 1 >= cl.1 {
1192            let x = cross_row + 1 - cl.0;
1193            let y = cross_col + 1 - cl.1;
1194            let matches = pattern.pattern_matches_boxed(grid, x, y);
1195            if matches {
1196                pattern.apply_weighted_gadget_boxed(grid, x, y);
1197                return Some((idx, x, y));
1198            }
1199        }
1200    }
1201    None
1202}
1203
1204/// Apply weighted simplifier gadgets (WeightedKsgDanglingLeg variants).
1205pub fn apply_weighted_simplifier_gadgets(
1206    grid: &mut MappingGrid,
1207    nrepeat: usize,
1208) -> Vec<WeightedKsgTapeEntry> {
1209    let mut tape = Vec::new();
1210    let (rows, cols) = grid.size();
1211
1212    let patterns = rotated_and_reflected_weighted_danglingleg();
1213
1214    for _ in 0..nrepeat {
1215        for (pattern_idx, pattern) in patterns.iter().enumerate() {
1216            for j in 0..cols {
1217                for i in 0..rows {
1218                    if pattern_matches_weighted(pattern.as_ref(), grid, i, j) {
1219                        pattern.apply_weighted_gadget_boxed(grid, i, j);
1220                        tape.push(WeightedKsgTapeEntry {
1221                            pattern_idx: 100 + pattern_idx,
1222                            row: i,
1223                            col: j,
1224                        });
1225                    }
1226                }
1227            }
1228        }
1229    }
1230
1231    tape
1232}
1233
1234/// Check if a weighted KsgDanglingLeg pattern matches.
1235/// For weighted mode, the center node must have weight 1.
1236fn pattern_matches_weighted(
1237    pattern: &dyn WeightedKsgPatternBoxed,
1238    grid: &MappingGrid,
1239    i: usize,
1240    j: usize,
1241) -> bool {
1242    // First check basic pattern match
1243    if !pattern.pattern_matches_boxed(grid, i, j) {
1244        return false;
1245    }
1246
1247    // Check that source weights match the grid weights
1248    let (locs, _, _) = pattern.source_graph_boxed();
1249    let source_weights = pattern.source_weights_boxed();
1250
1251    for (idx, (loc_r, loc_c)) in locs.iter().enumerate() {
1252        let grid_r = i + loc_r - 1;
1253        let grid_c = j + loc_c - 1;
1254        if let Some(cell) = grid.get(grid_r, grid_c) {
1255            let expected_weight = source_weights.get(idx).copied().unwrap_or(2);
1256            if cell.weight() != expected_weight {
1257                return false;
1258            }
1259        }
1260    }
1261
1262    true
1263}
1264
1265fn rotated_and_reflected_weighted_danglingleg() -> Vec<Box<dyn WeightedKsgPatternBoxed>> {
1266    vec![
1267        Box::new(WeightedKsgDanglingLeg),
1268        Box::new(KsgRotatedGadget::new(WeightedKsgDanglingLeg, 1)),
1269        Box::new(KsgRotatedGadget::new(WeightedKsgDanglingLeg, 2)),
1270        Box::new(KsgRotatedGadget::new(WeightedKsgDanglingLeg, 3)),
1271        Box::new(KsgReflectedGadget::new(WeightedKsgDanglingLeg, Mirror::X)),
1272        Box::new(KsgReflectedGadget::new(WeightedKsgDanglingLeg, Mirror::Y)),
1273    ]
1274}
1275
1276/// Map configuration back through a single gadget.
1277pub fn map_config_back_pattern<P: Pattern>(
1278    pattern: &P,
1279    gi: usize,
1280    gj: usize,
1281    config: &mut [Vec<usize>],
1282) {
1283    let (m, n) = pattern.size();
1284    let (mapped_locs, mapped_pins) = pattern.mapped_graph();
1285    let (source_locs, _, _) = pattern.source_graph();
1286
1287    // Step 1: Extract config at mapped locations
1288    let mapped_config: Vec<usize> = mapped_locs
1289        .iter()
1290        .map(|&(r, c)| {
1291            let row = gi + r - 1;
1292            let col = gj + c - 1;
1293            config
1294                .get(row)
1295                .and_then(|row_vec| row_vec.get(col))
1296                .copied()
1297                .unwrap_or(0)
1298        })
1299        .collect();
1300
1301    // Step 2: Compute boundary config
1302    let bc = {
1303        let mut result = 0usize;
1304        for (i, &pin_idx) in mapped_pins.iter().enumerate() {
1305            if pin_idx < mapped_config.len() && mapped_config[pin_idx] > 0 {
1306                result |= 1 << i;
1307            }
1308        }
1309        result
1310    };
1311
1312    // Step 3: Look up source config
1313    let d1 = pattern.mapped_entry_to_compact();
1314    let d2 = pattern.source_entry_to_configs();
1315
1316    let compact = d1.get(&bc).copied();
1317    debug_assert!(
1318        compact.is_some(),
1319        "Boundary config {} not found in mapped_entry_to_compact",
1320        bc
1321    );
1322    let compact = compact.unwrap_or(0);
1323
1324    let source_configs = d2.get(&compact).cloned();
1325    debug_assert!(
1326        source_configs.is_some(),
1327        "Compact {} not found in source_entry_to_configs",
1328        compact
1329    );
1330    let source_configs = source_configs.unwrap_or_default();
1331
1332    debug_assert!(
1333        !source_configs.is_empty(),
1334        "Empty source configs for compact {}.",
1335        compact
1336    );
1337    let new_config = if source_configs.is_empty() {
1338        vec![false; source_locs.len()]
1339    } else {
1340        source_configs[0].clone()
1341    };
1342
1343    // Step 4: Clear gadget area
1344    for row in gi..gi + m {
1345        for col in gj..gj + n {
1346            if let Some(row_vec) = config.get_mut(row) {
1347                if let Some(cell) = row_vec.get_mut(col) {
1348                    *cell = 0;
1349                }
1350            }
1351        }
1352    }
1353
1354    // Step 5: Write source config
1355    for (k, &(r, c)) in source_locs.iter().enumerate() {
1356        let row = gi + r - 1;
1357        let col = gj + c - 1;
1358        if let Some(rv) = config.get_mut(row) {
1359            if let Some(cv) = rv.get_mut(col) {
1360                *cv += if new_config.get(k).copied().unwrap_or(false) {
1361                    1
1362                } else {
1363                    0
1364                };
1365            }
1366        }
1367    }
1368}
1369
1370#[cfg(test)]
1371#[path = "../../../unit_tests/rules/unitdiskmapping/ksg/gadgets_weighted.rs"]
1372mod tests;