yui_link/
inv_link.rs

1use std::collections::HashMap;
2
3use itertools::Itertools;
4use num_integer::Integer;
5use yui_core::CloneAnd;
6use crate::{Node, Edge, Link, XCode};
7
8// Involutive link
9#[derive(Debug, Clone)]
10pub struct InvLink { 
11    link: Link,
12    base_pt: Option<Edge>,
13    e_map: HashMap<Edge, Edge>,
14    x_map: HashMap<Node, Node>
15}
16
17impl InvLink { 
18    pub fn new<F>(link: Link, e_map: F, base_pt: Option<Edge>) -> InvLink
19    where F: Fn(Edge) -> Edge { 
20        let e_map = link.edges().map(|&e| (e, e_map(e))).collect::<HashMap<_, _>>();
21        let mut x_map = HashMap::new();
22
23        // TODO? check resolution
24        for x in link.nodes() { 
25            let edges = x.edges().map(|e| e_map.get(&e).unwrap());
26            let find = link.nodes().find_position(|y|
27                edges.iter().all(|e| y.edges().contains(e))
28            );
29
30            assert!(find.is_some(), "no match for x: {x} -> {edges:?}");
31
32            let j = find.unwrap().0;
33            let y = link.node(j);
34
35            x_map.insert(x.clone(), y.clone());
36
37            if x != y { 
38                x_map.insert(y.clone(), x.clone());
39            }
40        }
41
42        assert_eq!(x_map.len(), link.n_nodes());
43
44        if let Some(p) = base_pt { 
45            assert_eq!(p, e_map[&p], "base-pt must be on-axis.");
46        }
47
48        Self { link, base_pt, e_map, x_map }
49    }
50
51    pub fn sinv_knot_from_code<I1>(pd_code: I1) -> Self
52    where I1: IntoIterator<Item = XCode> { 
53        let code = pd_code.into_iter().collect_vec();
54        let l = Link::from_pd_code(code);
55        let n = l.n_edges();
56
57        assert!(n.is_even(), "number of edges must be even.");
58        assert_eq!(l.edges().min(), Some(&1), "edge must start from index 1.");
59        assert_eq!(l.edges().max(), Some(&n), "edges must have sequential indexing.");
60
61        Self::new(l, |e| (n + 1 - e) % n + 1, Some(1))
62    }
63
64    pub fn link(&self) -> &Link { 
65        &self.link
66    }
67
68    pub fn base_pt(&self) -> Option<Edge> {
69        self.base_pt
70    }
71
72    pub fn inv_e(&self, e: Edge) -> Edge { 
73        self.e_map.get(&e).cloned().unwrap()
74    }
75
76    pub fn inv_x(&self, x: &Node) -> &Node { 
77        self.x_map.get(x).unwrap()
78    }
79
80    pub fn mirror(&self) -> Self { 
81        Self { 
82            link: self.link.mirror(),
83            base_pt: self.base_pt,
84            e_map: self.e_map.clone(),
85            x_map: self.x_map.iter().map(|(x, y)| 
86                (x.clone_and(|x| x.cc()), y.clone_and(|y| y.cc()))
87            ).collect(), 
88        }
89    }
90}
91
92impl InvLink {
93    pub fn load(name: &str) -> Result<InvLink, Box<dyn std::error::Error>> {
94        match name {
95            "3_1" => Ok(InvLink::sinv_knot_from_code(
96                [[1,5,2,4],[3,1,4,6],[5,3,6,2]]
97            )),
98            "4_1" => Ok(InvLink::sinv_knot_from_code(
99                [[2,7,3,8],[4,2,5,1],[6,3,7,4],[8,6,1,5]],
100            )),
101            "5_1" => Ok(InvLink::sinv_knot_from_code(
102                [[1,7,2,6],[3,9,4,8],[5,1,6,10],[7,3,8,2],[9,5,10,4]],
103            )), 
104            "5_2a" => Ok(InvLink::sinv_knot_from_code(
105                [[3,11,4,10],[5,9,6,8],[6,2,7,1],[9,5,10,4],[11,3,12,2],[12,8,1,7]],
106            )), 
107            "5_2b" => Ok(InvLink::sinv_knot_from_code(
108                [[1,7,2,6],[4,10,5,9],[5,3,6,2],[7,1,8,12],[10,4,11,3],[11,9,12,8]],
109            )), 
110            "6_1a" => Ok(InvLink::sinv_knot_from_code(
111                [[1,6,2,7],[3,11,4,10],[5,9,6,8],[7,12,8,1],[9,5,10,4],[11,3,12,2]],
112            )), 
113            "6_1b" => Ok(InvLink::sinv_knot_from_code(
114                [[1,7,2,6],[3,10,4,11],[5,3,6,2],[7,1,8,12],[9,4,10,5],[11,9,12,8]],
115            )), 
116            "6_2a" => Ok(InvLink::sinv_knot_from_code(
117                [[1,6,2,7],[3,11,4,10],[5,9,6,8],[7,12,8,1],[9,3,10,2],[11,5,12,4]],
118            )), 
119            "6_2b" => Ok(InvLink::sinv_knot_from_code(
120                [[1,9,2,8],[4,11,5,12],[7,1,8,14],[9,3,10,2],[10,5,11,6],[12,3,13,4],[13,7,14,6]],
121            )), 
122            "6_3" => Ok(InvLink::sinv_knot_from_code(
123                [[3,13,4,12],[6,9,7,10],[8,1,9,2],[10,5,11,6],[11,3,12,2],[13,5,14,4],[14,7,1,8]],
124            )), 
125            "7_1" => Ok(InvLink::sinv_knot_from_code(
126                [[1,9,2,8],[3,11,4,10],[5,13,6,12],[7,1,8,14],[9,3,10,2],[11,5,12,4],[13,7,14,6]],
127            )), 
128            "7_2a" => Ok(InvLink::sinv_knot_from_code(
129                [[3,15,4,14],[5,13,6,12],[7,11,8,10],[8,2,9,1],[11,7,12,6],[13,5,14,4],[15,3,16,2],[16,10,1,9]],
130            )), 
131            "7_2b" => Ok(InvLink::sinv_knot_from_code(
132                [[1,9,2,8],[3,7,4,6],[4,14,5,13],[7,3,8,2],[9,1,10,16],[11,15,12,14],[12,6,13,5],[15,11,16,10]],
133            )), 
134            "7_3a" => Ok(InvLink::sinv_knot_from_code(
135                [[1,9,2,8],[3,13,4,12],[5,11,6,10],[7,1,8,14],[9,3,10,2],[11,5,12,4],[13,7,14,6]],
136            )), 
137            "7_3b" => Ok(InvLink::sinv_knot_from_code(
138                [[3,13,4,12],[5,15,6,14],[8,2,9,1],[10,7,11,8],[11,3,12,2],[13,5,14,4],[15,7,16,6],[16,10,1,9]],
139            )), 
140            "7_4a" => Ok(InvLink::sinv_knot_from_code(
141                [[2,8,3,7],[3,15,4,14],[5,13,6,12],[8,2,9,1],[10,16,11,15],[11,7,12,6],[13,5,14,4],[16,10,1,9]],
142            )), 
143            "7_4b" => Ok(InvLink::sinv_knot_from_code(
144                [[2,10,3,9],[4,12,5,11],[6,14,7,13],[8,4,9,3],[10,2,11,1],[12,8,13,7],[14,6,1,5]],
145            )), 
146            "7_5a" => Ok(InvLink::sinv_knot_from_code(
147                [[1,9,2,8],[3,13,4,12],[5,11,6,10],[7,1,8,14],[9,7,10,6],[11,3,12,2],[13,5,14,4]],
148            )), 
149            "7_5b" => Ok(InvLink::sinv_knot_from_code(
150                [[1,11,2,10],[4,8,5,7],[5,15,6,14],[9,1,10,18],[11,3,12,2],[12,16,13,15],[13,7,14,6],[16,3,17,4],[17,9,18,8]],
151            )), 
152            "7_6a" => Ok(InvLink::sinv_knot_from_code(
153                [[2,13,3,14],[4,11,5,12],[6,4,7,3],[8,1,9,2],[10,5,11,6],[12,10,13,9],[14,7,1,8]],
154            )), 
155            "7_6b" => Ok(InvLink::sinv_knot_from_code(
156                [[1,8,2,9],[3,15,4,14],[6,11,7,12],[7,4,8,5],[9,16,10,1],[12,5,13,6],[13,10,14,11],[15,3,16,2]],
157            )), 
158            "7_7a" => Ok(InvLink::sinv_knot_from_code(
159                [[1,8,2,9],[4,13,5,14],[7,11,8,10],[9,16,10,1],[11,3,12,2],[12,5,13,6],[14,3,15,4],[15,7,16,6]],
160            )), 
161            "7_7b" => Ok(InvLink::sinv_knot_from_code(
162                [[1,10,2,11],[3,13,4,12],[5,14,6,1],[7,5,8,4],[9,2,10,3],[11,9,12,8],[13,6,14,7]],
163            )), 
164            _ => todo!()
165        }
166    }
167}
168
169#[cfg(test)]
170mod tests { 
171    use super::*;
172
173    #[test]
174    fn inv_e() { 
175        let l = Link::from_pd_code([[1,5,2,4],[3,1,4,6],[5,3,6,2]]);
176        let l = InvLink::new(l, |e| (7 - e) % 6 + 1, None);
177
178        assert_eq!(l.inv_e(1), 1);
179        assert_eq!(l.inv_e(2), 6);
180        assert_eq!(l.inv_e(3), 5);
181        assert_eq!(l.inv_e(4), 4);
182        assert_eq!(l.inv_e(5), 3);
183        assert_eq!(l.inv_e(6), 2);
184    }
185    
186    #[test]
187    fn inv_x() { 
188        let l = Link::from_pd_code([[1,5,2,4],[3,1,4,6],[5,3,6,2]]);
189        let l = InvLink::new(l, |e| (7 - e) % 6 + 1, None);
190        let nodes = l.link.nodes().collect_vec();
191
192        assert_eq!(l.inv_x(&nodes[0]), nodes[1]);
193        assert_eq!(l.inv_x(&nodes[1]), nodes[0]);
194        assert_eq!(l.inv_x(&nodes[2]), nodes[2]);
195    }
196
197    #[test]
198    fn from_sinv() { 
199        let l = InvLink::sinv_knot_from_code([[1,5,2,4],[3,1,4,6],[5,3,6,2]]);
200
201        assert_eq!(l.inv_e(1), 1);
202        assert_eq!(l.inv_e(2), 6);
203        assert_eq!(l.inv_e(3), 5);
204        assert_eq!(l.inv_e(4), 4);
205        assert_eq!(l.inv_e(5), 3);
206        assert_eq!(l.inv_e(6), 2);
207    }
208
209    #[test]
210    fn load_3_1() { 
211        let l = InvLink::load("3_1").unwrap();
212        assert_eq!(l.link().count_crossings(), 3);
213    }
214
215    #[test]
216    fn load_4_1() { 
217        let l = InvLink::load("4_1").unwrap();
218        assert_eq!(l.link().count_crossings(), 4);
219    }
220}