use std::fmt::{Display, Formatter, Result as FmtResult};
#[derive(Clone, Debug)]
pub struct TreeNode {
pub identifier: usize,
pub label: Option<NameOrSupport>,
pub children: Option<[Box<TreeNode>; 2]>,
pub len: Option<f64>,
}
#[derive(Clone, Debug)]
pub enum NameOrSupport {
Name(String),
Support(usize),
}
impl Display for TreeNode {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{}", self.to_newick())
}
}
impl TreeNode {
pub fn leaf(identifier: usize, name: String, len: Option<f64>) -> Self {
Self {
identifier,
label: Some(NameOrSupport::Name(name)),
children: None,
len,
}
}
pub fn internal(
identifier: usize,
children: Option<[Box<TreeNode>; 2]>,
len: Option<f64>,
support: Option<usize>,
) -> Self {
Self {
identifier,
children: Some(children.unwrap()),
len,
label: match support {
Some(s) => Some(NameOrSupport::Support(s)),
None => None,
},
}
}
fn to_newick_recursion(&self) -> String {
match &self.children {
Some([left, right]) => {
let left_part = match left.len {
Some(len) => {
let left_str = left.to_newick_recursion();
format!("{}:{:.3}", left_str, len)
}
None => left.to_newick_recursion(),
};
let right_part = match right.len {
Some(len) => {
let right_str = right.to_newick_recursion();
format!("{}:{:.3}", right_str, len)
}
None => right.to_newick_recursion(),
};
let label_str = match self.label {
Some(NameOrSupport::Support(s)) => format!("{}", s),
Some(NameOrSupport::Name(ref n)) => n.clone(),
None => "".to_string(),
};
format!("({},{}){}", left_part, right_part, label_str)
}
None => match self.label {
Some(NameOrSupport::Support(s)) => format!("{}", s),
Some(NameOrSupport::Name(ref n)) => n.clone(),
None => "".to_string(),
},
}
}
pub fn to_newick(&self) -> String {
let mut newick = self.to_newick_recursion();
newick.push(';');
newick
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_leaf_newick_with_branch_length() {
let node = TreeNode::leaf(0, "A".into(), Some(0.5));
assert_eq!(node.to_newick_recursion(), "A");
assert_eq!(node.to_newick(), "A;");
}
#[test]
fn test_leaf_newick_no_branch_length() {
let node = TreeNode::leaf(0, "X".into(), None);
assert_eq!(node.to_newick(), "X;");
}
#[test]
fn test_internal_node_branch_lengths_three_decimals() {
let left = TreeNode::leaf(0, "L".into(), Some(0.1234));
let right = TreeNode::leaf(1, "R".into(), Some(0.5678));
let root = TreeNode::internal(2, Some([Box::new(left), Box::new(right)]), Some(0.0), None);
let newick = root.to_newick();
assert!(newick.contains("L:0.123"), "got {newick}");
assert!(newick.contains("R:0.568"), "got {newick}");
assert!(newick.starts_with('('));
assert!(newick.ends_with(");"));
}
#[test]
fn test_internal_node_bootstrap_support_label() {
let left = TreeNode::leaf(0, "A".into(), Some(0.1));
let right = TreeNode::leaf(1, "B".into(), Some(0.2));
let root =
TreeNode::internal(2, Some([Box::new(left), Box::new(right)]), Some(0.0), Some(85));
let newick = root.to_newick();
assert!(newick.ends_with(")85;"), "got {newick}");
}
#[test]
fn test_internal_node_no_support_label() {
let left = TreeNode::leaf(0, "A".into(), Some(0.1));
let right = TreeNode::leaf(1, "B".into(), Some(0.2));
let root = TreeNode::internal(2, Some([Box::new(left), Box::new(right)]), Some(0.0), None);
let newick = root.to_newick();
assert!(newick.ends_with(");"), "got {newick}");
}
#[test]
fn test_nested_tree_newick() {
let a = TreeNode::leaf(0, "A".into(), Some(0.1));
let b = TreeNode::leaf(1, "B".into(), Some(0.2));
let ab = TreeNode::internal(3, Some([Box::new(a), Box::new(b)]), Some(0.3), None);
let c = TreeNode::leaf(2, "C".into(), Some(0.4));
let root =
TreeNode::internal(4, Some([Box::new(ab), Box::new(c)]), Some(0.0), None);
let newick = root.to_newick();
assert!(newick.contains("A:0.100"), "got {newick}");
assert!(newick.contains("B:0.200"), "got {newick}");
assert!(newick.contains("C:0.400"), "got {newick}");
assert!(newick.starts_with("(("), "got {newick}");
assert!(newick.ends_with(");"), "got {newick}");
}
#[test]
fn test_zero_branch_length_newick() {
let left = TreeNode::leaf(0, "A".into(), Some(0.0));
let right = TreeNode::leaf(1, "B".into(), Some(0.0));
let root = TreeNode::internal(2, Some([Box::new(left), Box::new(right)]), Some(0.0), None);
assert_eq!(root.to_newick(), "(A:0.000,B:0.000);");
}
}