use super::{
EvaluatedInput, MultiIndex, Strategy, Tuple, index_fn_dim, index_fn_size,
};
use crate::iteration::comprehension::metadata::IndexFn;
use crate::iteration::comprehension::strategy::StrategyName;
pub struct Lex;
impl Strategy for Lex {
fn name(&self) -> StrategyName {
StrategyName::Lex
}
fn accepts_input(&self, _idx: Option<&IndexFn>) -> bool {
true
}
fn has_closed_form_for(&self, _idx: &IndexFn) -> bool {
true
}
fn apply(&self, input: &EvaluatedInput, truncation: Option<u64>) -> Vec<Tuple> {
match truncation {
Some(n) => input.tuples.iter().take(n as usize).cloned().collect(),
None => input.tuples.clone(),
}
}
}
pub(crate) fn lex_multi_indices(idx: &IndexFn, truncation: Option<u64>) -> Vec<MultiIndex> {
let total = index_fn_size(idx);
let n = match truncation {
Some(t) => t.min(total),
None => total,
};
let dim = index_fn_dim(idx);
let axis_sizes: Vec<u64> = match idx {
IndexFn::Lattice { axis_sizes } | IndexFn::Modular { axis_sizes } => axis_sizes.clone(),
IndexFn::Lockstep { length } => vec![*length],
IndexFn::Concatenation { segment_sizes } => vec![segment_sizes.iter().sum()],
IndexFn::Continuous { .. } | IndexFn::Hybrid { .. } => return Vec::new(),
};
let mut out = Vec::with_capacity(n as usize);
let mut cursor: Vec<u64> = vec![0; dim];
for _ in 0..n {
out.push(cursor.clone());
for i in (0..dim).rev() {
cursor[i] += 1;
if cursor[i] < axis_sizes[i] {
break;
}
cursor[i] = 0;
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::iteration::comprehension::strategies::TupleValue;
fn tup(k: i64) -> Tuple {
Tuple::new().with("k", TupleValue::I64(k))
}
fn input_with(tuples: Vec<Tuple>, axis_sizes: Vec<u64>) -> EvaluatedInput {
let card = tuples.len() as u64;
EvaluatedInput {
tuples,
cardinality: card,
index_fn: IndexFn::Lattice { axis_sizes },
}
}
#[test]
fn apply_is_identity_without_truncation() {
let inp = input_with(vec![tup(1), tup(2), tup(3)], vec![3]);
let result = Lex.apply(&inp, None);
assert_eq!(result, inp.tuples);
}
#[test]
fn lex_multi_indices_is_mixed_radix_order() {
let idx = IndexFn::Lattice { axis_sizes: vec![2, 3] };
let out = lex_multi_indices(&idx, None);
assert_eq!(
out,
vec![
vec![0, 0], vec![0, 1], vec![0, 2],
vec![1, 0], vec![1, 1], vec![1, 2],
]
);
}
#[test]
fn apply_truncates() {
let inp = input_with(vec![tup(1), tup(2), tup(3), tup(4)], vec![4]);
let result = Lex.apply(&inp, Some(2));
assert_eq!(result.len(), 2);
assert_eq!(result[0], tup(1));
assert_eq!(result[1], tup(2));
}
#[test]
fn lex_multi_indices_2d() {
let idx = IndexFn::Lattice { axis_sizes: vec![2, 3] };
let result = lex_multi_indices(&idx, None);
assert_eq!(result.len(), 6);
assert_eq!(result[0], vec![0, 0]);
assert_eq!(result[1], vec![0, 1]);
assert_eq!(result[2], vec![0, 2]);
assert_eq!(result[3], vec![1, 0]);
assert_eq!(result[4], vec![1, 1]);
assert_eq!(result[5], vec![1, 2]);
}
#[test]
fn lex_multi_indices_respects_truncation() {
let idx = IndexFn::Lattice { axis_sizes: vec![3, 3] };
let result = lex_multi_indices(&idx, Some(4));
assert_eq!(result.len(), 4);
assert_eq!(result[3], vec![1, 0]);
}
#[test]
fn accepts_anything() {
assert!(Lex.accepts_input(None));
assert!(Lex.accepts_input(Some(&IndexFn::Lattice { axis_sizes: vec![3] })));
}
}