use layout::SOA;
#[derive(Clone, SOA)]
#[layout(Clone)]
struct Particle {
x: f64,
y: f64,
mass: f64,
}
fn particle(i: usize) -> Particle {
Particle {
x: i as f64,
y: -(i as f64),
mass: (i % 7) as f64,
}
}
#[test]
fn from_iter_capacity_tracks_result_not_source() {
let src: ParticleVec = (0..10_000).map(particle).collect();
let kept: ParticleVec = src
.iter()
.filter(|p| *p.mass == 0.0)
.map(|p| p.to_owned())
.collect();
assert!(kept.len() <= 1500);
assert!(
kept.capacity() < 4096,
"over-allocated: len={} capacity={}",
kept.len(),
kept.capacity()
);
for p in kept.iter() {
assert_eq!(*p.mass, 0.0);
}
}
#[test]
fn compact_from_iter_capacity_tracks_result() {
use layout::{Compact, CompactVec};
let full: CompactVec<bool> = (0..100_000)
.map(|i| Compact::new(i % 10_000 == 0))
.collect();
let kept: CompactVec<bool> = full.iter().filter(|c| c.get()).collect();
assert!(kept.len() <= 20);
assert!(
kept.capacity() < 10_000,
"over-allocated: len={} capacity={}",
kept.len(),
kept.capacity()
);
assert!(kept.iter().all(|c| c.get()));
}
#[derive(SOA)]
struct Tagged {
id: u64,
flag: layout::Compact<bool>,
}
#[test]
fn capacity_returns_min_across_columns() {
use layout::Compact;
let mut v = TaggedVec::new();
for i in 0..1000u64 {
v.push(Tagged {
id: i,
flag: Compact::new(i % 2 == 0),
});
}
assert_eq!(v.capacity(), v.id.capacity().min(v.flag.capacity()));
}
#[test]
fn compact_bulk_ops_preserve_lanes() {
use layout::{Compact, CompactVec};
for n in [0usize, 1, 63, 64, 65, 200, 1000] {
let src: CompactVec<bool> =
(0..n).map(|i| Compact::new(i % 3 == 0)).collect();
let copied = src.as_slice().to_vec();
assert!(copied
.iter()
.map(|c| c.get())
.eq((0..n).map(|i| i % 3 == 0)));
let mut dst: CompactVec<bool> =
(0..7).map(|i| Compact::new(i % 2 == 0)).collect();
dst.extend_from_slice(src.as_slice());
assert_eq!(dst.len(), 7 + n);
for i in 0..n {
assert_eq!(dst.get(7 + i).unwrap().get(), i % 3 == 0);
}
if n > 0 {
let mut a = src.clone();
let at = n / 2;
let tail = a.split_off(at);
assert_eq!(a.len(), at);
assert_eq!(tail.len(), n - at);
for i in 0..at {
assert_eq!(a.get(i).unwrap().get(), i % 3 == 0);
}
for i in 0..(n - at) {
assert_eq!(tail.get(i).unwrap().get(), (at + i) % 3 == 0);
}
}
}
let mut v: CompactVec<bool> = CompactVec::new();
v.resize(100, Compact::new(true));
assert_eq!(v.len(), 100);
assert!(v.iter().all(|c| c.get()));
v.resize(10, Compact::new(false));
assert_eq!(v.len(), 10);
assert!(v.iter().all(|c| c.get()));
}
#[test]
fn compact_iter_matches_oracle_all_directions() {
use layout::{Compact, CompactVec};
for n in [0usize, 1, 63, 64, 65, 130, 200] {
let want: Vec<bool> =
(0..n).map(|i| i % 5 == 0 || i % 7 == 0).collect();
let v: CompactVec<bool> =
want.iter().map(|&b| Compact::new(b)).collect();
assert!(v.iter().map(|c| c.get()).eq(want.iter().copied()));
assert!(v
.iter()
.rev()
.map(|c| c.get())
.eq(want.iter().rev().copied()));
let mut it = v.iter();
let mut front = Vec::new();
let mut back = Vec::new();
while let Some(c) = it.next() {
front.push(c.get());
if let Some(c) = it.next_back() {
back.push(c.get());
}
}
front.extend(back.into_iter().rev());
assert_eq!(front, want, "interleaved n={n}");
}
}
#[derive(Clone, SOA)]
#[layout(Clone)]
struct Item {
key: u32,
seq: u32,
}
#[test]
fn sort_by_key_orders_and_is_stable() {
let mut v = ItemVec::new();
for i in 0..1000u32 {
v.push(Item {
key: i % 10,
seq: i,
});
}
v.as_mut_slice().sort_by_key(|r| *r.key);
let mut prev_key = 0u32;
let mut last_seq_for_key = [None::<u32>; 10];
for r in v.iter() {
let (k, s) = (*r.key, *r.seq);
assert!(k >= prev_key, "not ordered by key");
prev_key = k;
if let Some(prev_seq) = last_seq_for_key[k as usize] {
assert!(
s > prev_seq,
"unstable within key {k}: {s} after {prev_seq}"
);
}
last_seq_for_key[k as usize] = Some(s);
}
}
#[test]
fn compact_eq_ignores_stale_tail_bits() {
use layout::{Compact, CompactVec};
for n in [0usize, 1, 64, 65, 200] {
let a: CompactVec<bool> =
(0..n).map(|i| Compact::new(i % 3 == 0)).collect();
let mut b: CompactVec<bool> =
(0..n + 40).map(|i| Compact::new(i % 3 == 0)).collect();
b.truncate(n);
assert_eq!(a, b);
assert_eq!(a.as_slice(), b.as_slice());
if n > 0 {
let mut c = a.clone();
let mid = n / 2;
let old = c.get(mid).unwrap().get();
c.set(mid, Compact::new(!old));
assert_ne!(a, c);
}
}
}