1use std::ops::{MulAssign, Mul};
5use auto_impl_ops::auto_ops;
6use delegate::delegate;
7use derive_more::{Display, Debug};
8use itertools::Itertools;
9
10use crate::{Link, LinkBuilder, NodeType};
11
12use super::braid_gen::{BraidGen, from_raw};
13
14#[derive(Clone, PartialEq, Eq, Display, Debug)]
15#[display("{:?}", elements)]
16pub struct Braid {
17 strands: usize,
18 elements: Vec<BraidGen>
19}
20
21impl Braid {
22 pub fn new(strands: usize, elements: Vec<BraidGen>) -> Self {
23 if let Some(g) = elements.iter().find(|g| g.index() >= strands) {
24 panic!("σ{} needs {} strands, but the braid has {strands}", g.index(), g.index() + 1);
25 }
26 Self { strands, elements }
27 }
28
29 pub fn id(strands: usize) -> Self {
30 Self::new(
31 strands,
32 vec![]
33 )
34 }
35
36 pub fn generator(strands: usize, index: usize) -> Self {
37 let val = i8::try_from(index).expect("index must fit in i8");
38 Self::new(strands, vec![BraidGen::new(val)])
39 }
40
41 pub fn strands(&self) -> usize {
42 self.strands
43 }
44
45 pub fn elements(&self) -> &[BraidGen] {
46 &self.elements
47 }
48
49 delegate! {
50 to self.elements {
51 pub fn len(&self) -> usize;
52 #[call(is_empty)]
53 pub fn is_id(&self) -> bool;
54 }
55 }
56
57 pub fn inv(&self) -> Self {
58 Self::new(
59 self.strands,
60 self.elements.iter().rev().map(
61 |g| g.inv()
62 ).collect()
63 )
64 }
65
66 pub fn extend(&self, by: usize) -> Self {
67 Self::new(self.strands + by, self.elements.clone())
68 }
69
70 pub fn reduced(&self) -> Self {
71 let mut stack: Vec<BraidGen> = Vec::new();
72 for g in self.elements.iter().copied() {
73 match stack.last() {
74 Some(&top) if top.inv() == g => { stack.pop(); }
75 _ => stack.push(g),
76 }
77 }
78 Self::new(self.strands, stack)
79 }
80
81 pub fn closure(&self) -> Link {
85 let mut b = LinkBuilder::new();
86
87 let xs: Vec<_> = self.elements.iter().map(|g| {
88 let nt = if g.sign().is_positive() { NodeType::XR } else { NodeType::XL };
89 b.add_crossing(nt)
90 }).collect();
91
92 let strands = self.elements.iter().zip(&xs).fold(
95 vec![vec![]; self.strands],
96 |mut strands, (g, &x)| {
97 let i = g.index() - 1;
98 strands[i] .push(((x, 3), (x, 0)));
99 strands[i + 1].push(((x, 2), (x, 1)));
100 strands
101 }
102 );
103
104 for visits in strands {
105 if visits.is_empty() {
106 b.add_loop();
107 } else {
108 for (&(_, bot), &(top, _)) in visits.iter().circular_tuple_windows() {
109 b.connect(bot, top);
110 }
111 }
112 }
113
114 b.build_with(|_, j| j >= 2).unwrap()
117 }
118
119 pub fn display(&self) -> String {
120 fn row(strands: usize, g: &BraidGen) -> String {
121 let index = g.index();
122 let sign = g.sign();
123
124 (0..3).map(|r| {
125 (1..=strands).map(|i| {
126 if i == index {
127 match r {
128 0 => "\\ /",
129 1 => if sign.is_positive() { " / " } else { " \\ " },
130 _ => "/ \\",
131 }
132 } else if i == index + 1 {
133 " "
134 } else {
135 "| "
136 }
137 }).join("")
138 }).join("\n")
139 }
140
141 self.elements.iter().map(|g|
142 row(self.strands, g)
143 ).join("\n")
144 }
145
146 pub fn load(name: &str) -> Result<Braid, Box<dyn std::error::Error>> {
147 let json = yui_core::util::data_dir::load_json("braid", name)?;
148 let code: Vec<i32> = serde_json::from_str(&json)?;
149 Ok(Braid::from_iter(code))
150 }
151}
152
153macro_rules! impl_from_int {
154 ($($t:ty),* $(,)?) => {
155 $(
156 impl<const N: usize> From<[$t; N]> for Braid {
157 fn from(value: [$t; N]) -> Self {
158 Self::from_iter(value)
159 }
160 }
161
162 impl FromIterator<$t> for Braid {
163 fn from_iter<T: IntoIterator<Item = $t>>(iter: T) -> Self {
164 let elements = iter.into_iter().map(|v|
165 from_raw(i8::try_from(v).expect("BraidGen value must fit in i8"))
166 ).collect_vec();
167 let strands = elements.iter().map(|g| g.index() + 1).max().unwrap_or(0);
168 Self::new(strands, elements)
169 }
170 }
171 )*
172 };
173}
174
175impl_from_int!(i8, i16, i32, i64);
176
177#[auto_ops]
178impl MulAssign<&Braid> for Braid {
179 fn mul_assign(&mut self, rhs: &Braid) {
180 assert_eq!(self.strands, rhs.strands);
181 self.elements.extend(rhs.elements.iter().cloned());
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188 use yui_core::poly::LPoly;
189 use crate::misc::jones_polynomial;
190
191 type P = LPoly<'q', i32>;
192
193 #[test]
194 fn closure_orientation() {
195 let l = Braid::from([-1, -1, -1, -1]).closure();
199 assert_eq!(l.n_comps(), 2);
200 assert!(l.is_oriented());
201 assert_eq!(l.writhe(), -4);
202
203 let q = P::mono;
204 assert_eq!(jones_polynomial(&l), P::from_iter([(q(-2), 1), (q(-4), 1), (q(-6), 1), (q(-12), 1)]));
205 }
206
207 #[test]
208 fn init_by_code() {
209 let b = Braid::from([1, 1, -2, -1, 3]);
210 assert_eq!(b.strands(), 4);
211 assert_eq!(b.len(), 5);
212 }
213
214 #[test]
215 fn to_string() {
216 let b = Braid::from([1, 1, -2, -1, 3]);
217 assert_eq!(b.to_string(), "[1, 1, -2, -1, 3]");
218 }
219
220 #[test]
221 fn display() {
222 let b = Braid::from([1, 1, -2, -1, 3]);
223 let display = b.display();
224 assert_ne!(display, "")
225 }
226
227 #[test]
228 fn reduced_empty() {
229 let b = Braid::from([] as [i32; 0]);
230 assert_eq!(b.reduced(), b);
231 }
232
233 #[test]
234 fn reduced_no_cancel() {
235 let b = Braid::from([1, 2, 3]);
236 assert_eq!(b.reduced(), b);
237 }
238
239 #[test]
240 fn reduced_single_pair() {
241 let b = Braid::from([1, -1]);
242 let r = b.reduced();
243 assert!(r.is_id());
244 assert_eq!(r.strands(), b.strands());
245 }
246
247 #[test]
248 fn reduced_cascading() {
249 let b = Braid::from([1, 2, -2, -1]);
250 assert!(b.reduced().is_id());
251 }
252
253 #[test]
254 fn reduced_mid_sequence() {
255 let b = Braid::from([1, 2, -2, 3]);
256 assert_eq!(b.reduced(), Braid::new(b.strands(), vec![BraidGen::new(1), BraidGen::new(3)]));
257 }
258
259 #[test]
260 fn reduced_non_inverse_pair() {
261 let b = Braid::from([1, -2]);
263 assert_eq!(b.reduced(), b);
264 }
265
266 #[test]
267 fn closure() {
268 let b = Braid::test_data("3_1");
269 let l = b.closure();
270
271 assert_eq!(l.n_crossings(), 3);
272 assert_eq!(l.writhe(), 3);
273 assert_eq!(l.n_comps(), 1);
274 }
275
276 #[test]
277 fn extend() {
278 let b = Braid::from([1, 2]).extend(2);
279 assert_eq!(b.strands(), 5);
280 assert_eq!(b.len(), 2);
281 }
282
283 #[test]
284 fn extend_zero() {
285 let b0 = Braid::from([1, 2]);
286 let b1 = b0.extend(0);
287 assert_eq!(b1, b0);
288 }
289
290 #[test]
291 fn extend_closure_adds_loops() {
292 let b = Braid::from([1, 1, 1]).extend(2);
294 let l = b.closure();
295 assert_eq!(l.n_crossings(), 3);
296 assert_eq!(l.n_loops(), 2);
297 assert_eq!(l.n_comps(), 1 + 2); }
299
300 #[test]
301 fn closure_identity_braid() {
302 let b = Braid::id(3);
304 let l = b.closure();
305
306 assert_eq!(l.n_crossings(), 0);
307 assert_eq!(l.n_loops(), 3);
308 assert_eq!(l.n_comps(), 3);
309 }
310
311 #[test]
312 fn closure_with_free_strand() {
313 let b = Braid::new(3, vec![BraidGen::new(1)]);
316 let l = b.closure();
317
318 assert_eq!(l.n_crossings(), 1);
319 assert_eq!(l.n_loops(), 1);
320 assert_eq!(l.n_comps(), 2);
321 }
322
323 #[test]
324 #[should_panic(expected = "needs 6 strands")]
325 fn new_rejects_a_generator_beyond_the_strands() {
326 let _ = Braid::new(2, vec![BraidGen::new(5)]);
328 }
329}