use std::num::NonZeroU64;
use molly::selection::{AtomSelection as AS, FrameSelection as FS, Range};
mod common;
use common::trajectories;
const PATH: &str = trajectories::SMOL;
fn count_frames(
reader: &mut molly::XTCReader<std::fs::File>,
frames: &mut Vec<molly::Frame>,
frame_selection: FS,
atom_selection: AS,
) -> std::io::Result<usize> {
reader.read_frames::<true>(frames, &frame_selection, &atom_selection)?;
let nframes = frames.len();
frames.clear();
reader.home()?;
Ok(nframes)
}
fn count_atoms(
reader: &mut molly::XTCReader<std::fs::File>,
atom_selection: AS,
) -> std::io::Result<usize> {
let mut frame = molly::Frame::default();
reader.read_frame_with_selection(&mut frame, &atom_selection)?;
reader.home()?;
let is_nan = |p: molly::Position| p.into_iter().any(f32::is_nan);
assert_eq!(frame.coords().position(is_nan), None);
assert_eq!(frame.coords().filter(|&p| is_nan(p)).count(), 0);
Ok(frame.coords().count())
}
macro_rules! assert_frames {
($frame_selection:expr, $atom_selection:expr => $expected:expr) => {{
let mut reader = molly::XTCReader::open(&PATH)?;
let mut frames = Vec::new();
assert_eq!(
count_frames(&mut reader, &mut frames, $frame_selection, $atom_selection)?,
$expected
);
Ok::<(), std::io::Error>(())
}};
}
macro_rules! assert_atoms {
($atom_selection:expr => $expected:expr) => {{
let mut reader = molly::XTCReader::open(&PATH)?;
assert_eq!(count_atoms(&mut reader, $atom_selection)?, $expected);
Ok::<(), std::io::Error>(())
}};
}
mod frame_selection {
use super::*;
const NFRAMES: usize = 1001;
#[test]
fn all_frames() -> std::io::Result<()> {
assert_frames!(FS::All, AS::All => NFRAMES)
}
#[test]
fn all_frames_with_atom_selection() -> std::io::Result<()> {
assert_frames!(FS::All, AS::Until(100) => NFRAMES)
}
#[test]
fn range_all() -> std::io::Result<()> {
assert_frames!(FS::Range(Range::new(None, None, None)), AS::All => NFRAMES)
}
#[test]
fn range_half() -> std::io::Result<()> {
assert_frames!(FS::Range(Range::new(None, None, NonZeroU64::new(2))), AS::All => 501)
}
#[test]
fn range_huge_step() -> std::io::Result<()> {
assert_frames!(FS::Range(Range::new(None, None, NonZeroU64::new(2000))), AS::All => 1)
}
#[test]
fn range_last_frames() -> std::io::Result<()> {
assert_frames!(FS::Range(Range::new(Some(NFRAMES as u64 - 20), None, None)), AS::All => 20)
}
#[test]
fn range_last_frames_step() -> std::io::Result<()> {
assert_frames!(FS::Range(Range::new(Some(981), None, NonZeroU64::new(3))), AS::All => 7)
}
#[test]
fn range_clamped() -> std::io::Result<()> {
assert_frames!(FS::Range(Range::new(Some(500), Some(750), None)), AS::All => 250)
}
#[test]
fn range_clamped_step() -> std::io::Result<()> {
assert_frames!(FS::Range(Range::new(Some(500), Some(750), NonZeroU64::new(5))), AS::All => 50)
}
#[test]
fn range_clamped_step_3() -> std::io::Result<()> {
assert_frames!(FS::Range(Range::new(Some(25), Some(50), NonZeroU64::new(3))), AS::All => 9)
}
#[test]
fn indices() -> std::io::Result<()> {
assert_frames!(FS::framelist_from_iter([0, 1, 500]), AS::All => 3)
}
#[test]
fn indices_first_frame() -> std::io::Result<()> {
assert_frames!(FS::framelist_from_iter([0]), AS::All =>1)
}
#[test]
fn indices_single_frame() -> std::io::Result<()> {
assert_frames!(FS::framelist_from_iter([100]), AS::All => 1)
}
#[test]
fn indices_last_frame() -> std::io::Result<()> {
assert_frames!(FS::framelist_from_iter([NFRAMES - 1]), AS::All => 1)
}
#[test]
fn indices_after_last_frame() -> std::io::Result<()> {
assert_frames!(FS::framelist_from_iter([NFRAMES]), AS::All => 0)
}
#[test]
fn indices_within_range_and_outside() -> std::io::Result<()> {
assert_frames!(FS::framelist_from_iter([0, 1, 500, NFRAMES * 2]), AS::All => 3)
}
#[test]
fn indices_empty_list() -> std::io::Result<()> {
assert_frames!(FS::FrameList(Default::default()), AS::All => 0)
}
}
mod atom_selection {
use super::*;
const NATOMS: usize = 24316;
#[test]
fn all_atoms() -> std::io::Result<()> {
assert_atoms!(AS::All => NATOMS)
}
#[test]
fn until_zero() -> std::io::Result<()> {
assert_atoms!(AS::Until(0) => 0)
}
#[test]
fn until_first() -> std::io::Result<()> {
assert_atoms!(AS::Until(1) => 1)
}
#[test]
fn until_half() -> std::io::Result<()> {
assert_atoms!(AS::Until(NATOMS as u32 / 2) => NATOMS / 2)
}
#[test]
fn until_up_to_end() -> std::io::Result<()> {
assert_atoms!(AS::Until(NATOMS as u32) => NATOMS )
}
#[test]
fn until_just_beyond() -> std::io::Result<()> {
assert_atoms!(AS::Until(NATOMS as u32 + 1) => NATOMS)
}
#[test]
fn until_far_beyond() -> std::io::Result<()> {
assert_atoms!(AS::Until(NATOMS as u32 + 1000) => NATOMS)
}
#[test]
fn indices() -> std::io::Result<()> {
assert_atoms!(AS::from_index_list(&[0, 1, 500]) => 3)
}
#[test]
fn indices_empty_list() -> std::io::Result<()> {
assert_atoms!(AS::from_index_list(&[]) => 0)
}
#[test]
fn indices_first_atom() -> std::io::Result<()> {
assert_atoms!(AS::from_index_list(&[0]) => 1)
}
#[test]
fn indices_single_atom() -> std::io::Result<()> {
assert_atoms!(AS::from_index_list(&[100]) => 1)
}
#[test]
fn indices_last_atom() -> std::io::Result<()> {
assert_atoms!(AS::from_index_list(&[NATOMS as u32 - 1]) => 1)
}
#[test]
fn indices_after_last_atom() -> std::io::Result<()> {
assert_atoms!(AS::from_index_list(&[NATOMS as u32]) => 0)
}
#[test]
fn indices_far_beyond_last_atom() -> std::io::Result<()> {
assert_atoms!(AS::from_index_list(&[NATOMS as u32 + 1000]) => 0)
}
#[test]
fn indices_within_range_and_outside() -> std::io::Result<()> {
assert_atoms!(AS::from_index_list(&[0, 1, 500, NATOMS as u32 + 1000]) => 3)
}
#[test]
fn mask() -> std::io::Result<()> {
assert_atoms!(AS::Mask(vec![true, false, false, true, false, true]) => 3)
}
#[test]
fn mask_empty_list() -> std::io::Result<()> {
assert_atoms!(AS::Mask(vec![]) => 0)
}
#[test]
fn mask_first_atom() -> std::io::Result<()> {
assert_atoms!(AS::Mask(vec![true]) => 1)
}
#[test]
fn mask_single_atom() -> std::io::Result<()> {
assert_atoms!(AS::Mask([vec![false; 100], vec![true]].concat()) => 1)
}
#[test]
fn mask_last_atom() -> std::io::Result<()> {
let n = NATOMS;
let mut mask = vec![false; n];
mask[n - 1] = true;
assert_atoms!(AS::Mask(mask) => 1)
}
#[test]
fn mask_after_last_atom() -> std::io::Result<()> {
let n = NATOMS + 1;
let mut mask = vec![false; n];
mask[n - 1] = true;
assert_atoms!(AS::Mask(mask) => 0)
}
#[test]
fn mask_far_beyond_last_atom() -> std::io::Result<()> {
let n = NATOMS + 1000;
let mut mask = vec![false; n];
mask[n - 1] = true;
assert_atoms!(AS::Mask(mask) => 0)
}
#[test]
fn mask_within_range_and_outside() -> std::io::Result<()> {
let n = NATOMS + 1000;
let mut mask = vec![false; n];
mask[0] = true;
mask[1] = true;
mask[500] = true;
mask[n - 500] = true;
mask[n - 1] = true;
assert_atoms!(AS::Mask(mask) => 3)
}
}