use proptest::prelude::*;
use super::*;
#[test]
fn streaming_fit_matches_iterator_fit() {
let a = seq(2, 2, vec![0.0, 10.0, 2.0, 20.0]);
let b = seq(1, 2, vec![4.0, 30.0]);
let buffered = LatentNorm::fit([&a, &b], LatentNorm::DEFAULT_EPS).unwrap();
let mut fit = NormFitter::new();
fit.push(&a).unwrap();
fit.push(&b).unwrap();
let streamed = fit.finish().unwrap();
assert_eq!(streamed, buffered);
}
#[test]
fn dim_and_is_empty_track_state() {
let mut fit = NormFitter::new();
assert!(fit.is_empty());
assert_eq!(fit.dim(), None);
let latent = seq(2, 3, vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0]);
fit.push(&latent).unwrap();
assert!(!fit.is_empty());
assert_eq!(fit.dim(), Some(3));
}
#[test]
fn push_fixes_dimension_then_rejects_mismatch() {
let a = seq(1, 2, vec![0.0, 0.0]);
let b = seq(1, 3, vec![0.0, 0.0, 0.0]);
let mut fit = NormFitter::new();
fit.push(&a).unwrap();
assert!(
matches!(fit.push(&b), Err(Error::Shape { expected, actual })
if expected == vec![1, 2] && actual == vec![1, 3])
);
}
#[test]
fn failed_push_leaves_builder_unchanged() {
let a2 = seq(1, 2, vec![0.0, 0.0]);
let mismatched = seq(1, 3, vec![0.0, 0.0, 0.0]);
let mut fit = NormFitter::new();
assert!(fit.push(&mismatched).is_ok());
fit.push(&mismatched).unwrap();
let snapshot = fit.clone().finish().unwrap();
assert!(fit.push(&a2).is_err());
let after = fit.finish().unwrap();
assert_eq!(after, snapshot);
}
#[test]
fn finish_rejects_empty_or_frameless_builder() {
assert!(matches!(
NormFitter::new().finish(),
Err(Error::Validation(_))
));
let empty = seq(0, 2, vec![]);
let mut fit = NormFitter::new();
fit.push(&empty).unwrap();
assert!(matches!(fit.finish(), Err(Error::Validation(_))));
}
proptest! {
#[test]
fn streaming_equals_buffered_prop(
flat in prop::collection::vec(-1.0e3f32..1.0e3, 2..64),
split in 1usize..8,
) {
let dim = 2usize;
let rows = flat.len() / dim;
prop_assume!(rows >= 1);
let data = flat[..rows * dim].to_vec();
let chunk = rows.div_ceil(split).max(1);
let mut latents = Vec::new();
let mut f = 0;
while f < rows {
let take = chunk.min(rows - f);
let slice = data[f * dim..(f + take) * dim].to_vec();
latents.push(seq(take, dim, slice));
f += take;
}
let buffered = LatentNorm::fit(latents.iter(), LatentNorm::DEFAULT_EPS).unwrap();
let mut fit = NormFitter::new();
for latent in &latents {
fit.push(latent).unwrap();
}
let streamed = fit.finish().unwrap();
prop_assert_eq!(streamed, buffered);
}
}