yui_link/link/
link.rs

1use core::panic;
2use std::collections::{HashMap, HashSet};
3use std::fmt::Display;
4use itertools::Itertools;
5use yui_core::{hashmap, CloneAnd, Sign};
6use yui_core::bitseq::Bit;
7
8use super::{Node, NodeType, Path};
9
10pub type Edge = usize;
11pub type State = yui_core::bitseq::BitSeq;
12pub type XCode = [Edge; 4];
13
14#[derive(Debug, Clone)]
15pub struct Link { 
16    nodes: Vec<Node>,
17    edges: HashSet<Edge>
18}
19
20impl Link {
21    pub fn new(nodes: Vec<Node>) -> Self { 
22        let edges = nodes.iter().flat_map(|x| x.edges()).cloned().collect();
23        let l = Self { nodes, edges };
24        l.validate();
25        l
26    }
27
28    fn validate(&self) { 
29        assert_eq!(self.edges.len(), self.nodes.len() * 2, "Invalid data.");
30        self.traverse(|_, _, _| ());
31    }
32
33    pub fn empty() -> Link {
34        Link { nodes: vec![], edges: HashSet::new() }
35    }
36
37    pub fn is_empty(&self) -> bool {
38        self.nodes.is_empty()
39    }
40
41    pub fn is_knot(&self) -> bool { 
42        self.n_components() == 1
43    }
44
45    pub fn writhe(&self) -> i32 { 
46        let (p, n) = self.count_signed_crossings();
47        (p as i32) - (n as i32)
48    }
49
50    pub fn mirror(&self) -> Self {
51        self.clone_and(|l|
52            l.nodes.iter_mut().for_each(|x| x.cc())
53        )
54    }
55
56    pub fn n_nodes(&self) -> usize { 
57        self.nodes.len()
58    }
59
60    pub fn nodes(&self) -> impl Iterator<Item = &Node> { 
61        self.nodes.iter()
62    }
63
64    pub fn node(&self, i: usize) -> &Node { 
65        &self.nodes[i]
66    }
67
68    pub fn node_mut(&mut self, i: usize) -> &mut Node { 
69        &mut self.nodes[i]
70    }
71
72    pub fn crossings(&self) -> impl Iterator<Item = &Node> { 
73        self.nodes.iter().filter(|x| x.is_crossing())
74    }
75
76    pub fn count_crossings(&self) -> usize { 
77        self.nodes.iter()
78            .filter(|x| x.is_crossing())
79            .count()
80    }
81
82    pub fn count_signed_crossings(&self) -> (usize, usize) {
83        let signs = self.collect_crossing_signs();
84        let pos = signs.iter().filter(|(_, s)| s.is_positive()).count();
85        let neg = signs.len() - pos;
86        (pos, neg)
87    }
88
89    pub fn collect_crossing_signs(&self) -> HashMap<usize, Sign> {
90        use NodeType::{X, Xm};
91
92        let mut result = hashmap!{};
93
94        self.traverse(|_, i, j| { 
95            let c = self.node(i);
96            match (c.ntype(), j) { 
97                (Xm, 1) | (X, 3) => result.insert(i, Sign::Pos),
98                (Xm, 3) | (X, 1) => result.insert(i, Sign::Neg),
99                _ => None
100            };
101        });
102
103        result
104    }
105
106    pub fn n_edges(&self) -> usize { 
107        self.edges.len()
108    }
109    
110    pub fn edges(&self) -> impl Iterator<Item = &Edge> {
111        self.edges.iter()
112    }
113
114    pub fn min_edge(&self) -> Option<Edge> { 
115        self.nodes.first().map(|x| x.min_edge())
116    }
117
118    pub fn n_components(&self) -> usize { 
119        let mut count = 0;
120        self.traverse(|c, _, _| 
121            if count <= c { count = c + 1 } 
122        );
123        count
124    }
125
126    pub fn collect_components(&self) -> Vec<Path> {
127        let mut comps = vec![];
128
129        self.traverse(|c, i, j| { 
130            if c == comps.len() { 
131                comps.push(vec![]);
132            }
133
134            let e = self.node(i).edge(j);
135            comps[c].push(e);
136        });
137
138        comps.into_iter().map(|edges| 
139            Path::circ(edges)
140        ).collect()
141    }
142
143    pub fn crossing_change(&self, i: usize) -> Self { 
144        assert!(self.node(i).is_crossing());
145        self.clone_and(|l| l.node_mut(i).cc())
146    }
147
148    pub fn resolved_at(&self, i: usize, r: Bit) -> Self {
149        assert!(self.node(i).is_crossing());
150        self.clone_and(|l| l.node_mut(i).resolve(r))
151    }
152
153    pub fn resolved_by(&self, s: &State) -> Self {
154        assert!(s.len() == self.count_crossings());
155
156        let n = self.nodes.len();
157        let itr = (0..n).filter(|&i| self.node(i).is_crossing());
158
159        self.clone_and(|l| {
160            for (i, r) in Iterator::zip(itr, s.iter()) {
161                l.node_mut(i).resolve(r); 
162            }
163        })
164    }
165
166    pub fn seifert_state(&self) -> State { 
167        let signs = self.collect_crossing_signs();
168        let seq = signs.iter().sorted_by_key(|(&i, _)| i).map(|(_, s)| 
169            match s { 
170                Sign::Pos => 0, 
171                Sign::Neg => 1
172            }
173        ); 
174        State::from_iter(seq)
175    }
176
177    pub fn seifert_circles(&self) -> Vec<Path> { 
178        self.resolved_by(&self.seifert_state()).collect_components()
179    }
180
181    fn traverse<F>(&self, mut f: F) where 
182    F: FnMut(usize, usize, usize) { 
183        let n = self.n_nodes();
184
185        let mut c = 0; // component counter
186        let mut remain = self.edges.clone();
187
188        // MEMO: For a link obtained from a PD-code, 
189        // the following loop should break after the first iteration: j0 = 0.
190
191        for j0 in [0, 1, 2] { 
192            for i0 in 0..n {
193                let e0 = self.node(i0).edge(j0);
194                if !remain.remove(&e0) { 
195                    continue 
196                }
197
198                self.traverse_from((i0, j0), |i, j| { 
199                    let e = self.node(i).edge(j);
200                    remain.remove(&e);
201
202                    f(c, i, j);
203                });
204
205                c += 1;
206            }
207
208            if remain.is_empty() { 
209                break
210            }
211        }
212
213        assert!(remain.is_empty())
214    }
215
216    fn traverse_from<F>(&self, start: (usize, usize), mut f:F) where
217        F: FnMut(usize, usize)
218    {
219        let (mut i, mut j) = start;
220
221        f(i, j); // call starting point
222
223        loop {
224            let c = self.node(i);
225            let k = c.traverse_inner(j);
226            let next = self.traverse_outer(i, k);
227
228            if next == start {
229                break
230            }
231
232            (i, j) = next;
233
234            f(i, j)
235        }
236    }
237
238    fn traverse_outer(&self, n_index: usize, e_index: usize) -> (usize, usize) {
239        let e = self.nodes[n_index].edge(e_index);
240
241        for (i, c) in self.nodes.iter().enumerate() { 
242            for (j, &f) in c.edges().iter().enumerate() { 
243                if e == f && (n_index != i || (n_index == i && e_index != j)) { 
244                    return (i, j)
245                }
246            }
247        }
248
249        panic!("Broken data")
250    }
251}
252
253impl Link { 
254    // Planer Diagram code, represented by crossings:
255    //
256    //     3   2
257    //      \ /
258    //       \      = (0, 1, 2, 3)
259    //      / \
260    //     0   1
261    //
262    // The lower edge has direction 0 -> 2.
263    // The crossing is +1 if the upper goes 3 -> 1.
264    // see: http://katlas.math.toronto.edu/wiki/Planar_Diagrams
265
266    pub fn from_pd_code<I>(pd_code: I) -> Self
267    where I: IntoIterator<Item = XCode> { 
268        let nodes = pd_code.into_iter().map(Node::from_pd_code).collect();
269        Self::new(nodes)
270    }
271
272    pub fn is_valid_name(str: &str) -> bool { 
273        use regex::Regex;
274        let r1 = Regex::new(r"^([1-9]|10)_[0-9]+$").unwrap();
275        let r2 = Regex::new(r"^(K|L)?[1-9]+(a|n)_?[0-9]+$").unwrap(); // FIXME tmp
276        r1.is_match(str) || r2.is_match(str)
277    }
278
279    pub fn load(name_or_path: &str) -> Result<Link, Box<dyn std::error::Error>> {
280        const RESOURCE_DIR: &str = "resources/links/";
281        
282        if Self::is_valid_name(name_or_path) { 
283            let dir = std::env!("CARGO_MANIFEST_DIR");
284            let path = format!("{dir}/{RESOURCE_DIR}{name_or_path}.json");
285            Self::_load(&path)
286        } else { 
287            Self::_load(name_or_path)
288        }
289    }
290
291    fn _load(path: &str) -> Result<Link, Box<dyn std::error::Error>> {
292        let json = std::fs::read_to_string(path)?;
293        let data: Vec<XCode> = serde_json::from_str(&json)?;
294        let l = Link::from_pd_code(data);
295        Ok(l)
296    }
297
298    pub fn unknot() -> Link { 
299        Link::from_pd_code([[0, 1, 1, 0]]).resolved_at(0, Bit::Bit0)
300    }
301
302    pub fn trefoil() -> Link { 
303        Link::from_pd_code([[1,4,2,5],[3,6,4,1],[5,2,6,3]])
304    }
305
306    pub fn figure8() -> Link { 
307        Link::from_pd_code([[4,2,5,1],[8,6,1,5],[6,3,7,4],[2,7,3,8]])
308    }
309
310    pub fn hopf_link() -> Link { 
311        Link::from_pd_code([[4,1,3,2],[2,3,1,4]])
312    }
313}
314
315impl Display for Link {
316    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
317        write!(f, "L[{}]", self.nodes.iter().map(|x| x.to_string()).join(", "))
318    }
319}
320
321#[cfg(test)]
322mod tests { 
323    use yui_core::hashmap;
324
325    use super::*;
326    use super::NodeType::{X, Xm};
327
328    #[test]
329    fn link_init() { 
330        let l = Link::new(vec![]);
331        assert_eq!(l.nodes.len(), 0);
332    }
333
334    #[test]
335    fn link_from_pd_code() { 
336        let pd_code = [[0,0,1,1]];
337        let l = Link::from_pd_code(pd_code);
338        assert_eq!(l.nodes.len(), 1);
339        assert_eq!(l.node(0).ntype(), X);
340    }
341
342    #[test]
343    fn link_is_empty() {
344        let l = Link::empty();
345        assert!(l.is_empty());
346
347        let pd_code = [[0,0,1,1]];
348        let l = Link::from_pd_code(pd_code);
349        assert!(!l.is_empty());
350    }
351
352    #[test]
353    fn link_crossing_num() {
354        let l = Link::empty();
355        assert_eq!(l.count_crossings(), 0);
356
357        let pd_code = [[0,0,1,1]];
358        let l = Link::from_pd_code(pd_code);
359        assert_eq!(l.count_crossings(), 1);
360        
361        let pd_code = [[1,4,2,5],[3,6,4,1],[5,2,6,3]];
362        let l = Link::from_pd_code(pd_code);
363        assert_eq!(l.count_crossings(), 3);
364    }
365
366    #[test]
367    fn link_next() {
368        let pd_code = [[0,0,1,1]];
369        let l = Link::from_pd_code(pd_code);
370
371        assert_eq!(l.traverse_outer(0, 0), (0, 1));
372        assert_eq!(l.traverse_outer(0, 1), (0, 0));
373        assert_eq!(l.traverse_outer(0, 2), (0, 3));
374        assert_eq!(l.traverse_outer(0, 3), (0, 2));
375    }
376
377    #[test]
378    fn link_traverse() {
379        let traverse = |l: &Link, (i0, j0)| { 
380            let mut queue = vec![];
381            l.traverse_from((i0, j0), |i, j| queue.push((i, j)));
382            queue
383        };
384
385        let pd_code = [[0,0,1,1]];
386        let l = Link::from_pd_code(pd_code);
387        let path = traverse(&l, (0, 0));
388        
389        assert_eq!(path, [(0, 0), (0, 3)]); // loop
390    }
391
392    #[test]
393    fn link_crossing_signs() {
394        let pd_code = [[0,0,1,1]];
395        let l = Link::from_pd_code(pd_code);
396        assert_eq!(l.collect_crossing_signs(), hashmap!{ 0 => Sign::Pos});
397
398        let pd_code = [[0,1,1,0]];
399        let l = Link::from_pd_code(pd_code);
400        assert_eq!(l.collect_crossing_signs(), hashmap!{ 0 => Sign::Neg} );
401
402        let pd_code = [[0,0,1,1]];
403        let l = Link::from_pd_code(pd_code).resolved_at(0, Bit::Bit0);
404        assert_eq!(l.collect_crossing_signs(), hashmap!{});
405    }
406
407    #[test]
408    fn link_writhe() {
409        let pd_code = [[0,0,1,1]];
410        let l = Link::from_pd_code(pd_code);
411        assert_eq!(l.writhe(), 1);
412
413        let pd_code = [[0,1,1,0]];
414        let l = Link::from_pd_code(pd_code);
415        assert_eq!(l.writhe(), -1);
416
417        let pd_code = [[0,0,1,1]];
418        let l = Link::from_pd_code(pd_code).resolved_at(0, Bit::Bit0);
419        assert_eq!(l.writhe(), 0);
420
421    }
422
423    #[test]
424    fn link_components() {
425        let pd_code = [[0,0,1,1]];
426        let l = Link::from_pd_code(pd_code);
427        let comps = l.collect_components();
428        assert_eq!(comps, vec![ Path::new(vec![0, 1], true)]);
429    }
430
431    #[test]
432    fn link_mirror() { 
433        let pd_code = [[0,0,1,1]];
434        let l = Link::from_pd_code(pd_code);
435        assert_eq!(l.node(0).ntype(), X);
436
437        let l = l.mirror();
438        assert_eq!(l.node(0).ntype(), Xm);
439    }
440
441    #[test]
442    fn link_resolve() {
443        let s = State::from([0, 0, 0]);
444        let l = Link::from_pd_code([[1,4,2,5],[3,6,4,1],[5,2,6,3]]) // trefoil
445            .resolved_by(&s);
446
447        let comps = l.collect_components();
448        assert_eq!(comps.len(), 3);
449        assert!(comps.iter().all(|c| c.is_circle()));
450
451        let s = State::from([1, 1, 1]);
452        let l = Link::from_pd_code([[1,4,2,5],[3,6,4,1],[5,2,6,3]]) // trefoil
453            .resolved_by(&s);
454
455        let comps = l.collect_components();
456        assert_eq!(comps.len(), 2);
457        assert!(comps.iter().all(|c| c.is_circle()));
458    }
459
460    #[test]
461    fn empty_link() {
462        let l = Link::empty();
463        assert_eq!(l.count_crossings(), 0);
464        assert_eq!(l.writhe(), 0);
465        assert_eq!(l.n_components(), 0);
466    }
467
468    #[test]
469    fn unknot() { 
470        let l = Link::unknot();
471        assert_eq!(l.count_crossings(), 0);
472        assert_eq!(l.writhe(), 0);
473        assert_eq!(l.n_components(), 1);
474    }
475
476    #[test]
477    fn trefoil() { 
478        let l = Link::trefoil();
479        assert_eq!(l.count_crossings(), 3);
480        assert_eq!(l.writhe(), -3);
481        assert_eq!(l.n_components(), 1);
482    }
483
484    #[test]
485    fn figure8() { 
486        let l = Link::figure8();
487        assert_eq!(l.count_crossings(), 4);
488        assert_eq!(l.writhe(), 0);
489        assert_eq!(l.n_components(), 1);
490    }
491
492    #[test]
493    fn hopf_link() { 
494        let l = Link::hopf_link();
495        assert_eq!(l.count_crossings(), 2);
496        assert_eq!(l.writhe(), -2);
497        assert_eq!(l.n_components(), 2);
498    }
499
500    #[test]
501    fn unlink_2() {
502        let pd_code = [[1,2,3,4], [3,2,1,4]];
503        let l = Link::from_pd_code(pd_code);
504        assert_eq!(l.count_crossings(), 2);
505        assert_eq!(l.writhe(), 0);
506        assert_eq!(l.n_components(), 2);
507    }
508
509
510    #[test]
511    fn l2x4() {
512        let pd_code = [[1,5,2,8],[5,3,6,2],[3,7,4,6],[7,1,8,4]];
513        let l = Link::from_pd_code(pd_code);
514        assert_eq!(l.count_crossings(), 4);
515        assert_eq!(l.writhe(), 4);
516        assert_eq!(l.n_components(), 2);
517    }
518
519    #[test]
520    fn load() { 
521        let l = Link::load("3_1");
522        assert!(l.is_ok());
523
524        let l = l.unwrap();
525        assert_eq!(l.count_crossings(), 3);
526    }
527
528    #[test]
529    fn crossing_change() { 
530        let l = Link::from_pd_code([[1,4,2,5],[3,6,4,1],[5,2,6,3]]);
531        let l2 = l.crossing_change(1);
532
533        assert_eq!(l.node(1),  &Node::new(NodeType::X, [3,6,4,1]));
534        assert_eq!(l2.node(1), &Node::new(NodeType::Xm, [3,6,4,1]));
535    }
536}