Branch lengths that are not finite are rejected. A:1e910 parses as infinity rather than failing, and an infinite depth made every distance inf with NaN down the diagonal, reported with a success exit code. The run also stops when finite depths are large enough that d_i + d_j overflows
A leaf label containing a space is rejected in --lower mode: PHYLIP readers treat whitespace as the end of the name, so every row after it landed a column out
--lower with --lmm now warns that the omitted diagonal holds each leaf's root-to-tip length rather than zeros
Parallel computation now speeds the run up instead of slowing it down. One parallel job per row could not pay for synchronising the thread pool, and float formatting sat in the serial write loop; rows are now batched and each worker formats its own. A 20,000-tip matrix went from 10.6 s to 1.6 s, and an 8,000-tip one now scales from 1.26 s on one core to 0.21 s on eight
Trees prefixed with a [&R] / [&U] rooting marker, or carrying a comment before a label, no longer fail to parse
Non-ASCII leaf labels (accents, Greek, CJK) are preserved instead of being mangled into mojibake
Truncated trees, trailing content, unclosed comments and free-form text are rejected instead of yielding a plausible but wrong matrix
Files holding more than one tree are rejected instead of silently using the first
Apostrophes in unquoted labels (O'Brien) no longer break parsing, and a doubled quote keeps the whitespace around it
Deeply nested trees no longer overflow the stack when the parse tree is freed
-o FILE no longer truncates an existing file before the tree has parsed
Write failures are reported instead of being discarded along with a success exit code; a closed pipe exits quietly
--precision out of range is rejected instead of panicking inside the formatter
--threads 0 is rejected instead of quietly starting one thread per core
Newlines and carriage returns in labels are rejected like tabs, and unlabeled leaves are reported rather than dropped in silence
--midpoint no longer adds a hop to every topological distance crossing the midpoint edge
Negative patristic distances are reported as computed instead of being rounded up to zero, which claimed distinct taxa were identical
Midpoint rooting (--midpoint) rewritten to fix infinite loop caused by cyclic graph construction
NHX/bracket comment annotations (e.g., [&&NHX:S=human]) no longer crash the parser
Single-quoted labels (e.g., 'Taxon A') now parsed correctly
Double-quoted labels (e.g., "Taxon A") now parsed correctly
Whitespace and newlines in Newick strings no longer crash the parser
Stack overflow on deeply nested trees (>5,000 levels) — parser and flattener rewritten iteratively
Duplicate leaf names are now detected with a clear error message
--lmm --topology conflict now warns instead of silently choosing LMM
Topology mode now outputs integers instead of float decimals
Floating point output uses configurable precision instead of raw representation
Header row always prints leading tab for R/Python distance matrix compatibility
--precision / -p flag to control decimal places in output (default: 10)
-t / --threads flag to control number of parallel threads
Stdin support: use - as the phylogeny argument to read from stdin
Warning when no branch lengths are detected in patristic mode
Warning when negative branch lengths are found in the tree
--npy writes the matrix as a NumPy array of 64-bit floats instead of text: exact, half the size, and 2.6x faster. With --lower it writes the condensed vector SciPy reads. Labels go to <FILE>.labels.txt
--taxa FILE restricts the matrix to a list of leaf labels, keeping the distances the full tree gives rather than pruning it
--stats prints a summary to stderr: leaves, nodes, mode, cells, and the minimum, maximum and mean off the diagonal
Gzipped input is read directly, detected by content rather than by file name, so it works from stdin too
CITATION.cff with DOI
MkDocs Material documentation site with a tutorial, published to GitHub Pages
Comprehensive test suite (96 tests), including randomised checks of midpoint rooting and of MRCA queries against a brute-force walk
Codebase split into modules: parser.rs, tree.rs, lca.rs, midpoint.rs
Fixed-precision float formatting is done by hand where the rounding is unambiguous and handed to the standard formatter where it is not, which cut a text run by a third to a half with byte-identical output
Cross-validated against R's ape (cophenetic.phylo, vcv.phylo) and phangorn::midpoint over 250 generated trees; reference matrices for six of them are committed so cargo test checks the same thing without R
The parser and the whole pipeline are fuzzed, in the test suite on every push and with cargo fuzz for longer runs
The tree logic moved into a library target, so it can be fuzzed, documented and used from other Rust code
LCA binary-lifting table stores plain usize rather than Option<usize>, halving the memory it needs
Output buffer raised from 8 KB to 1 MB, so a multi-gigabyte matrix is not hundreds of thousands of write syscalls
The input text and the recursive parse tree are released once the flat node array is built, rather than held to the end of the run
Version string now derived from Cargo.toml via env!("CARGO_PKG_VERSION")
Removed unused --format flag
Fixed clippy warnings (&Vec<Node> → &[Node])
Fixed contributors link in README
README output examples replaced with the real output of a worked example; the previous ones lost their header row to the code fence and the topological matrix was not a realisable tree
CI runs on every branch, lints the test code, and runs the test suite in release as well as debug