mod common;
use common::load_vectors_f64;
use approx::assert_abs_diff_eq;
use exg::fiff::raw::open_raw;
use std::path::Path;
const FIF_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/data/sample1_raw.fif");
fn fif_available() -> bool {
Path::new(FIF_PATH).exists()
}
#[test]
fn info_nchan() {
if !fif_available() { return; }
let vecs = load_vectors_f64("fiff_sample_info");
let expected = vecs.get("nchan").unwrap()[[0]] as i32;
let raw = open_raw(FIF_PATH).unwrap();
assert_eq!(raw.info.n_chan as i32, expected);
}
#[test]
fn info_sfreq() {
if !fif_available() { return; }
let vecs = load_vectors_f64("fiff_sample_info");
let expected = vecs.get("sfreq").unwrap()[[0]];
let raw = open_raw(FIF_PATH).unwrap();
assert_abs_diff_eq!(raw.info.sfreq, expected, epsilon = 1e-6);
}
#[test]
fn info_first_last_samp() {
if !fif_available() { return; }
let vecs = load_vectors_f64("fiff_sample_info");
let expected_first = vecs.get("first_samp").unwrap()[[0]] as i64;
let expected_ntimes = vecs.get("ntimes").unwrap()[[0]] as i64;
let raw = open_raw(FIF_PATH).unwrap();
assert_eq!(raw.first_samp as i64, expected_first);
assert_eq!(raw.n_times() as i64, expected_ntimes);
assert_eq!(raw.last_samp as i64, expected_first + expected_ntimes - 1);
}
#[test]
fn info_channel_names() {
if !fif_available() { return; }
let vecs = load_vectors_f64("fiff_sample_info");
let names_raw = vecs.get("ch_names").unwrap();
let raw = open_raw(FIF_PATH).unwrap();
let n_ch = raw.info.n_chan;
let ref_names: Vec<String> = (0..n_ch)
.map(|c| {
let bytes: Vec<u8> = (0..16_usize)
.map(|b| names_raw[[c, b]] as u8)
.take_while(|&b| b != 0)
.collect();
String::from_utf8_lossy(&bytes).into_owned()
})
.collect();
for (i, (got, exp)) in raw.info.ch_names().iter().zip(ref_names.iter()).enumerate() {
assert_eq!(*got, exp.as_str(), "ch[{i}] name mismatch");
}
}
#[test]
fn info_calibrations() {
if !fif_available() { return; }
let vecs = load_vectors_f64("fiff_sample_info");
let ref_cal = vecs.get("ch_cal").unwrap();
let ref_range = vecs.get("ch_range").unwrap();
let raw = open_raw(FIF_PATH).unwrap();
for (i, ch) in raw.info.chs.iter().enumerate() {
assert_abs_diff_eq!(ch.cal as f64, ref_cal [[i]], epsilon = 1e-6_f64);
assert_abs_diff_eq!(ch.range as f64, ref_range[[i]], epsilon = 1e-6_f64);
}
}
#[test]
fn info_channel_kinds() {
if !fif_available() { return; }
let vecs = load_vectors_f64("fiff_sample_info");
let ref_kind = vecs.get("ch_kind").unwrap();
let raw = open_raw(FIF_PATH).unwrap();
for (i, ch) in raw.info.chs.iter().enumerate() {
assert_eq!(ch.kind, ref_kind[[i]] as i32, "ch[{}] kind", i);
}
}
#[test]
fn info_channel_locations() {
if !fif_available() { return; }
let vecs = load_vectors_f64("fiff_sample_info");
let ref_locs = vecs.get("ch_locs").unwrap(); let raw = open_raw(FIF_PATH).unwrap();
for (c, ch) in raw.info.chs.iter().enumerate() {
for i in 0..12 {
let got = ch.loc[i] as f64;
let exp = ref_locs[[c, i]];
if exp.is_nan() {
assert!(got.is_nan(), "ch[{}] loc[{}]: expected NaN, got {}", c, i, got);
} else {
assert_abs_diff_eq!(got, exp, epsilon = 1e-6_f64);
}
}
}
}
#[test]
fn data_shape_matches_mne() {
if !fif_available() { return; }
let vecs = load_vectors_f64("fiff_sample_data");
let ref_data = vecs.get("data").unwrap(); let raw = open_raw(FIF_PATH).unwrap();
let data = raw.read_all_data().unwrap();
assert_eq!(data.nrows(), ref_data.shape()[0], "n_channels");
assert_eq!(data.ncols(), ref_data.shape()[1], "n_times");
}
#[test]
fn data_all_samples_match_mne() {
if !fif_available() { return; }
let vecs = load_vectors_f64("fiff_sample_data");
let ref_data = vecs.get("data").unwrap();
let raw = open_raw(FIF_PATH).unwrap();
let data = raw.read_all_data().unwrap();
let n_ch = data.nrows();
let n_t = data.ncols();
let mut max_err = 0_f64;
let mut worst_c = 0;
let mut worst_t = 0;
for c in 0..n_ch {
for t in 0..n_t {
let err = (data[[c, t]] - ref_data[[c, t]]).abs();
if err > max_err {
max_err = err;
worst_c = c;
worst_t = t;
}
}
}
assert!(max_err < 1e-9,
"max error {max_err:.2e} at ch={worst_c} t={worst_t} \
(got={:.10e} ref={:.10e})",
data[[worst_c, worst_t]], ref_data[[worst_c, worst_t]]);
}
#[test]
fn data_first_second_sample_exact() {
if !fif_available() { return; }
let vecs_short = load_vectors_f64("fiff_sample_data_short");
let ref_256 = vecs_short.get("data").unwrap(); let raw = open_raw(FIF_PATH).unwrap();
let data = raw.read_slice(0, 256).unwrap();
for c in 0..12 {
for t in 0..256 {
let err = (data[[c, t]] - ref_256[[c, t]]).abs();
assert!(err < 1e-9,
"ch={} t={}: got={:.10e} ref={:.10e} err={:.2e}",
c, t, data[[c, t]], ref_256[[c, t]], err);
}
}
}
#[test]
fn read_slice_equals_subarray_of_read_all() {
if !fif_available() { return; }
let raw = open_raw(FIF_PATH).unwrap();
let all = raw.read_all_data().unwrap();
let slice = raw.read_slice(100, 600).unwrap(); for c in 0..12 {
for t in 0..500 {
assert_abs_diff_eq!(slice[[c, t]], all[[c, t + 100]], epsilon = 1e-14);
}
}
}
#[test]
fn buffer_boundaries_match_mne() {
if !fif_available() { return; }
let vecs = load_vectors_f64("fiff_sample_info");
let ref_bounds = vecs.get("buf_bounds").unwrap(); let raw = open_raw(FIF_PATH).unwrap();
let mut rust_bounds: Vec<u64> = Vec::new();
rust_bounds.push(raw.first_samp);
for buf in &raw.buffers {
rust_bounds.push(rust_bounds.last().copied().unwrap() + buf.n_samp as u64);
}
assert_eq!(rust_bounds.len(), ref_bounds.shape()[0],
"number of boundaries: Rust={} MNE={}", rust_bounds.len(), ref_bounds.shape()[0]);
for (i, (&rust_b, ref_b)) in rust_bounds.iter()
.zip(ref_bounds.iter().map(|&v| v as u64))
.enumerate()
{
assert_eq!(rust_b, ref_b, "bound[{}]: rust={} mne={}", i, rust_b, ref_b);
}
}
#[test]
fn data_statistics_match_mne() {
if !fif_available() { return; }
let vecs = load_vectors_f64("fiff_sample_data");
let ref_data = vecs.get("data").unwrap();
let raw = open_raw(FIF_PATH).unwrap();
let data = raw.read_all_data().unwrap();
let n = data.len() as f64;
let rust_mean = data.iter().sum::<f64>() / n;
let ref_mean = ref_data.iter().sum::<f64>() / n;
assert_abs_diff_eq!(rust_mean, ref_mean, epsilon = 1e-12);
let rust_max = data.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
let ref_max = ref_data.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
assert_abs_diff_eq!(rust_max, ref_max, epsilon = 1e-10);
}
#[test]
fn read_slice_boundary_wraps_buffers() {
if !fif_available() { return; }
let raw = open_raw(FIF_PATH).unwrap();
let all = raw.read_all_data().unwrap();
let slice = raw.read_slice(200, 320).unwrap();
for c in 0..12 {
for t in 0..120 {
assert_abs_diff_eq!(slice[[c, t]], all[[c, t + 200]], epsilon = 1e-14);
}
}
}