use std::collections::{HashMap, HashSet};
#[derive(Debug, PartialEq)]
pub(crate) struct State {
pub(crate) primary_active: usize,
pub(crate) active: usize,
pub(crate) sizes: Vec<(usize, usize)>,
}
#[derive(Debug)]
pub(crate) struct Items {
pub(crate) list: Vec<usize>,
pub(crate) sets: Vec<usize>,
pub(crate) primary_items: HashSet<usize>,
pub(crate) active: usize,
pub(crate) primary_active: usize,
pub(crate) previous_active: usize,
}
impl Items {
pub(crate) fn new(
names: &Vec<&String>,
sizes: &HashMap<&String, usize>,
primary: &HashSet<&String>,
) -> Self {
let pos = 2;
let mut col_ix = pos;
let mut list: Vec<usize> = Vec::new();
let mut primary_items = HashSet::new();
let mut sets: Vec<usize> = Vec::new();
for i in 0..names.len() {
let item = names[i];
list.push(col_ix);
if primary.contains(item) {
primary_items.insert(col_ix);
}
col_ix += pos + sizes[item];
sets.push(i);
sets.push(sizes[item]);
for _ in 0..sizes[item] {
sets.push(0);
}
}
let active = list.len();
let previous_active = list.len();
let primary_active = primary_items.len();
Self {
list,
sets,
primary_items,
active,
previous_active,
primary_active,
}
}
pub(crate) fn is_primary(&self, item: &usize) -> bool {
self.primary_items.contains(&item)
}
pub(crate) fn get_state(&self) -> State {
let mut sizes = vec![];
for item_ix in 0..self.active {
sizes.push((self.list[item_ix], self.get_size(&self.list[item_ix])));
}
State {
sizes,
active: self.active,
primary_active: self.primary_active,
}
}
pub(crate) fn reset(&mut self, state: &State) {
self.active = state.active;
self.primary_active = state.primary_active;
for (item, size) in &state.sizes {
self.set_size(item, *size);
}
}
pub(crate) fn get_position(&self, item: &usize) -> &usize {
&self.sets[item - 2]
}
pub(crate) fn swap_position(&mut self, item_a: &usize, item_b: &usize) {
self.sets.swap(item_a - 2, item_b - 2);
}
pub(crate) fn get_size(&self, item: &usize) -> usize {
self.sets[item - 1]
}
pub(crate) fn set_size(&mut self, item: &usize, size: usize) {
self.sets[item - 1] = size;
}
pub(crate) fn is_active(&self, item: &usize) -> bool {
self.get_position(item) < &self.active
}
pub(crate) fn was_active(&self, item: &usize) -> bool {
self.get_position(item) < &self.previous_active
}
pub(crate) fn solution_found(&self) -> bool {
self.primary_active == 0
}
fn is_last_active(&self, item: &usize) -> bool {
item == &self.list[self.active - 1]
}
pub(crate) fn deactivate_item(&mut self, item: &usize) {
if self.is_active(item) {
if !self.is_last_active(item) {
let item_ix = *self.get_position(item);
let last_active = self.list[self.active - 1];
self.list.swap(item_ix, self.active - 1);
self.swap_position(&item, &last_active);
}
self.active -= 1;
if self.is_primary(item) {
self.primary_active -= 1;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn get_items() -> Items {
let s: Vec<String> = vec!["i0", "i1", "i2", "i3"]
.iter()
.map(|x| x.to_string())
.collect();
let names = vec![&s[1], &s[2], &s[3]];
let sizes: HashMap<&String, usize> = [(&s[1], 1), (&s[2], 2), (&s[3], 3)].into();
let primary: HashSet<&String> = [&s[1], &s[2]].into();
Items::new(&names, &sizes, &primary)
}
#[test]
fn test_items() {
let items = get_items();
assert_eq!(items.list, [2, 5, 9]);
assert_eq!(items.sets, [0, 1, 0, 1, 2, 0, 0, 2, 3, 0, 0, 0]);
assert_eq!(items.primary_items, [2, 5].into());
assert_eq!(items.active, 3);
assert_eq!(items.previous_active, 3);
assert_eq!(items.primary_active, 2);
let last_item = items.list.last().unwrap();
assert_eq!(items.get_position(last_item), &2);
assert_eq!(items.get_size(last_item), 3);
assert!(items.is_active(last_item));
assert!(items.was_active(last_item));
assert!(!items.solution_found());
assert_eq!(
items.get_state(),
State {
sizes: vec![(2, 1), (5, 2), (9, 3)],
primary_active: 2,
active: 3
}
)
}
#[test]
fn test_deactivate_last_item() {
let mut items = get_items();
let last_item = *items.list.last().unwrap();
items.deactivate_item(&last_item);
assert_eq!(items.list, [2, 5, 9]);
assert_eq!(items.sets, [0, 1, 0, 1, 2, 0, 0, 2, 3, 0, 0, 0]);
assert_eq!(items.primary_items, [2, 5].into());
assert_eq!(items.active, 2);
assert_eq!(items.previous_active, 3);
assert_eq!(items.primary_active, 2);
}
#[test]
fn test_deactivate_first_item() {
let mut items = get_items();
let first_item = *items.list.first().unwrap();
items.deactivate_item(&first_item);
assert_eq!(items.list, [9, 5, 2]);
assert_eq!(items.sets, [2, 1, 0, 1, 2, 0, 0, 0, 3, 0, 0, 0]);
assert_eq!(items.primary_items, [2, 5].into());
assert_eq!(items.active, 2);
assert_eq!(items.previous_active, 3);
assert_eq!(items.primary_active, 1);
}
#[test]
fn test_get_state_reset_state() {
let mut items = get_items();
let state = items.get_state();
let first_item = *items.list.first().unwrap();
items.deactivate_item(&first_item);
assert_eq!(
items.get_state(),
State {
sizes: vec![(9, 3), (5, 2)],
primary_active: 1,
active: 2
}
);
let current_last_item = items.list[1];
assert_eq!(current_last_item, 5);
items.set_size(¤t_last_item, 1);
assert_eq!(
items.get_state(),
State {
sizes: vec![(9, 3), (5, 1)],
primary_active: 1,
active: 2
}
);
items.reset(&state);
assert_eq!(
items.get_state(),
State {
sizes: vec![(9, 3), (5, 2), (2, 1)],
primary_active: 2,
active: 3
}
);
}
}