problemreductions/rules/unitdiskmapping/ksg/
gadgets.rs

1//! KSG unweighted square lattice gadgets for resolving crossings.
2//!
3//! This module contains all gadget implementations for the King's SubGraph (KSG)
4//! unweighted mapping: KsgCross, KsgTurn, KsgWTurn, KsgBranch, KsgBranchFix, KsgTCon,
5//! KsgTrivialTurn, KsgEndTurn, KsgBranchFixB, KsgDanglingLeg, and their rotated/reflected variants.
6
7use super::super::grid::{CellState, MappingGrid};
8use super::super::traits::{apply_gadget, pattern_matches, Pattern, PatternCell};
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12/// Type alias for pattern factory function used in crossing gadget matching.
13type PatternFactory = Box<dyn Fn() -> Box<dyn KsgPatternBoxed>>;
14
15/// Type alias for source graph representation: (locations, pin_edges, source_pins).
16pub type SourceGraph = (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>);
17
18// ============================================================================
19// Crossing Gadgets - matching Julia's gadgets.jl exactly
20// ============================================================================
21
22/// Crossing gadget for resolving two crossing copy-lines.
23///
24/// `KsgCross<true>`: connected crossing (edges share a vertex), size (3,3)
25/// `KsgCross<false>`: disconnected crossing, size (4,5)
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
27pub struct KsgCross<const CON: bool>;
28
29impl Pattern for KsgCross<true> {
30    fn size(&self) -> (usize, usize) {
31        (3, 3)
32    }
33
34    fn cross_location(&self) -> (usize, usize) {
35        (2, 2)
36    }
37
38    fn is_connected(&self) -> bool {
39        true
40    }
41
42    fn is_cross_gadget(&self) -> bool {
43        true
44    }
45
46    fn connected_nodes(&self) -> Vec<usize> {
47        vec![0, 5]
48    }
49
50    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
51        let locs = vec![(2, 1), (2, 2), (2, 3), (1, 2), (2, 2), (3, 2)];
52        let edges = vec![(0, 1), (1, 2), (3, 4), (4, 5), (0, 5)];
53        let pins = vec![0, 3, 5, 2];
54        (locs, edges, pins)
55    }
56
57    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
58        let locs = vec![(2, 1), (2, 2), (2, 3), (1, 2), (3, 2)];
59        let pins = vec![0, 3, 4, 2];
60        (locs, pins)
61    }
62
63    fn mis_overhead(&self) -> i32 {
64        -1
65    }
66
67    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
68        [
69            (5, 5),
70            (12, 12),
71            (8, 0),
72            (1, 0),
73            (0, 0),
74            (6, 6),
75            (11, 11),
76            (9, 9),
77            (14, 14),
78            (3, 3),
79            (7, 7),
80            (4, 0),
81            (13, 13),
82            (15, 15),
83            (2, 0),
84            (10, 10),
85        ]
86        .into_iter()
87        .collect()
88    }
89
90    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
91        let mut map = HashMap::new();
92        map.insert(0, vec![vec![false, true, false, false, true, false]]);
93        map.insert(1, vec![vec![true, false, false, false, true, false]]);
94        map.insert(3, vec![vec![true, false, false, true, false, false]]);
95        map.insert(4, vec![vec![false, true, false, false, false, true]]);
96        map.insert(6, vec![vec![false, true, false, true, false, true]]);
97        map.insert(8, vec![vec![false, false, true, false, true, false]]);
98        map.insert(9, vec![vec![true, false, true, false, true, false]]);
99        map.insert(10, vec![vec![false, false, true, true, false, false]]);
100        map.insert(11, vec![vec![true, false, true, true, false, false]]);
101        map.insert(12, vec![vec![false, false, true, false, false, true]]);
102        map.insert(14, vec![vec![false, false, true, true, false, true]]);
103        map.insert(5, vec![]);
104        map.insert(7, vec![]);
105        map.insert(13, vec![]);
106        map.insert(15, vec![]);
107        map.insert(2, vec![vec![false, true, false, true, false, false]]);
108        map
109    }
110}
111
112impl Pattern for KsgCross<false> {
113    fn size(&self) -> (usize, usize) {
114        (4, 5)
115    }
116
117    fn cross_location(&self) -> (usize, usize) {
118        (2, 3)
119    }
120
121    fn is_connected(&self) -> bool {
122        false
123    }
124
125    fn is_cross_gadget(&self) -> bool {
126        true
127    }
128
129    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
130        let locs = vec![
131            (2, 1),
132            (2, 2),
133            (2, 3),
134            (2, 4),
135            (2, 5),
136            (1, 3),
137            (2, 3),
138            (3, 3),
139            (4, 3),
140        ];
141        let edges = vec![(0, 1), (1, 2), (2, 3), (3, 4), (5, 6), (6, 7), (7, 8)];
142        let pins = vec![0, 5, 8, 4];
143        (locs, edges, pins)
144    }
145
146    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
147        let locs = vec![
148            (2, 1),
149            (2, 2),
150            (2, 3),
151            (2, 4),
152            (2, 5),
153            (1, 3),
154            (3, 3),
155            (4, 3),
156            (3, 2),
157            (3, 4),
158        ];
159        let pins = vec![0, 5, 7, 4];
160        (locs, pins)
161    }
162
163    fn mis_overhead(&self) -> i32 {
164        -1
165    }
166
167    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
168        [
169            (5, 4),
170            (12, 4),
171            (8, 0),
172            (1, 0),
173            (0, 0),
174            (6, 0),
175            (11, 11),
176            (9, 9),
177            (14, 2),
178            (3, 2),
179            (7, 2),
180            (4, 4),
181            (13, 13),
182            (15, 11),
183            (2, 2),
184            (10, 2),
185        ]
186        .into_iter()
187        .collect()
188    }
189
190    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
191        let mut map = HashMap::new();
192        map.insert(
193            0,
194            vec![
195                vec![false, true, false, true, false, false, false, true, false],
196                vec![false, true, false, true, false, false, true, false, false],
197            ],
198        );
199        map.insert(
200            2,
201            vec![vec![
202                false, true, false, true, false, true, false, true, false,
203            ]],
204        );
205        map.insert(
206            4,
207            vec![vec![
208                false, true, false, true, false, false, true, false, true,
209            ]],
210        );
211        map.insert(
212            9,
213            vec![
214                vec![true, false, true, false, true, false, false, true, false],
215                vec![true, false, true, false, true, false, true, false, false],
216            ],
217        );
218        map.insert(
219            11,
220            vec![vec![
221                true, false, true, false, true, true, false, true, false,
222            ]],
223        );
224        map.insert(
225            13,
226            vec![vec![
227                true, false, true, false, true, false, true, false, true,
228            ]],
229        );
230        for i in [1, 3, 5, 6, 7, 8, 10, 12, 14, 15] {
231            map.entry(i).or_insert_with(Vec::new);
232        }
233        map
234    }
235}
236
237/// Turn gadget for 90-degree turns in copy-lines.
238#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
239pub struct KsgTurn;
240
241impl Pattern for KsgTurn {
242    fn size(&self) -> (usize, usize) {
243        (4, 4)
244    }
245    fn cross_location(&self) -> (usize, usize) {
246        (3, 2)
247    }
248    fn is_connected(&self) -> bool {
249        false
250    }
251
252    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
253        let locs = vec![(1, 2), (2, 2), (3, 2), (3, 3), (3, 4)];
254        let edges = vec![(0, 1), (1, 2), (2, 3), (3, 4)];
255        let pins = vec![0, 4];
256        (locs, edges, pins)
257    }
258
259    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
260        let locs = vec![(1, 2), (2, 3), (3, 4)];
261        let pins = vec![0, 2];
262        (locs, pins)
263    }
264
265    fn mis_overhead(&self) -> i32 {
266        -1
267    }
268
269    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
270        [(0, 0), (2, 0), (3, 3), (1, 0)].into_iter().collect()
271    }
272
273    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
274        let mut map = HashMap::new();
275        map.insert(0, vec![vec![false, true, false, true, false]]);
276        map.insert(
277            1,
278            vec![
279                vec![true, false, true, false, false],
280                vec![true, false, false, true, false],
281            ],
282        );
283        map.insert(
284            2,
285            vec![
286                vec![false, true, false, false, true],
287                vec![false, false, true, false, true],
288            ],
289        );
290        map.insert(3, vec![vec![true, false, true, false, true]]);
291        map
292    }
293}
294
295/// W-shaped turn gadget.
296#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
297pub struct KsgWTurn;
298
299impl Pattern for KsgWTurn {
300    fn size(&self) -> (usize, usize) {
301        (4, 4)
302    }
303    fn cross_location(&self) -> (usize, usize) {
304        (2, 2)
305    }
306    fn is_connected(&self) -> bool {
307        false
308    }
309
310    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
311        let locs = vec![(2, 3), (2, 4), (3, 2), (3, 3), (4, 2)];
312        let edges = vec![(0, 1), (0, 3), (2, 3), (2, 4)];
313        let pins = vec![1, 4];
314        (locs, edges, pins)
315    }
316
317    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
318        let locs = vec![(2, 4), (3, 3), (4, 2)];
319        let pins = vec![0, 2];
320        (locs, pins)
321    }
322
323    fn mis_overhead(&self) -> i32 {
324        -1
325    }
326
327    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
328        [(0, 0), (2, 0), (3, 3), (1, 0)].into_iter().collect()
329    }
330
331    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
332        let mut map = HashMap::new();
333        map.insert(0, vec![vec![true, false, true, false, false]]);
334        map.insert(
335            1,
336            vec![
337                vec![false, true, false, true, false],
338                vec![false, true, true, false, false],
339            ],
340        );
341        map.insert(
342            2,
343            vec![
344                vec![false, false, false, true, true],
345                vec![true, false, false, false, true],
346            ],
347        );
348        map.insert(3, vec![vec![false, true, false, true, true]]);
349        map
350    }
351}
352
353/// Branch gadget for T-junctions.
354#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
355pub struct KsgBranch;
356
357impl Pattern for KsgBranch {
358    fn size(&self) -> (usize, usize) {
359        (5, 4)
360    }
361    fn cross_location(&self) -> (usize, usize) {
362        (3, 2)
363    }
364    fn is_connected(&self) -> bool {
365        false
366    }
367
368    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
369        let locs = vec![
370            (1, 2),
371            (2, 2),
372            (3, 2),
373            (3, 3),
374            (3, 4),
375            (4, 3),
376            (4, 2),
377            (5, 2),
378        ];
379        let edges = vec![(0, 1), (1, 2), (2, 3), (3, 4), (3, 5), (5, 6), (6, 7)];
380        let pins = vec![0, 4, 7];
381        (locs, edges, pins)
382    }
383
384    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
385        let locs = vec![(1, 2), (2, 3), (3, 2), (3, 4), (4, 3), (5, 2)];
386        let pins = vec![0, 3, 5];
387        (locs, pins)
388    }
389
390    fn mis_overhead(&self) -> i32 {
391        -1
392    }
393
394    // Julia: sw[[4]] .= 3 (node 4 = 0-indexed 3 has weight 3)
395    fn source_weights(&self) -> Vec<i32> {
396        vec![2, 2, 2, 3, 2, 2, 2, 2]
397    }
398    // Julia: mw[[2]] .= 3 (mapped node 2 = 0-indexed 1 has weight 3)
399    fn mapped_weights(&self) -> Vec<i32> {
400        vec![2, 3, 2, 2, 2, 2]
401    }
402
403    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
404        [
405            (0, 0),
406            (4, 0),
407            (5, 5),
408            (6, 6),
409            (2, 0),
410            (7, 7),
411            (3, 3),
412            (1, 0),
413        ]
414        .into_iter()
415        .collect()
416    }
417
418    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
419        let mut map = HashMap::new();
420        map.insert(
421            0,
422            vec![vec![false, true, false, true, false, false, true, false]],
423        );
424        map.insert(
425            3,
426            vec![
427                vec![true, false, true, false, true, false, true, false],
428                vec![true, false, true, false, true, true, false, false],
429            ],
430        );
431        map.insert(
432            5,
433            vec![vec![true, false, true, false, false, true, false, true]],
434        );
435        map.insert(
436            6,
437            vec![
438                vec![false, false, true, false, true, true, false, true],
439                vec![false, true, false, false, true, true, false, true],
440            ],
441        );
442        map.insert(
443            7,
444            vec![vec![true, false, true, false, true, true, false, true]],
445        );
446        for i in [1, 2, 4] {
447            map.insert(i, vec![]);
448        }
449        map
450    }
451}
452
453/// Branch fix gadget for simplifying branches.
454#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
455pub struct KsgBranchFix;
456
457impl Pattern for KsgBranchFix {
458    fn size(&self) -> (usize, usize) {
459        (4, 4)
460    }
461    fn cross_location(&self) -> (usize, usize) {
462        (2, 2)
463    }
464    fn is_connected(&self) -> bool {
465        false
466    }
467
468    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
469        let locs = vec![(1, 2), (2, 2), (2, 3), (3, 3), (3, 2), (4, 2)];
470        let edges = vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)];
471        let pins = vec![0, 5];
472        (locs, edges, pins)
473    }
474
475    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
476        let locs = vec![(1, 2), (2, 2), (3, 2), (4, 2)];
477        let pins = vec![0, 3];
478        (locs, pins)
479    }
480
481    fn mis_overhead(&self) -> i32 {
482        -1
483    }
484
485    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
486        [(0, 0), (2, 2), (3, 1), (1, 1)].into_iter().collect()
487    }
488
489    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
490        let mut map = HashMap::new();
491        map.insert(
492            0,
493            vec![
494                vec![false, true, false, true, false, false],
495                vec![false, true, false, false, true, false],
496                vec![false, false, true, false, true, false],
497            ],
498        );
499        map.insert(1, vec![vec![true, false, true, false, true, false]]);
500        map.insert(2, vec![vec![false, true, false, true, false, true]]);
501        map.insert(
502            3,
503            vec![
504                vec![true, false, false, true, false, true],
505                vec![true, false, true, false, false, true],
506            ],
507        );
508        map
509    }
510}
511
512/// T-connection gadget.
513#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
514pub struct KsgTCon;
515
516impl Pattern for KsgTCon {
517    fn size(&self) -> (usize, usize) {
518        (3, 4)
519    }
520    fn cross_location(&self) -> (usize, usize) {
521        (2, 2)
522    }
523    fn is_connected(&self) -> bool {
524        true
525    }
526    fn connected_nodes(&self) -> Vec<usize> {
527        vec![0, 1]
528    }
529
530    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
531        let locs = vec![(1, 2), (2, 1), (2, 2), (3, 2)];
532        let edges = vec![(0, 1), (0, 2), (2, 3)];
533        let pins = vec![0, 1, 3];
534        (locs, edges, pins)
535    }
536
537    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
538        let locs = vec![(1, 2), (2, 1), (2, 3), (3, 2)];
539        let pins = vec![0, 1, 3];
540        (locs, pins)
541    }
542
543    fn mis_overhead(&self) -> i32 {
544        0
545    }
546
547    // Julia: sw[[2]] .= 1 (node 2 = 0-indexed 1 has weight 1)
548    fn source_weights(&self) -> Vec<i32> {
549        vec![2, 1, 2, 2]
550    }
551    // Julia: mw[[2]] .= 1 (mapped node 2 = 0-indexed 1 has weight 1)
552    fn mapped_weights(&self) -> Vec<i32> {
553        vec![2, 1, 2, 2]
554    }
555
556    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
557        [
558            (0, 0),
559            (4, 0),
560            (5, 5),
561            (6, 6),
562            (2, 2),
563            (7, 7),
564            (3, 3),
565            (1, 0),
566        ]
567        .into_iter()
568        .collect()
569    }
570
571    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
572        let mut map = HashMap::new();
573        map.insert(0, vec![vec![false, false, true, false]]);
574        map.insert(1, vec![vec![true, false, false, false]]);
575        map.insert(2, vec![vec![false, true, true, false]]);
576        map.insert(4, vec![vec![false, false, false, true]]);
577        map.insert(5, vec![vec![true, false, false, true]]);
578        map.insert(6, vec![vec![false, true, false, true]]);
579        map.insert(3, vec![]);
580        map.insert(7, vec![]);
581        map
582    }
583}
584
585/// Trivial turn gadget for simple diagonal turns.
586#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
587pub struct KsgTrivialTurn;
588
589impl Pattern for KsgTrivialTurn {
590    fn size(&self) -> (usize, usize) {
591        (2, 2)
592    }
593    fn cross_location(&self) -> (usize, usize) {
594        (2, 2)
595    }
596    fn is_connected(&self) -> bool {
597        true
598    }
599    fn connected_nodes(&self) -> Vec<usize> {
600        vec![0, 1]
601    }
602
603    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
604        let locs = vec![(1, 2), (2, 1)];
605        let edges = vec![(0, 1)];
606        let pins = vec![0, 1];
607        (locs, edges, pins)
608    }
609
610    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
611        let locs = vec![(1, 2), (2, 1)];
612        let pins = vec![0, 1];
613        (locs, pins)
614    }
615
616    fn mis_overhead(&self) -> i32 {
617        0
618    }
619
620    // Julia: sw[[1,2]] .= 1 (nodes 1,2 have weight 1)
621    fn source_weights(&self) -> Vec<i32> {
622        vec![1, 1]
623    }
624    // Julia: mw[[1,2]] .= 1 (mapped nodes 1,2 have weight 1)
625    fn mapped_weights(&self) -> Vec<i32> {
626        vec![1, 1]
627    }
628
629    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
630        [(0, 0), (2, 2), (3, 3), (1, 1)].into_iter().collect()
631    }
632
633    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
634        let mut map = HashMap::new();
635        map.insert(0, vec![vec![false, false]]);
636        map.insert(1, vec![vec![true, false]]);
637        map.insert(2, vec![vec![false, true]]);
638        map.insert(3, vec![]);
639        map
640    }
641}
642
643/// End turn gadget for line terminations.
644#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
645pub struct KsgEndTurn;
646
647impl Pattern for KsgEndTurn {
648    fn size(&self) -> (usize, usize) {
649        (3, 4)
650    }
651    fn cross_location(&self) -> (usize, usize) {
652        (2, 2)
653    }
654    fn is_connected(&self) -> bool {
655        false
656    }
657
658    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
659        let locs = vec![(1, 2), (2, 2), (2, 3)];
660        let edges = vec![(0, 1), (1, 2)];
661        let pins = vec![0];
662        (locs, edges, pins)
663    }
664
665    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
666        let locs = vec![(1, 2)];
667        let pins = vec![0];
668        (locs, pins)
669    }
670
671    fn mis_overhead(&self) -> i32 {
672        -1
673    }
674
675    // Julia: sw[[3]] .= 1 (node 3 = 0-indexed 2 has weight 1)
676    fn source_weights(&self) -> Vec<i32> {
677        vec![2, 2, 1]
678    }
679    // Julia: mw[[1]] .= 1 (mapped node 1 = 0-indexed 0 has weight 1)
680    fn mapped_weights(&self) -> Vec<i32> {
681        vec![1]
682    }
683
684    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
685        [(0, 0), (1, 1)].into_iter().collect()
686    }
687
688    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
689        let mut map = HashMap::new();
690        map.insert(0, vec![vec![false, false, true], vec![false, true, false]]);
691        map.insert(1, vec![vec![true, false, true]]);
692        map
693    }
694}
695
696/// Alternate branch fix gadget.
697#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
698pub struct KsgBranchFixB;
699
700impl Pattern for KsgBranchFixB {
701    fn size(&self) -> (usize, usize) {
702        (4, 4)
703    }
704    fn cross_location(&self) -> (usize, usize) {
705        (2, 2)
706    }
707    fn is_connected(&self) -> bool {
708        false
709    }
710
711    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
712        let locs = vec![(2, 3), (3, 2), (3, 3), (4, 2)];
713        let edges = vec![(0, 2), (1, 2), (1, 3)];
714        let pins = vec![0, 3];
715        (locs, edges, pins)
716    }
717
718    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
719        let locs = vec![(3, 2), (4, 2)];
720        let pins = vec![0, 1];
721        (locs, pins)
722    }
723
724    fn mis_overhead(&self) -> i32 {
725        -1
726    }
727
728    // Julia: sw[[1]] .= 1 (node 1 = 0-indexed 0 has weight 1)
729    fn source_weights(&self) -> Vec<i32> {
730        vec![1, 2, 2, 2]
731    }
732    // Julia: mw[[1]] .= 1 (mapped node 1 = 0-indexed 0 has weight 1)
733    fn mapped_weights(&self) -> Vec<i32> {
734        vec![1, 2]
735    }
736
737    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
738        [(0, 0), (2, 2), (3, 3), (1, 1)].into_iter().collect()
739    }
740
741    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
742        let mut map = HashMap::new();
743        map.insert(
744            0,
745            vec![
746                vec![false, false, true, false],
747                vec![false, true, false, false],
748            ],
749        );
750        map.insert(1, vec![vec![true, true, false, false]]);
751        map.insert(2, vec![vec![false, false, true, true]]);
752        map.insert(3, vec![vec![true, false, false, true]]);
753        map
754    }
755}
756
757// ============================================================================
758// Rotated and Reflected Gadgets
759// ============================================================================
760
761/// A rotated version of a gadget.
762#[derive(Debug, Clone)]
763pub struct KsgRotatedGadget<G: Pattern> {
764    pub gadget: G,
765    /// Number of 90-degree clockwise rotations (0-3).
766    pub n: usize,
767}
768
769impl<G: Pattern> KsgRotatedGadget<G> {
770    pub fn new(gadget: G, n: usize) -> Self {
771        Self { gadget, n: n % 4 }
772    }
773}
774
775fn rotate90(loc: (i32, i32)) -> (i32, i32) {
776    (-loc.1, loc.0)
777}
778
779fn rotate_around_center(loc: (usize, usize), center: (usize, usize), n: usize) -> (i32, i32) {
780    let mut dx = loc.0 as i32 - center.0 as i32;
781    let mut dy = loc.1 as i32 - center.1 as i32;
782    for _ in 0..n {
783        let (nx, ny) = rotate90((dx, dy));
784        dx = nx;
785        dy = ny;
786    }
787    (center.0 as i32 + dx, center.1 as i32 + dy)
788}
789
790impl<G: Pattern> Pattern for KsgRotatedGadget<G> {
791    fn size(&self) -> (usize, usize) {
792        let (m, n) = self.gadget.size();
793        if self.n.is_multiple_of(2) {
794            (m, n)
795        } else {
796            (n, m)
797        }
798    }
799
800    fn cross_location(&self) -> (usize, usize) {
801        let center = self.gadget.cross_location();
802        let (m, n) = self.gadget.size();
803        let rotated = rotate_around_center(center, center, self.n);
804        let corners = [(1, 1), (m, n)];
805        let rotated_corners: Vec<_> = corners
806            .iter()
807            .map(|&c| rotate_around_center(c, center, self.n))
808            .collect();
809        let min_r = rotated_corners.iter().map(|c| c.0).min().unwrap();
810        let min_c = rotated_corners.iter().map(|c| c.1).min().unwrap();
811        let offset_r = 1 - min_r;
812        let offset_c = 1 - min_c;
813        (
814            (rotated.0 + offset_r) as usize,
815            (rotated.1 + offset_c) as usize,
816        )
817    }
818
819    fn is_connected(&self) -> bool {
820        self.gadget.is_connected()
821    }
822    fn is_cross_gadget(&self) -> bool {
823        self.gadget.is_cross_gadget()
824    }
825    fn connected_nodes(&self) -> Vec<usize> {
826        self.gadget.connected_nodes()
827    }
828
829    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
830        let (locs, edges, pins) = self.gadget.source_graph();
831        let center = self.gadget.cross_location();
832        let (m, n) = self.gadget.size();
833        let corners = [(1usize, 1usize), (m, n)];
834        let rotated_corners: Vec<_> = corners
835            .iter()
836            .map(|&c| rotate_around_center(c, center, self.n))
837            .collect();
838        let min_r = rotated_corners.iter().map(|c| c.0).min().unwrap();
839        let min_c = rotated_corners.iter().map(|c| c.1).min().unwrap();
840        let offset_r = 1 - min_r;
841        let offset_c = 1 - min_c;
842        let new_locs: Vec<_> = locs
843            .into_iter()
844            .map(|loc| {
845                let rotated = rotate_around_center(loc, center, self.n);
846                (
847                    (rotated.0 + offset_r) as usize,
848                    (rotated.1 + offset_c) as usize,
849                )
850            })
851            .collect();
852        (new_locs, edges, pins)
853    }
854
855    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
856        let (locs, pins) = self.gadget.mapped_graph();
857        let center = self.gadget.cross_location();
858        let (m, n) = self.gadget.size();
859        let corners = [(1usize, 1usize), (m, n)];
860        let rotated_corners: Vec<_> = corners
861            .iter()
862            .map(|&c| rotate_around_center(c, center, self.n))
863            .collect();
864        let min_r = rotated_corners.iter().map(|c| c.0).min().unwrap();
865        let min_c = rotated_corners.iter().map(|c| c.1).min().unwrap();
866        let offset_r = 1 - min_r;
867        let offset_c = 1 - min_c;
868        let new_locs: Vec<_> = locs
869            .into_iter()
870            .map(|loc| {
871                let rotated = rotate_around_center(loc, center, self.n);
872                (
873                    (rotated.0 + offset_r) as usize,
874                    (rotated.1 + offset_c) as usize,
875                )
876            })
877            .collect();
878        (new_locs, pins)
879    }
880
881    fn mis_overhead(&self) -> i32 {
882        self.gadget.mis_overhead()
883    }
884    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
885        self.gadget.mapped_entry_to_compact()
886    }
887    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
888        self.gadget.source_entry_to_configs()
889    }
890
891    // Weights don't change with rotation - delegate to inner gadget
892    fn source_weights(&self) -> Vec<i32> {
893        self.gadget.source_weights()
894    }
895    fn mapped_weights(&self) -> Vec<i32> {
896        self.gadget.mapped_weights()
897    }
898}
899
900/// Mirror axis for reflection.
901#[derive(Debug, Clone, Copy, PartialEq, Eq)]
902pub enum Mirror {
903    X,
904    Y,
905    Diag,
906    OffDiag,
907}
908
909/// A reflected version of a gadget.
910#[derive(Debug, Clone)]
911pub struct KsgReflectedGadget<G: Pattern> {
912    pub gadget: G,
913    pub mirror: Mirror,
914}
915
916impl<G: Pattern> KsgReflectedGadget<G> {
917    pub fn new(gadget: G, mirror: Mirror) -> Self {
918        Self { gadget, mirror }
919    }
920}
921
922fn reflect(loc: (i32, i32), mirror: Mirror) -> (i32, i32) {
923    match mirror {
924        Mirror::X => (loc.0, -loc.1),
925        Mirror::Y => (-loc.0, loc.1),
926        Mirror::Diag => (-loc.1, -loc.0),
927        Mirror::OffDiag => (loc.1, loc.0),
928    }
929}
930
931fn reflect_around_center(
932    loc: (usize, usize),
933    center: (usize, usize),
934    mirror: Mirror,
935) -> (i32, i32) {
936    let dx = loc.0 as i32 - center.0 as i32;
937    let dy = loc.1 as i32 - center.1 as i32;
938    let (nx, ny) = reflect((dx, dy), mirror);
939    (center.0 as i32 + nx, center.1 as i32 + ny)
940}
941
942impl<G: Pattern> Pattern for KsgReflectedGadget<G> {
943    fn size(&self) -> (usize, usize) {
944        let (m, n) = self.gadget.size();
945        match self.mirror {
946            Mirror::X | Mirror::Y => (m, n),
947            Mirror::Diag | Mirror::OffDiag => (n, m),
948        }
949    }
950
951    fn cross_location(&self) -> (usize, usize) {
952        let center = self.gadget.cross_location();
953        let (m, n) = self.gadget.size();
954        let reflected = reflect_around_center(center, center, self.mirror);
955        let corners = [(1, 1), (m, n)];
956        let reflected_corners: Vec<_> = corners
957            .iter()
958            .map(|&c| reflect_around_center(c, center, self.mirror))
959            .collect();
960        let min_r = reflected_corners.iter().map(|c| c.0).min().unwrap();
961        let min_c = reflected_corners.iter().map(|c| c.1).min().unwrap();
962        let offset_r = 1 - min_r;
963        let offset_c = 1 - min_c;
964        (
965            (reflected.0 + offset_r) as usize,
966            (reflected.1 + offset_c) as usize,
967        )
968    }
969
970    fn is_connected(&self) -> bool {
971        self.gadget.is_connected()
972    }
973    fn is_cross_gadget(&self) -> bool {
974        self.gadget.is_cross_gadget()
975    }
976    fn connected_nodes(&self) -> Vec<usize> {
977        self.gadget.connected_nodes()
978    }
979
980    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
981        let (locs, edges, pins) = self.gadget.source_graph();
982        let center = self.gadget.cross_location();
983        let (m, n) = self.gadget.size();
984        let corners = [(1usize, 1usize), (m, n)];
985        let reflected_corners: Vec<_> = corners
986            .iter()
987            .map(|&c| reflect_around_center(c, center, self.mirror))
988            .collect();
989        let min_r = reflected_corners.iter().map(|c| c.0).min().unwrap();
990        let min_c = reflected_corners.iter().map(|c| c.1).min().unwrap();
991        let offset_r = 1 - min_r;
992        let offset_c = 1 - min_c;
993        let new_locs: Vec<_> = locs
994            .into_iter()
995            .map(|loc| {
996                let reflected = reflect_around_center(loc, center, self.mirror);
997                (
998                    (reflected.0 + offset_r) as usize,
999                    (reflected.1 + offset_c) as usize,
1000                )
1001            })
1002            .collect();
1003        (new_locs, edges, pins)
1004    }
1005
1006    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
1007        let (locs, pins) = self.gadget.mapped_graph();
1008        let center = self.gadget.cross_location();
1009        let (m, n) = self.gadget.size();
1010        let corners = [(1usize, 1usize), (m, n)];
1011        let reflected_corners: Vec<_> = corners
1012            .iter()
1013            .map(|&c| reflect_around_center(c, center, self.mirror))
1014            .collect();
1015        let min_r = reflected_corners.iter().map(|c| c.0).min().unwrap();
1016        let min_c = reflected_corners.iter().map(|c| c.1).min().unwrap();
1017        let offset_r = 1 - min_r;
1018        let offset_c = 1 - min_c;
1019        let new_locs: Vec<_> = locs
1020            .into_iter()
1021            .map(|loc| {
1022                let reflected = reflect_around_center(loc, center, self.mirror);
1023                (
1024                    (reflected.0 + offset_r) as usize,
1025                    (reflected.1 + offset_c) as usize,
1026                )
1027            })
1028            .collect();
1029        (new_locs, pins)
1030    }
1031
1032    fn mis_overhead(&self) -> i32 {
1033        self.gadget.mis_overhead()
1034    }
1035    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
1036        self.gadget.mapped_entry_to_compact()
1037    }
1038    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
1039        self.gadget.source_entry_to_configs()
1040    }
1041
1042    // Weights don't change with reflection - delegate to inner gadget
1043    fn source_weights(&self) -> Vec<i32> {
1044        self.gadget.source_weights()
1045    }
1046    fn mapped_weights(&self) -> Vec<i32> {
1047        self.gadget.mapped_weights()
1048    }
1049}
1050
1051// ============================================================================
1052// Simplifier Patterns
1053// ============================================================================
1054
1055/// Dangling leg simplifier pattern.
1056///
1057/// Julia pattern:
1058/// ```text
1059/// Source:       Mapped:
1060/// . . .         . . .
1061/// . X .    =>   . . .
1062/// . X .         . . .
1063/// . X .         . X .
1064/// ```
1065/// Removes 2 nodes from a dangling chain, keeping only the endpoint.
1066#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1067pub struct KsgDanglingLeg;
1068
1069impl Pattern for KsgDanglingLeg {
1070    fn size(&self) -> (usize, usize) {
1071        (4, 3)
1072    }
1073    // Julia: cross_location = size ./ 2 = (4/2, 3/2) = (2, 1)
1074    fn cross_location(&self) -> (usize, usize) {
1075        (2, 1)
1076    }
1077    fn is_connected(&self) -> bool {
1078        false
1079    }
1080
1081    fn source_graph(&self) -> (Vec<(usize, usize)>, Vec<(usize, usize)>, Vec<usize>) {
1082        // Julia: 3 nodes at (2,2), (3,2), (4,2) - vertical chain in column 2
1083        let locs = vec![(2, 2), (3, 2), (4, 2)];
1084        let edges = vec![(0, 1), (1, 2)];
1085        // Boundary node: only (4,2) is on boundary (row 4 = m for 4x3 pattern)
1086        let pins = vec![2];
1087        (locs, edges, pins)
1088    }
1089
1090    fn mapped_graph(&self) -> (Vec<(usize, usize)>, Vec<usize>) {
1091        // Julia: 1 node at (4,2) - the bottom endpoint
1092        let locs = vec![(4, 2)];
1093        let pins = vec![0];
1094        (locs, pins)
1095    }
1096
1097    fn mis_overhead(&self) -> i32 {
1098        -1
1099    }
1100
1101    // Julia: sw[[1]] .= 1 (node 1 = 0-indexed 0 has weight 1)
1102    fn source_weights(&self) -> Vec<i32> {
1103        vec![1, 2, 2]
1104    }
1105    // Julia: mw[[1]] .= 1 (mapped node 1 = 0-indexed 0 has weight 1)
1106    fn mapped_weights(&self) -> Vec<i32> {
1107        vec![1]
1108    }
1109
1110    fn mapped_entry_to_compact(&self) -> HashMap<usize, usize> {
1111        // Julia: Dict([0 => 0, 1 => 1])
1112        [(0, 0), (1, 1)].into_iter().collect()
1113    }
1114
1115    fn source_entry_to_configs(&self) -> HashMap<usize, Vec<Vec<bool>>> {
1116        // Julia: 0 => [[1,0,0], [0,1,0]], 1 => [[1,0,1]]
1117        // Entry 0 (mapped node not selected): select node 0 OR node 1
1118        // Entry 1 (mapped node selected): select nodes 0 and 2
1119        let mut map = HashMap::new();
1120        map.insert(0, vec![vec![true, false, false], vec![false, true, false]]);
1121        map.insert(1, vec![vec![true, false, true]]);
1122        map
1123    }
1124}
1125
1126// ============================================================================
1127// KsgPattern Enum for Dynamic Dispatch
1128// ============================================================================
1129
1130/// Enum wrapping all KSG square lattice patterns for dynamic dispatch during unapply.
1131#[derive(Debug, Clone)]
1132pub enum KsgPattern {
1133    CrossFalse(KsgCross<false>),
1134    CrossTrue(KsgCross<true>),
1135    Turn(KsgTurn),
1136    WTurn(KsgWTurn),
1137    Branch(KsgBranch),
1138    BranchFix(KsgBranchFix),
1139    TCon(KsgTCon),
1140    TrivialTurn(KsgTrivialTurn),
1141    EndTurn(KsgEndTurn),
1142    BranchFixB(KsgBranchFixB),
1143    DanglingLeg(KsgDanglingLeg),
1144    RotatedTCon1(KsgRotatedGadget<KsgTCon>),
1145    ReflectedCrossTrue(KsgReflectedGadget<KsgCross<true>>),
1146    ReflectedTrivialTurn(KsgReflectedGadget<KsgTrivialTurn>),
1147    ReflectedRotatedTCon1(KsgReflectedGadget<KsgRotatedGadget<KsgTCon>>),
1148    DanglingLegRot1(KsgRotatedGadget<KsgDanglingLeg>),
1149    DanglingLegRot2(KsgRotatedGadget<KsgRotatedGadget<KsgDanglingLeg>>),
1150    DanglingLegRot3(KsgRotatedGadget<KsgRotatedGadget<KsgRotatedGadget<KsgDanglingLeg>>>),
1151    DanglingLegReflX(KsgReflectedGadget<KsgDanglingLeg>),
1152    DanglingLegReflY(KsgReflectedGadget<KsgDanglingLeg>),
1153}
1154
1155impl KsgPattern {
1156    /// Get pattern from tape index.
1157    pub fn from_tape_idx(idx: usize) -> Option<Self> {
1158        match idx {
1159            0 => Some(Self::CrossFalse(KsgCross::<false>)),
1160            1 => Some(Self::Turn(KsgTurn)),
1161            2 => Some(Self::WTurn(KsgWTurn)),
1162            3 => Some(Self::Branch(KsgBranch)),
1163            4 => Some(Self::BranchFix(KsgBranchFix)),
1164            5 => Some(Self::TCon(KsgTCon)),
1165            6 => Some(Self::TrivialTurn(KsgTrivialTurn)),
1166            7 => Some(Self::RotatedTCon1(KsgRotatedGadget::new(KsgTCon, 1))),
1167            8 => Some(Self::ReflectedCrossTrue(KsgReflectedGadget::new(
1168                KsgCross::<true>,
1169                Mirror::Y,
1170            ))),
1171            9 => Some(Self::ReflectedTrivialTurn(KsgReflectedGadget::new(
1172                KsgTrivialTurn,
1173                Mirror::Y,
1174            ))),
1175            10 => Some(Self::BranchFixB(KsgBranchFixB)),
1176            11 => Some(Self::EndTurn(KsgEndTurn)),
1177            12 => Some(Self::ReflectedRotatedTCon1(KsgReflectedGadget::new(
1178                KsgRotatedGadget::new(KsgTCon, 1),
1179                Mirror::Y,
1180            ))),
1181            100 => Some(Self::DanglingLeg(KsgDanglingLeg)),
1182            101 => Some(Self::DanglingLegRot1(KsgRotatedGadget::new(
1183                KsgDanglingLeg,
1184                1,
1185            ))),
1186            102 => Some(Self::DanglingLegRot2(KsgRotatedGadget::new(
1187                KsgRotatedGadget::new(KsgDanglingLeg, 1),
1188                1,
1189            ))),
1190            103 => Some(Self::DanglingLegRot3(KsgRotatedGadget::new(
1191                KsgRotatedGadget::new(KsgRotatedGadget::new(KsgDanglingLeg, 1), 1),
1192                1,
1193            ))),
1194            104 => Some(Self::DanglingLegReflX(KsgReflectedGadget::new(
1195                KsgDanglingLeg,
1196                Mirror::X,
1197            ))),
1198            105 => Some(Self::DanglingLegReflY(KsgReflectedGadget::new(
1199                KsgDanglingLeg,
1200                Mirror::Y,
1201            ))),
1202            _ => None,
1203        }
1204    }
1205
1206    /// Apply map_config_back_pattern for this pattern.
1207    pub fn map_config_back(&self, gi: usize, gj: usize, config: &mut [Vec<usize>]) {
1208        match self {
1209            Self::CrossFalse(p) => map_config_back_pattern(p, gi, gj, config),
1210            Self::CrossTrue(p) => map_config_back_pattern(p, gi, gj, config),
1211            Self::Turn(p) => map_config_back_pattern(p, gi, gj, config),
1212            Self::WTurn(p) => map_config_back_pattern(p, gi, gj, config),
1213            Self::Branch(p) => map_config_back_pattern(p, gi, gj, config),
1214            Self::BranchFix(p) => map_config_back_pattern(p, gi, gj, config),
1215            Self::TCon(p) => map_config_back_pattern(p, gi, gj, config),
1216            Self::TrivialTurn(p) => map_config_back_pattern(p, gi, gj, config),
1217            Self::EndTurn(p) => map_config_back_pattern(p, gi, gj, config),
1218            Self::BranchFixB(p) => map_config_back_pattern(p, gi, gj, config),
1219            Self::DanglingLeg(p) => map_config_back_pattern(p, gi, gj, config),
1220            Self::RotatedTCon1(p) => map_config_back_pattern(p, gi, gj, config),
1221            Self::ReflectedCrossTrue(p) => map_config_back_pattern(p, gi, gj, config),
1222            Self::ReflectedTrivialTurn(p) => map_config_back_pattern(p, gi, gj, config),
1223            Self::ReflectedRotatedTCon1(p) => map_config_back_pattern(p, gi, gj, config),
1224            Self::DanglingLegRot1(p) => map_config_back_pattern(p, gi, gj, config),
1225            Self::DanglingLegRot2(p) => map_config_back_pattern(p, gi, gj, config),
1226            Self::DanglingLegRot3(p) => map_config_back_pattern(p, gi, gj, config),
1227            Self::DanglingLegReflX(p) => map_config_back_pattern(p, gi, gj, config),
1228            Self::DanglingLegReflY(p) => map_config_back_pattern(p, gi, gj, config),
1229        }
1230    }
1231}
1232
1233// ============================================================================
1234// Crossing ruleset and apply functions
1235// ============================================================================
1236
1237/// A tape entry recording a gadget application.
1238#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1239pub struct KsgTapeEntry {
1240    pub pattern_idx: usize,
1241    pub row: usize,
1242    pub col: usize,
1243}
1244
1245/// Calculate MIS overhead for a tape entry.
1246pub fn tape_entry_mis_overhead(entry: &KsgTapeEntry) -> i32 {
1247    match entry.pattern_idx {
1248        0 => KsgCross::<false>.mis_overhead(),
1249        1 => KsgTurn.mis_overhead(),
1250        2 => KsgWTurn.mis_overhead(),
1251        3 => KsgBranch.mis_overhead(),
1252        4 => KsgBranchFix.mis_overhead(),
1253        5 => KsgTCon.mis_overhead(),
1254        6 => KsgTrivialTurn.mis_overhead(),
1255        7 => KsgRotatedGadget::new(KsgTCon, 1).mis_overhead(),
1256        8 => KsgReflectedGadget::new(KsgCross::<true>, Mirror::Y).mis_overhead(),
1257        9 => KsgReflectedGadget::new(KsgTrivialTurn, Mirror::Y).mis_overhead(),
1258        10 => KsgBranchFixB.mis_overhead(),
1259        11 => KsgEndTurn.mis_overhead(),
1260        12 => KsgReflectedGadget::new(KsgRotatedGadget::new(KsgTCon, 1), Mirror::Y).mis_overhead(),
1261        100..=105 => KsgDanglingLeg.mis_overhead(),
1262        _ => 0,
1263    }
1264}
1265
1266/// The default crossing ruleset for KSG square lattice.
1267#[allow(dead_code)]
1268pub fn crossing_ruleset_indices() -> Vec<usize> {
1269    (0..13).collect()
1270}
1271
1272/// Apply all crossing gadgets to the grid.
1273/// Follows Julia's algorithm: iterate over all (i,j) pairs and try all patterns.
1274/// Note: Unlike the previous version, we don't skip based on crossat position
1275/// because different (i,j) pairs with the same crossat can match different patterns
1276/// at different positions (since each pattern has a different cross_location).
1277pub fn apply_crossing_gadgets(
1278    grid: &mut MappingGrid,
1279    copylines: &[super::super::copyline::CopyLine],
1280) -> Vec<KsgTapeEntry> {
1281    let mut tape = Vec::new();
1282    let n = copylines.len();
1283
1284    for j in 0..n {
1285        for i in 0..n {
1286            let (cross_row, cross_col) = crossat(grid, copylines, i, j);
1287            if let Some((pattern_idx, row, col)) =
1288                try_match_and_apply_crossing(grid, cross_row, cross_col)
1289            {
1290                tape.push(KsgTapeEntry {
1291                    pattern_idx,
1292                    row,
1293                    col,
1294                });
1295            }
1296        }
1297    }
1298    tape
1299}
1300
1301/// Calculate crossing point for two copylines.
1302/// Uses grid.cross_at() which implements Julia's crossat formula.
1303fn crossat(
1304    grid: &MappingGrid,
1305    copylines: &[super::super::copyline::CopyLine],
1306    v: usize,
1307    w: usize,
1308) -> (usize, usize) {
1309    let line_v = copylines.get(v);
1310    let line_w = copylines.get(w);
1311
1312    match (line_v, line_w) {
1313        (Some(lv), Some(lw)) => {
1314            let (line_first, line_second) = if lv.vslot < lw.vslot {
1315                (lv, lw)
1316            } else {
1317                (lw, lv)
1318            };
1319            // Delegate to grid.cross_at() - single source of truth for crossat formula
1320            grid.cross_at(line_first.vslot, line_second.vslot, line_first.hslot)
1321        }
1322        _ => (0, 0),
1323    }
1324}
1325
1326fn try_match_and_apply_crossing(
1327    grid: &mut MappingGrid,
1328    cross_row: usize,
1329    cross_col: usize,
1330) -> Option<(usize, usize, usize)> {
1331    // Try each pattern in order
1332    let patterns: Vec<(usize, PatternFactory)> = vec![
1333        (0, Box::new(|| Box::new(KsgCross::<false>))),
1334        (1, Box::new(|| Box::new(KsgTurn))),
1335        (2, Box::new(|| Box::new(KsgWTurn))),
1336        (3, Box::new(|| Box::new(KsgBranch))),
1337        (4, Box::new(|| Box::new(KsgBranchFix))),
1338        (5, Box::new(|| Box::new(KsgTCon))),
1339        (6, Box::new(|| Box::new(KsgTrivialTurn))),
1340        (7, Box::new(|| Box::new(KsgRotatedGadget::new(KsgTCon, 1)))),
1341        (
1342            8,
1343            Box::new(|| Box::new(KsgReflectedGadget::new(KsgCross::<true>, Mirror::Y))),
1344        ),
1345        (
1346            9,
1347            Box::new(|| Box::new(KsgReflectedGadget::new(KsgTrivialTurn, Mirror::Y))),
1348        ),
1349        (10, Box::new(|| Box::new(KsgBranchFixB))),
1350        (11, Box::new(|| Box::new(KsgEndTurn))),
1351        (
1352            12,
1353            Box::new(|| {
1354                Box::new(KsgReflectedGadget::new(
1355                    KsgRotatedGadget::new(KsgTCon, 1),
1356                    Mirror::Y,
1357                ))
1358            }),
1359        ),
1360    ];
1361
1362    for (idx, make_pattern) in patterns {
1363        let pattern = make_pattern();
1364        let cl = pattern.cross_location();
1365        // cross_row/cross_col are 0-indexed, cl is 1-indexed within gadget
1366        // x = cross_row - (cl.0 - 1) = cross_row + 1 - cl.0, needs x >= 0
1367        if cross_row + 1 >= cl.0 && cross_col + 1 >= cl.1 {
1368            let x = cross_row + 1 - cl.0;
1369            let y = cross_col + 1 - cl.1;
1370            if pattern.pattern_matches_boxed(grid, x, y) {
1371                pattern.apply_gadget_boxed(grid, x, y);
1372                return Some((idx, x, y));
1373            }
1374        }
1375    }
1376    None
1377}
1378
1379/// Apply crossing gadgets with proper weights for weighted mode.
1380/// Uses apply_weighted_gadget which respects mapped_weights() for each gadget.
1381pub fn apply_weighted_crossing_gadgets(
1382    grid: &mut MappingGrid,
1383    copylines: &[super::super::copyline::CopyLine],
1384) -> Vec<KsgTapeEntry> {
1385    let mut tape = Vec::new();
1386    let n = copylines.len();
1387
1388    for j in 0..n {
1389        for i in 0..n {
1390            let (cross_row, cross_col) = crossat(grid, copylines, i, j);
1391            if let Some((pattern_idx, row, col)) =
1392                try_match_and_apply_weighted_crossing(grid, cross_row, cross_col)
1393            {
1394                tape.push(KsgTapeEntry {
1395                    pattern_idx,
1396                    row,
1397                    col,
1398                });
1399            }
1400        }
1401    }
1402    tape
1403}
1404
1405fn try_match_and_apply_weighted_crossing(
1406    grid: &mut MappingGrid,
1407    cross_row: usize,
1408    cross_col: usize,
1409) -> Option<(usize, usize, usize)> {
1410    // Try each pattern in order - same order as try_match_and_apply_crossing
1411    let patterns: Vec<(usize, PatternFactory)> = vec![
1412        (0, Box::new(|| Box::new(KsgCross::<false>))),
1413        (1, Box::new(|| Box::new(KsgTurn))),
1414        (2, Box::new(|| Box::new(KsgWTurn))),
1415        (3, Box::new(|| Box::new(KsgBranch))),
1416        (4, Box::new(|| Box::new(KsgBranchFix))),
1417        (5, Box::new(|| Box::new(KsgTCon))),
1418        (6, Box::new(|| Box::new(KsgTrivialTurn))),
1419        (7, Box::new(|| Box::new(KsgRotatedGadget::new(KsgTCon, 1)))),
1420        (
1421            8,
1422            Box::new(|| Box::new(KsgReflectedGadget::new(KsgCross::<true>, Mirror::Y))),
1423        ),
1424        (
1425            9,
1426            Box::new(|| Box::new(KsgReflectedGadget::new(KsgTrivialTurn, Mirror::Y))),
1427        ),
1428        (10, Box::new(|| Box::new(KsgBranchFixB))),
1429        (11, Box::new(|| Box::new(KsgEndTurn))),
1430        (
1431            12,
1432            Box::new(|| {
1433                Box::new(KsgReflectedGadget::new(
1434                    KsgRotatedGadget::new(KsgTCon, 1),
1435                    Mirror::Y,
1436                ))
1437            }),
1438        ),
1439    ];
1440
1441    for (idx, make_pattern) in patterns {
1442        let pattern = make_pattern();
1443        let cl = pattern.cross_location();
1444        if cross_row + 1 >= cl.0 && cross_col + 1 >= cl.1 {
1445            let x = cross_row + 1 - cl.0;
1446            let y = cross_col + 1 - cl.1;
1447            let matches = pattern.pattern_matches_boxed(grid, x, y);
1448            if matches {
1449                pattern.apply_weighted_gadget_boxed(grid, x, y);
1450                return Some((idx, x, y));
1451            }
1452        }
1453    }
1454    None
1455}
1456
1457/// Apply simplifier gadgets (KsgDanglingLeg variants).
1458/// `nrepeat` specifies the number of simplification passes.
1459pub fn apply_simplifier_gadgets(grid: &mut MappingGrid, nrepeat: usize) -> Vec<KsgTapeEntry> {
1460    let mut tape = Vec::new();
1461    let (rows, cols) = grid.size();
1462
1463    // Get all rotations and reflections of KsgDanglingLeg
1464    let patterns = rotated_and_reflected_danglinleg();
1465
1466    for _ in 0..nrepeat {
1467        for (pattern_idx, pattern) in patterns.iter().enumerate() {
1468            for j in 0..cols {
1469                for i in 0..rows {
1470                    if pattern_matches_boxed(pattern.as_ref(), grid, i, j) {
1471                        apply_gadget_boxed(pattern.as_ref(), grid, i, j);
1472                        tape.push(KsgTapeEntry {
1473                            pattern_idx: 100 + pattern_idx, // Offset to distinguish from crossing gadgets
1474                            row: i,
1475                            col: j,
1476                        });
1477                    }
1478                }
1479            }
1480        }
1481    }
1482
1483    tape
1484}
1485
1486/// Apply weighted simplifier gadgets (KsgDanglingLeg variants with weight checking).
1487/// For weighted mode, KsgDanglingLeg requires the center node to have weight 1.
1488/// Julia's WeightedGadget{DanglingLeg}: source_centers = [(2,2)] means node at (2,2) has weight 1.
1489pub fn apply_weighted_simplifier_gadgets(
1490    grid: &mut MappingGrid,
1491    nrepeat: usize,
1492) -> Vec<KsgTapeEntry> {
1493    let mut tape = Vec::new();
1494    let (rows, cols) = grid.size();
1495
1496    let patterns = rotated_and_reflected_danglinleg();
1497
1498    for _ in 0..nrepeat {
1499        for (pattern_idx, pattern) in patterns.iter().enumerate() {
1500            for j in 0..cols {
1501                for i in 0..rows {
1502                    if pattern_matches_weighted(pattern.as_ref(), grid, i, j) {
1503                        pattern.apply_weighted_gadget_boxed(grid, i, j);
1504                        tape.push(KsgTapeEntry {
1505                            pattern_idx: 100 + pattern_idx,
1506                            row: i,
1507                            col: j,
1508                        });
1509                    }
1510                }
1511            }
1512        }
1513    }
1514
1515    tape
1516}
1517
1518/// Check if a weighted KsgDanglingLeg pattern matches.
1519/// For weighted mode, the center node (at source_centers position) must have weight 1,
1520/// and other nodes must have weight 2.
1521fn pattern_matches_weighted(
1522    pattern: &dyn KsgPatternBoxed,
1523    grid: &MappingGrid,
1524    i: usize,
1525    j: usize,
1526) -> bool {
1527    // First check basic pattern match
1528    if !pattern_matches_boxed(pattern, grid, i, j) {
1529        return false;
1530    }
1531
1532    // For weighted KsgDanglingLeg, check that the center node has weight 1
1533    // KsgDanglingLeg source_centers = [(2,2)] (1-indexed), which is (1,1) 0-indexed in 4x3 pattern
1534    // After rotation/reflection, the center position changes
1535    let (locs, _, _) = pattern.source_graph_boxed();
1536    // The first node in source_graph is at (2,2), which should have weight 1
1537    // Node positions in source_graph are 1-indexed, convert to 0-indexed and add to (i,j)
1538    if let Some((loc_r, loc_c)) = locs.first() {
1539        let grid_r = i + loc_r - 1;
1540        let grid_c = j + loc_c - 1;
1541        if let Some(cell) = grid.get(grid_r, grid_c) {
1542            // Center node must have weight 1
1543            if cell.weight() != 1 {
1544                return false;
1545            }
1546        }
1547    }
1548
1549    // Check other nodes have weight 2
1550    for (_idx, (loc_r, loc_c)) in locs.iter().enumerate().skip(1) {
1551        let grid_r = i + loc_r - 1;
1552        let grid_c = j + loc_c - 1;
1553        if let Some(cell) = grid.get(grid_r, grid_c) {
1554            if cell.weight() != 2 {
1555                return false;
1556            }
1557        }
1558    }
1559
1560    true
1561}
1562
1563fn rotated_and_reflected_danglinleg() -> Vec<Box<dyn KsgPatternBoxed>> {
1564    vec![
1565        Box::new(KsgDanglingLeg),
1566        Box::new(KsgRotatedGadget::new(KsgDanglingLeg, 1)),
1567        Box::new(KsgRotatedGadget::new(KsgDanglingLeg, 2)),
1568        Box::new(KsgRotatedGadget::new(KsgDanglingLeg, 3)),
1569        Box::new(KsgReflectedGadget::new(KsgDanglingLeg, Mirror::X)),
1570        Box::new(KsgReflectedGadget::new(KsgDanglingLeg, Mirror::Y)),
1571    ]
1572}
1573
1574/// Check if a boxed pattern matches at position (i, j) in the grid.
1575#[allow(clippy::needless_range_loop)]
1576fn pattern_matches_boxed(
1577    pattern: &dyn KsgPatternBoxed,
1578    grid: &MappingGrid,
1579    i: usize,
1580    j: usize,
1581) -> bool {
1582    let source = pattern.source_matrix();
1583    let (m, n) = pattern.size_boxed();
1584
1585    for r in 0..m {
1586        for c in 0..n {
1587            let grid_r = i + r;
1588            let grid_c = j + c;
1589
1590            let expected = source[r][c];
1591            let actual = safe_get_pattern_cell(grid, grid_r, grid_c);
1592
1593            // Connected cells in pattern match both Connected and Occupied in grid
1594            // (Connected is just a marker for edge connection points)
1595            let matches = match (expected, actual) {
1596                (a, b) if a == b => true,
1597                (PatternCell::Connected, PatternCell::Occupied) => true,
1598                (PatternCell::Occupied, PatternCell::Connected) => true,
1599                _ => false,
1600            };
1601            if !matches {
1602                return false;
1603            }
1604        }
1605    }
1606    true
1607}
1608
1609fn safe_get_pattern_cell(grid: &MappingGrid, row: usize, col: usize) -> PatternCell {
1610    let (rows, cols) = grid.size();
1611    if row >= rows || col >= cols {
1612        return PatternCell::Empty;
1613    }
1614    match grid.get(row, col) {
1615        Some(CellState::Empty) => PatternCell::Empty,
1616        Some(CellState::Occupied { .. }) => PatternCell::Occupied,
1617        Some(CellState::Doubled { .. }) => PatternCell::Doubled,
1618        Some(CellState::Connected { .. }) => PatternCell::Connected,
1619        None => PatternCell::Empty,
1620    }
1621}
1622
1623/// Apply a boxed gadget pattern at position (i, j).
1624#[allow(clippy::needless_range_loop)]
1625fn apply_gadget_boxed(pattern: &dyn KsgPatternBoxed, grid: &mut MappingGrid, i: usize, j: usize) {
1626    let mapped = pattern.mapped_matrix();
1627    let (m, n) = pattern.size_boxed();
1628
1629    for r in 0..m {
1630        for c in 0..n {
1631            let grid_r = i + r;
1632            let grid_c = j + c;
1633
1634            let cell = mapped[r][c];
1635            let state = match cell {
1636                PatternCell::Empty => CellState::Empty,
1637                PatternCell::Occupied => CellState::Occupied { weight: 1 },
1638                PatternCell::Doubled => CellState::Doubled { weight: 1 },
1639                PatternCell::Connected => CellState::Connected { weight: 1 },
1640            };
1641            grid.set(grid_r, grid_c, state);
1642        }
1643    }
1644}
1645
1646/// Apply a boxed gadget pattern at position (i, j) with proper weights.
1647#[allow(dead_code)]
1648fn apply_weighted_gadget_boxed_fn(
1649    pattern: &dyn KsgPatternBoxed,
1650    grid: &mut MappingGrid,
1651    i: usize,
1652    j: usize,
1653) {
1654    pattern.apply_weighted_gadget_boxed(grid, i, j);
1655}
1656
1657/// Trait for boxed pattern operations.
1658pub trait KsgPatternBoxed {
1659    fn size_boxed(&self) -> (usize, usize);
1660    fn cross_location(&self) -> (usize, usize);
1661    fn source_matrix(&self) -> Vec<Vec<PatternCell>>;
1662    fn mapped_matrix(&self) -> Vec<Vec<PatternCell>>;
1663    fn source_graph_boxed(&self) -> SourceGraph;
1664    fn pattern_matches_boxed(&self, grid: &MappingGrid, i: usize, j: usize) -> bool;
1665    fn apply_gadget_boxed(&self, grid: &mut MappingGrid, i: usize, j: usize);
1666    fn apply_weighted_gadget_boxed(&self, grid: &mut MappingGrid, i: usize, j: usize);
1667}
1668
1669impl<P: Pattern> KsgPatternBoxed for P {
1670    fn size_boxed(&self) -> (usize, usize) {
1671        self.size()
1672    }
1673    fn cross_location(&self) -> (usize, usize) {
1674        Pattern::cross_location(self)
1675    }
1676    fn source_matrix(&self) -> Vec<Vec<PatternCell>> {
1677        Pattern::source_matrix(self)
1678    }
1679    fn mapped_matrix(&self) -> Vec<Vec<PatternCell>> {
1680        Pattern::mapped_matrix(self)
1681    }
1682    fn source_graph_boxed(&self) -> SourceGraph {
1683        Pattern::source_graph(self)
1684    }
1685    fn pattern_matches_boxed(&self, grid: &MappingGrid, i: usize, j: usize) -> bool {
1686        pattern_matches(self, grid, i, j)
1687    }
1688    fn apply_gadget_boxed(&self, grid: &mut MappingGrid, i: usize, j: usize) {
1689        apply_gadget(self, grid, i, j);
1690    }
1691    fn apply_weighted_gadget_boxed(&self, grid: &mut MappingGrid, i: usize, j: usize) {
1692        apply_weighted_gadget(self, grid, i, j);
1693    }
1694}
1695
1696/// Apply a weighted gadget pattern at position (i, j) with proper weights.
1697/// Uses mapped_graph locations and mapped_weights for each node.
1698#[allow(clippy::needless_range_loop)]
1699pub fn apply_weighted_gadget<P: Pattern>(pattern: &P, grid: &mut MappingGrid, i: usize, j: usize) {
1700    let (m, n) = pattern.size();
1701    let (mapped_locs, _) = pattern.mapped_graph();
1702    let mapped_weights = pattern.mapped_weights();
1703
1704    // First clear the gadget area
1705    for r in 0..m {
1706        for c in 0..n {
1707            let grid_r = i + r;
1708            let grid_c = j + c;
1709            grid.set(grid_r, grid_c, CellState::Empty);
1710        }
1711    }
1712
1713    // Build a map of (row, col) -> accumulated weight for doubled nodes
1714    let mut weight_map: HashMap<(usize, usize), i32> = HashMap::new();
1715    for (idx, &(r, c)) in mapped_locs.iter().enumerate() {
1716        let weight = mapped_weights.get(idx).copied().unwrap_or(2);
1717        *weight_map.entry((r, c)).or_insert(0) += weight;
1718    }
1719
1720    // Count occurrences to detect doubled nodes
1721    let mut count_map: HashMap<(usize, usize), usize> = HashMap::new();
1722    for &(r, c) in &mapped_locs {
1723        *count_map.entry((r, c)).or_insert(0) += 1;
1724    }
1725
1726    // Set cells with proper weights
1727    for (&(r, c), &total_weight) in &weight_map {
1728        let grid_r = i + r - 1; // Convert 1-indexed to 0-indexed
1729        let grid_c = j + c - 1;
1730        let count = count_map.get(&(r, c)).copied().unwrap_or(1);
1731
1732        let state = if count > 1 {
1733            CellState::Doubled {
1734                weight: total_weight,
1735            }
1736        } else {
1737            CellState::Occupied {
1738                weight: total_weight,
1739            }
1740        };
1741        grid.set(grid_r, grid_c, state);
1742    }
1743}
1744
1745/// Map configuration back through a single gadget.
1746pub fn map_config_back_pattern<P: Pattern>(
1747    pattern: &P,
1748    gi: usize,
1749    gj: usize,
1750    config: &mut [Vec<usize>],
1751) {
1752    let (m, n) = pattern.size();
1753    let (mapped_locs, mapped_pins) = pattern.mapped_graph();
1754    let (source_locs, _, _) = pattern.source_graph();
1755
1756    // Step 1: Extract config at mapped locations
1757    let mapped_config: Vec<usize> = mapped_locs
1758        .iter()
1759        .map(|&(r, c)| {
1760            let row = gi + r - 1;
1761            let col = gj + c - 1;
1762            config
1763                .get(row)
1764                .and_then(|row_vec| row_vec.get(col))
1765                .copied()
1766                .unwrap_or(0)
1767        })
1768        .collect();
1769
1770    // Step 2: Compute boundary config
1771    let bc = {
1772        let mut result = 0usize;
1773        for (i, &pin_idx) in mapped_pins.iter().enumerate() {
1774            if pin_idx < mapped_config.len() && mapped_config[pin_idx] > 0 {
1775                result |= 1 << i;
1776            }
1777        }
1778        result
1779    };
1780
1781    // Step 3: Look up source config
1782    let d1 = pattern.mapped_entry_to_compact();
1783    let d2 = pattern.source_entry_to_configs();
1784
1785    let compact = d1.get(&bc).copied();
1786    debug_assert!(
1787        compact.is_some(),
1788        "Boundary config {} not found in mapped_entry_to_compact",
1789        bc
1790    );
1791    let compact = compact.unwrap_or(0);
1792
1793    let source_configs = d2.get(&compact).cloned();
1794    debug_assert!(
1795        source_configs.is_some(),
1796        "Compact {} not found in source_entry_to_configs",
1797        compact
1798    );
1799    let source_configs = source_configs.unwrap_or_default();
1800
1801    debug_assert!(
1802        !source_configs.is_empty(),
1803        "Empty source configs for compact {}.",
1804        compact
1805    );
1806    let new_config = if source_configs.is_empty() {
1807        vec![false; source_locs.len()]
1808    } else {
1809        source_configs[0].clone()
1810    };
1811
1812    // Step 4: Clear gadget area
1813    for row in gi..gi + m {
1814        for col in gj..gj + n {
1815            if let Some(row_vec) = config.get_mut(row) {
1816                if let Some(cell) = row_vec.get_mut(col) {
1817                    *cell = 0;
1818                }
1819            }
1820        }
1821    }
1822
1823    // Step 5: Write source config
1824    for (k, &(r, c)) in source_locs.iter().enumerate() {
1825        let row = gi + r - 1;
1826        let col = gj + c - 1;
1827        if let Some(rv) = config.get_mut(row) {
1828            if let Some(cv) = rv.get_mut(col) {
1829                *cv += if new_config.get(k).copied().unwrap_or(false) {
1830                    1
1831                } else {
1832                    0
1833                };
1834            }
1835        }
1836    }
1837}