use crate::real::Real;
pub(crate) const NIL: usize = usize::MAX;
pub(crate) const LINK_MIN: usize = 16;
#[derive(Clone, Debug)]
pub(crate) struct LineStore<T: Real> {
start: Vec<usize>,
len: Vec<usize>,
cap: Vec<usize>,
idx: Vec<u32>,
val: Vec<T>,
link: Vec<u32>,
linked: bool,
has_val: bool,
live: usize,
garbage: usize,
}
impl<T: Real> Default for LineStore<T> {
fn default() -> Self {
Self {
start: Vec::new(),
len: Vec::new(),
cap: Vec::new(),
idx: Vec::new(),
val: Vec::new(),
link: Vec::new(),
linked: false,
has_val: false,
live: 0,
garbage: 0,
}
}
}
impl<T: Real> LineStore<T> {
pub(crate) fn new(caps: &[usize]) -> Self {
let mut start = Vec::with_capacity(caps.len());
let mut s = 0;
for &c in caps {
start.push(s);
s += c;
}
Self {
start,
len: vec![0; caps.len()],
cap: caps.to_vec(),
idx: vec![0; s],
val: vec![T::ZERO; s],
link: Vec::new(),
linked: false,
has_val: true,
live: 0,
garbage: 0,
}
}
pub(crate) fn new_linked(caps: &[usize]) -> Self {
let mut s = Self::new(caps);
s.link = vec![0; s.idx.len()];
s.linked = true;
s
}
pub(crate) fn new_pattern_linked(caps: &[usize]) -> Self {
let mut s = Self::new_linked(caps);
s.val = Vec::new();
s.has_val = false;
s
}
#[inline]
pub(crate) fn indices(&self, l: usize) -> &[u32] {
&self.idx[self.start[l]..self.start[l] + self.len[l]]
}
#[inline]
pub(crate) fn link(&self, l: usize, pos: usize) -> usize {
debug_assert!(pos < self.len[l]);
self.link[self.start[l] + pos] as usize
}
#[inline]
fn set_link(&mut self, l: usize, pos: usize, t: usize) {
debug_assert!(pos < self.len[l]);
self.link[self.start[l] + pos] = u32::try_from(t).expect("position exceeds u32::MAX");
}
#[inline]
pub(crate) fn index_at(&self, l: usize, pos: usize) -> u32 {
debug_assert!(pos < self.len[l]);
self.idx[self.start[l] + pos]
}
#[inline]
pub(crate) fn value_at(&self, l: usize, pos: usize) -> T {
debug_assert!(pos < self.len[l] && self.has_val);
self.val[self.start[l] + pos]
}
#[inline]
pub(crate) fn find(&self, l: usize, i: u32, t: usize) -> Option<usize> {
if t < self.len[l] && self.idx[self.start[l] + t] == i {
Some(t)
} else {
self.position(l, i)
}
}
pub(crate) fn push_linked(&mut self, l: usize, i: u32, v: T, t: usize) {
debug_assert!(self.linked);
debug_assert!(
self.position(l, i).is_none(),
"duplicate index {i} in line {l}"
);
if self.len[l] == self.cap[l] {
self.grow(l);
}
let p = self.start[l] + self.len[l];
self.idx[p] = i;
if self.has_val {
self.val[p] = v;
}
self.link[p] = u32::try_from(t).expect("position exceeds u32::MAX");
self.len[l] += 1;
self.live += 1;
}
#[inline]
pub(crate) fn live(&self) -> usize {
self.live
}
#[inline]
pub(crate) fn len(&self, l: usize) -> usize {
self.len[l]
}
#[inline]
pub(crate) fn line(&self, l: usize) -> (&[u32], &[T]) {
debug_assert!(self.has_val);
let r = self.start[l]..self.start[l] + self.len[l];
(&self.idx[r.clone()], &self.val[r])
}
#[inline]
pub(crate) fn values_mut(&mut self, l: usize) -> &mut [T] {
debug_assert!(self.has_val);
let r = self.start[l]..self.start[l] + self.len[l];
&mut self.val[r]
}
#[inline]
pub(crate) fn position(&self, l: usize, i: u32) -> Option<usize> {
self.indices(l).iter().position(|&x| x == i)
}
#[inline]
pub(crate) fn get(&self, l: usize, i: u32) -> Option<T> {
self.position(l, i).map(|p| self.line(l).1[p])
}
pub(crate) fn push(&mut self, l: usize, i: u32, v: T) {
debug_assert!(
self.position(l, i).is_none(),
"duplicate index {i} in line {l}"
);
if self.len[l] == self.cap[l] {
self.grow(l);
}
let p = self.start[l] + self.len[l];
self.idx[p] = i;
if self.has_val {
self.val[p] = v;
}
self.len[l] += 1;
self.live += 1;
}
pub(crate) fn swap_remove(&mut self, l: usize, pos: usize) -> (u32, T) {
debug_assert!(pos < self.len[l]);
let s = self.start[l];
let p = s + pos;
let last = s + self.len[l] - 1;
let out = (
self.idx[p],
if self.has_val { self.val[p] } else { T::ZERO },
);
self.idx[p] = self.idx[last];
if self.has_val {
self.val[p] = self.val[last];
}
if self.linked {
self.link[p] = self.link[last];
}
self.len[l] -= 1;
self.live -= 1;
out
}
pub(crate) fn remove(&mut self, l: usize, i: u32) -> Option<T> {
self.position(l, i).map(|p| self.swap_remove(l, p).1)
}
pub(crate) fn clear(&mut self, l: usize) {
self.live -= self.len[l];
self.len[l] = 0;
}
fn grow(&mut self, l: usize) {
let end = self.idx.len();
if self.cap[l] > 0 && self.start[l] + self.cap[l] == end {
let new_cap = (2 * self.cap[l]).max(4);
self.idx.resize(self.start[l] + new_cap, 0);
if self.has_val {
self.val.resize(self.start[l] + new_cap, T::ZERO);
}
if self.linked {
self.link.resize(self.start[l] + new_cap, 0);
}
self.cap[l] = new_cap;
return;
}
if self.garbage > self.live.max(64) {
self.compact();
if self.len[l] < self.cap[l] {
return;
}
}
let new_cap = (2 * self.len[l]).max(4);
let new_start = self.idx.len();
self.idx.resize(new_start + new_cap, 0);
let s = self.start[l];
self.idx.copy_within(s..s + self.len[l], new_start);
if self.has_val {
self.val.resize(new_start + new_cap, T::ZERO);
self.val.copy_within(s..s + self.len[l], new_start);
}
if self.linked {
self.link.resize(new_start + new_cap, 0);
self.link.copy_within(s..s + self.len[l], new_start);
}
self.garbage += self.cap[l];
self.start[l] = new_start;
self.cap[l] = new_cap;
}
fn compact(&mut self) {
let total: usize = self.len.iter().map(|&n| n + (n / 2).max(2)).sum();
let mut idx = Vec::with_capacity(total);
let mut val = Vec::with_capacity(if self.has_val { total } else { 0 });
let mut link = Vec::with_capacity(if self.linked { total } else { 0 });
for l in 0..self.start.len() {
let (s, n) = (self.start[l], self.len[l]);
let cap = n + (n / 2).max(2);
self.start[l] = idx.len();
self.cap[l] = cap;
idx.extend_from_slice(&self.idx[s..s + n]);
idx.resize(idx.len() + cap - n, 0);
if self.has_val {
val.extend_from_slice(&self.val[s..s + n]);
val.resize(val.len() + cap - n, T::ZERO);
}
if self.linked {
link.extend_from_slice(&self.link[s..s + n]);
link.resize(link.len() + cap - n, 0);
}
}
self.idx = idx;
self.val = val;
self.link = link;
self.garbage = 0;
}
#[cfg(test)]
pub(crate) fn check(&self) -> bool {
let mut segs: Vec<(usize, usize)> = (0..self.start.len())
.filter(|&l| self.cap[l] > 0)
.map(|l| (self.start[l], self.start[l] + self.cap[l]))
.collect();
segs.sort_unstable();
let disjoint = segs.windows(2).all(|w| w[0].1 <= w[1].0);
let inside = segs.last().is_none_or(|s| s.1 <= self.idx.len());
let lens = (0..self.start.len()).all(|l| self.len[l] <= self.cap[l]);
disjoint && inside && lens && self.live == self.len.iter().sum::<usize>()
}
}
pub(crate) fn remove_linked<T: Real>(
x: &mut LineStore<T>,
y: &mut LineStore<T>,
l: usize,
pos: usize,
) -> (u32, usize) {
debug_assert!(x.linked && y.linked);
let t = x.link(l, pos);
let last = x.len[l] - 1;
let (i, _) = x.swap_remove(l, pos);
if pos != last && last >= LINK_MIN {
let (k, tk) = (x.index_at(l, pos) as usize, x.link(l, pos));
let l32 = u32::try_from(l).expect("line fits in u32");
let tk = y
.find(k, l32, tk)
.expect("the twin of a linked entry exists");
y.set_link(k, tk, pos);
}
(i, t)
}
pub(crate) fn check_links<T: Real>(
c: &LineStore<T>,
r: &LineStore<T>,
exact: bool,
) -> Result<(), String> {
if !c.linked || !r.linked {
return Err("store not linked".into());
}
if c.live != r.live {
return Err(format!("{} entries by columns, {} by rows", c.live, r.live));
}
for j in 0..c.start.len() {
for q in 0..c.len[j] {
let i = c.index_at(j, q) as usize;
let j32 = u32::try_from(j).expect("line fits in u32");
if i >= r.start.len() {
return Err(format!("entry ({j}, {q}) names row {i}, out of range"));
}
let Some(t) = r.find(i, j32, c.link(j, q)) else {
return Err(format!("entry ({i}, {j}) has no twin"));
};
if exact && (t != c.link(j, q) || r.link(i, t) != q) {
return Err(format!("entry ({i}, {j}): hints not exact"));
}
}
}
Ok(())
}
#[derive(Clone, Debug, Default)]
pub(crate) struct CountLists {
head: Vec<usize>,
next: Vec<usize>,
prev: Vec<usize>,
count: Vec<usize>,
size: usize,
}
impl CountLists {
pub(crate) fn new(n: usize, max_count: usize) -> Self {
Self {
head: vec![NIL; max_count + 1],
next: vec![NIL; n],
prev: vec![NIL; n],
count: vec![NIL; n],
size: 0,
}
}
#[inline]
pub(crate) fn size(&self) -> usize {
self.size
}
pub(crate) fn insert(&mut self, e: usize, c: usize) {
debug_assert_eq!(self.count[e], NIL, "element {e} already listed");
let h = self.head[c];
self.count[e] = c;
self.prev[e] = NIL;
self.next[e] = h;
if h != NIL {
self.prev[h] = e;
}
self.head[c] = e;
self.size += 1;
}
pub(crate) fn remove(&mut self, e: usize) {
let c = self.count[e];
debug_assert_ne!(c, NIL, "element {e} not listed");
let (p, n) = (self.prev[e], self.next[e]);
if p == NIL {
self.head[c] = n;
} else {
self.next[p] = n;
}
if n != NIL {
self.prev[n] = p;
}
self.count[e] = NIL;
self.size -= 1;
}
pub(crate) fn set(&mut self, e: usize, c: usize) {
if self.count[e] != c {
self.remove(e);
self.insert(e, c);
}
}
#[inline]
pub(crate) fn contains(&self, e: usize) -> bool {
self.count[e] != NIL
}
#[inline]
pub(crate) fn first(&self, c: usize) -> Option<usize> {
let h = self.head.get(c).copied().unwrap_or(NIL);
(h != NIL).then_some(h)
}
#[inline]
pub(crate) fn next(&self, e: usize) -> Option<usize> {
let n = self.next[e];
(n != NIL).then_some(n)
}
#[cfg(test)]
pub(crate) fn check(&self) -> bool {
let mut seen = 0;
for (c, &h) in self.head.iter().enumerate() {
let mut e = h;
let mut p = NIL;
while e != NIL {
if self.count[e] != c || self.prev[e] != p {
return false;
}
seen += 1;
p = e;
e = self.next[e];
}
}
seen == self.size && self.count.iter().filter(|&&c| c != NIL).count() == self.size
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::real::approx_eq;
#[test]
fn line_store_grows_relocates_and_compacts() {
let mut s = LineStore::<f64>::new(&[1, 0, 2]);
for k in 0..500_u32 {
let l = (k % 3) as usize;
s.push(l, k, f64::from(k));
if k % 7 == 0 {
let (i, _) = s.swap_remove(l, 0);
assert!(s.position(l, i).is_none());
}
assert!(s.check());
}
for l in 0..3 {
let (idx, val) = s.line(l);
for (&i, &v) in idx.iter().zip(val) {
assert_eq!(i % 3, u32::try_from(l).unwrap());
assert!(approx_eq(v, f64::from(i), 0.0));
}
}
let n1 = s.len(1);
s.clear(1);
assert_eq!(s.live(), s.len(0) + s.len(2));
assert!(n1 > 0 && s.check());
assert_eq!(s.remove(0, 1), None);
assert!(s.get(0, 498).is_some());
}
#[test]
fn ex_5_2_twin_links_match_search_removal() {
const N: usize = 40;
let mut seed = 0x2545_f491_4f6c_dd1d_u64;
let mut next = |k: usize| {
seed ^= seed << 13;
seed ^= seed >> 7;
seed ^= seed << 17;
usize::try_from(seed % (k as u64)).unwrap()
};
let caps = vec![1; N];
let (mut c, mut r) = (
LineStore::<f64>::new_linked(&caps),
LineStore::<f64>::new_linked(&caps),
);
let (mut c0, mut r0) = (LineStore::<f64>::new(&caps), LineStore::<f64>::new(&caps));
let (mut removals, mut exact, mut searched) = (0, 0, 0);
for step in 0..30_000 {
let (i, j) = (next(N), next(N));
let (i32_, j32) = (u32::try_from(i).unwrap(), u32::try_from(j).unwrap());
match c0.position(j, i32_) {
None => {
let v = f64::from(u32::try_from(step).unwrap());
let (q, t) = (c.len(j), r.len(i));
c.push_linked(j, i32_, v, t);
r.push_linked(i, j32, 0.0, q);
c0.push(j, i32_, v);
r0.push(i, j32, 0.0);
}
Some(q0) => {
let t0 = r0.position(i, j32).unwrap();
let q = c.find(j, i32_, r.link(i, t0)).unwrap();
assert_eq!(q, q0);
let (_, t) = remove_linked(&mut c, &mut r, j, q);
let tf = r.find(i, j32, t).unwrap();
if tf == t {
exact += 1;
} else {
searched += 1;
}
remove_linked(&mut r, &mut c, i, tf);
c0.remove(j, i32_);
r0.remove(i, j32);
removals += 1;
}
}
if step % 97 == 0 {
assert_eq!(check_links(&c, &r, false), Ok(()));
}
for l in 0..N {
assert_eq!(c.line(l).0, c0.line(l).0, "column {l} at step {step}");
assert_eq!(r.line(l).0, r0.line(l).0, "row {l} at step {step}");
let same = c.line(l).1.iter().zip(c0.line(l).1);
assert!(same.into_iter().all(|(a, b)| a.to_bits() == b.to_bits()));
}
}
assert!(
removals > 5000 && exact > 1000 && searched > 100,
"{removals} {exact} {searched}"
);
assert!(c.check() && r.check());
assert_eq!(check_links(&c, &r, false), Ok(()));
let twins: Vec<(usize, usize)> = (0..c.len(3))
.map(|q| {
let i = c.index_at(3, q) as usize;
(i, r.find(i, 3, c.link(3, q)).unwrap())
})
.collect();
c.clear(3);
for (i, t) in twins {
remove_linked(&mut r, &mut c, i, t);
}
assert_eq!(check_links(&c, &r, false), Ok(()));
let (mut c1, mut r1) = (
LineStore::<f64>::new_linked(&caps),
LineStore::<f64>::new_linked(&caps),
);
for j in 0..N {
for i in (j % 3..N).step_by(3) {
let (q, t) = (c1.len(j), r1.len(i));
c1.push_linked(j, u32::try_from(i).unwrap(), 1.0, t);
r1.push_linked(i, u32::try_from(j).unwrap(), 0.0, q);
}
}
assert_eq!(check_links(&c1, &r1, true), Ok(()));
}
#[test]
fn ex_5_2_twin_links_stay_exact_in_long_lines() {
const N: usize = 40;
let mut seed = 0x9e37_79b9_7f4a_7c15_u64;
let mut next = |k: usize| {
seed ^= seed << 13;
seed ^= seed >> 7;
seed ^= seed << 17;
usize::try_from(seed % (k as u64)).unwrap()
};
let caps = vec![1; N];
let (mut c2, mut r2) = (
LineStore::<f64>::new_linked(&caps),
LineStore::<f64>::new_linked(&caps),
);
for j in 0..N {
for i in 0..N {
let (q, t) = (c2.len(j), r2.len(i));
c2.push_linked(j, u32::try_from(i).unwrap(), 1.0, t);
r2.push_linked(i, u32::try_from(j).unwrap(), 0.0, q);
}
}
let mut removed = 0;
for _ in 0..5_000 {
let (i, j) = (next(N), next(N));
let j32 = u32::try_from(j).unwrap();
let Some(q) = c2.position(j, u32::try_from(i).unwrap()) else {
continue;
};
if c2.len(j) <= LINK_MIN + 1 || r2.len(i) <= LINK_MIN + 1 {
continue;
}
let (_, t) = remove_linked(&mut c2, &mut r2, j, q);
assert_eq!(r2.find(i, j32, t), Some(t), "an exact hint");
remove_linked(&mut r2, &mut c2, i, t);
removed += 1;
}
assert!(removed > 300, "{removed}");
assert_eq!(check_links(&c2, &r2, true), Ok(()));
}
#[test]
fn count_lists_move_elements() {
let mut c = CountLists::new(5, 4);
for e in 0..5 {
c.insert(e, e % 3);
}
assert!(c.check());
c.set(0, 4);
c.remove(3);
assert!(c.check());
assert_eq!(c.size(), 4);
assert_eq!(c.first(4), Some(0));
let mut b1 = vec![];
let mut e = c.first(1);
while let Some(x) = e {
b1.push(x);
e = c.next(x);
}
assert_eq!(b1, vec![4, 1]);
}
}