use itertools::Itertools;
use super::{Edge, Link, Node, NodeType};
pub type PDCodeX = [Edge; 4];
impl Link {
pub fn from_pd_code<I>(pd_code: I) -> Self
where I: IntoIterator<Item = PDCodeX> {
let nodes = pd_code.into_iter().map(Node::from_pd_code).collect_vec();
let mut l = Self::from_nodes(nodes); l.reorient(|_, s| s.index() == 0); l
}
pub fn pd_code(&self) -> Vec<PDCodeX> {
assert!(self.loops().is_empty(), "a PD code cannot represent free loops");
let mut pd = Vec::with_capacity(self.n_nodes());
self.traverse_comps(|_, i, j| {
let x = self.node(i);
let under = match x.node_type() {
NodeType::XL => j.index() % 2 == 0,
NodeType::XR => j.index() % 2 == 1,
_ => return,
};
if under {
pd.push([x.edge(j), x.edge(j.shift(1)), x.edge(j.shift(2)), x.edge(j.shift(3))]);
}
});
pd
}
pub fn load(name: &str) -> Result<Link, Box<dyn std::error::Error>> {
let json = yui_core::util::data_dir::load_json("links", name)?;
let data: Vec<PDCodeX> = serde_json::from_str(&json)?;
Ok(Link::from_pd_code(data))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::NodeType::XL;
#[test]
fn pd_code_roundtrip() {
use crate::misc::jones_polynomial;
let l = Link::test_data("3_1");
let l2 = Link::from_pd_code(l.pd_code());
assert_eq!(jones_polynomial(&l), jones_polynomial(&l2), "XL round-trip");
let m = l.mirror();
let m2 = Link::from_pd_code(m.pd_code());
assert_eq!(jones_polynomial(&m), jones_polynomial(&m2), "XR round-trip");
assert_ne!(jones_polynomial(&l), jones_polynomial(&m));
}
#[test]
fn pd_code_depends_only_on_the_diagram() {
for name in ["3_1", "4_1", "5_2", "6_1", "L2a1", "L4a1"] {
let l = Link::test_data(name);
assert_eq!(Link::from_pd_code(l.pd_code()).pd_code(), l.pd_code(), "{name}");
}
let l = Link::pretzel(1, 3, 5);
assert_eq!(Link::from_pd_code(l.pd_code()).pd_code(), l.pd_code(), "pretzel(1,3,5)");
}
#[test]
#[should_panic(expected = "cannot represent free loops")]
fn pd_code_rejects_free_loops() {
let _ = Link::unknot().pd_code();
}
#[test]
fn link_from_pd_code() {
let l = Link::test_data("unknot_l_twist");
assert_eq!(l.n_nodes(), 1);
assert_eq!(l.node(0).node_type(), XL);
}
}