From 0d02de603a1290399f3548012c538fd9d387c3f0 Mon Sep 17 00:00:00 2001 From: d Date: Thu, 6 Aug 2020 11:36:26 +0200 Subject: [PATCH 1/2] add wiggle function to fix an invalidated position --- lib/src/array_stump.rs | 158 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/lib/src/array_stump.rs b/lib/src/array_stump.rs index c4ea368..9d8eceb 100644 --- a/lib/src/array_stump.rs +++ b/lib/src/array_stump.rs @@ -7,11 +7,25 @@ pub struct Index { } impl Index { + pub const FIRST: Index = Index{outer: 0, inner: 0}; + pub fn new(outer: usize, inner: usize) -> Index { Index{outer, inner} } } +#[derive(Clone, Copy, Debug)] +pub struct IndexTransition { + old: Index, + new: Index, +} + +impl IndexTransition { + pub fn new(old: Index, new: Index) -> IndexTransition { + IndexTransition{old, new} + } +} + /// The core data structure representing a two-level sorted stump. pub struct ArrayStump where @@ -84,6 +98,8 @@ where // Note: The `.to_vec()` requires T: Clone but is faster than using drain. Keep? // let block_tail: Vec<_> = self.data[idx_block].drain(tail_from .. tail_upto).collect(); + + // Note: why not use Vec.split_off? self.data[idx_block].truncate(tail_from); self.data.insert(idx_block + 1, block_tail); @@ -320,6 +336,105 @@ where pub fn debug(&self) { println!("{:?}", self.data); } + + pub fn debug_order(&self) { + println!("--- DEBUG ORDER"); + let mut remember : Option<&T> = None; + for (idx, block) in self.data.iter().enumerate() { + println!("-- BLOCK #{} ({}/{} items)", idx, block.len(), block.capacity()); + for value in block { + if let Some(last) = remember { + println!("{:?}", (self.comparator)(last, value)); + } + println!("{:?}", value); + remember = Some(value); + } + } + } + + pub fn fix_index(&self, transition: IndexTransition, idx: Index) -> Index { + let old = transition.old; + let new = transition.new; + + if idx == old { + new + } else { + if old < new { + if idx < old || new < idx { + idx + } else { + self.prev_index(idx).unwrap() + } + } else { + if idx < new || old < idx { + idx + } else { + self.next_index(idx).unwrap() + } + } + } + } + + pub fn wiggle(&mut self, idx: Index) -> Option { + let cmp = &self.comparator; + let val = self.get_by_index(idx); + + if let Some(prev) = self.prev_index(idx) { + let mut dest = Some(prev); + while cmp(self.get_by_index(dest.unwrap()), val) == Ordering::Greater { + dest = self.prev_index(dest.unwrap()); + if dest == None { + break; + } + } + if dest != Some(prev) { + let dest = if dest == None { + Index::FIRST + } else { + self.next_index(dest.unwrap()).unwrap() + }; + // println!("move {:?} forward (to {:?})", idx, dest); + let val = self.data[idx.outer].remove(idx.inner); + for block in (dest.outer..idx.outer).rev() { + let b = &mut self.data[block]; + let crosser = b.remove(b.len() - 1); + self.data[block+1].insert(0, crosser); + } + self.data[dest.outer].insert(dest.inner, val); + + return Some(IndexTransition::new( idx, dest )); + } + } + + if let Some(next) = self.next_index(idx) { + let mut dest = Some(next); + while cmp(val, self.get_by_index(dest.unwrap())) == Ordering::Greater { + dest = self.next_index(dest.unwrap()); + if dest == None { + break; + } + } + if dest != Some(next) { + let dest = if dest == None { + let last_block = self.data.len() - 1; + Index::new(last_block, self.data[last_block].len()-1) + } else { + self.prev_index(dest.unwrap()).unwrap() + }; + // println!("move {:?} back (to {:?})", idx, dest); + let val = self.data[idx.outer].remove(idx.inner); + for block in idx.outer..dest.outer { + let crosser = self.data[block+1].remove(0); + self.data[block].push(crosser); + } + self.data[dest.outer].insert(dest.inner, val); + + return Some(IndexTransition::new( idx, dest )); + } + } + + None + } } // I'm not quite sure if we should support the Index trait, because index semantics @@ -836,4 +951,47 @@ mod test { assert_eq!(a.max(), Some(&4)); } + #[test] + fn test_wiggle() { + let mut a = new_array!(2, vec![vec![2, 4], vec![6, 8], vec![10, 12]]); + // Wiggling without changes shan't do anything + let mut c = Some(Index::FIRST); + while c.is_some() { + assert!(a.wiggle(c.unwrap()).is_none()); + c = a.next_index(c.unwrap()); + } + + let mut tracer = Index::new(1,0); + + // Wiggle inside the same block + a.data[0][0] = 5; + assert_eq!(a.data, [vec![5, 4], vec![6, 8], vec![10, 12]]); + let transition = a.wiggle(Index::new(0,0)).unwrap(); + assert_eq!(transition.new, Index::new(0, 1)); + assert_eq!(a.data, [vec![4, 5], vec![6, 8], vec![10, 12]]); + + tracer = a.fix_index(transition, tracer); + assert_eq!(tracer, Index::new(1, 0)); + + // Wiggle over into another block + a.data[0][0] = 11; + let transition = a.wiggle(Index::new(0, 0)).unwrap(); + assert_eq!(transition.new, Index::new(2, 0)); + assert_eq!(a.data, [vec![5, 6], vec![8, 10], vec![11, 12]]); + tracer = a.fix_index(transition, tracer); + assert_eq!(tracer, Index::new(0, 1)); + + // Wiggle reverse over block boundary + a.data[2][1] = 1; + let transition = a.wiggle(Index::new(2, 1)).unwrap(); + assert_eq!(transition.new, Index::new(0, 0)); + assert_eq!(a.data, [vec![1, 5], vec![6, 8], vec![10, 11]]); + tracer = a.fix_index(transition, tracer); + assert_eq!(tracer, Index::new(1, 0)); + + // We must never increase capacities while wiggling + assert_eq!(a.data[0].capacity(), 2); + assert_eq!(a.data[1].capacity(), 2); + assert_eq!(a.data[2].capacity(), 2); + } } \ No newline at end of file From aef91b67d6ee961609fed1bcb75ad8ae4ca062ea Mon Sep 17 00:00:00 2001 From: d Date: Thu, 6 Aug 2020 12:16:53 +0200 Subject: [PATCH 2/2] increase test coverage --- lib/src/array_stump.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/src/array_stump.rs b/lib/src/array_stump.rs index 9d8eceb..62dd7e7 100644 --- a/lib/src/array_stump.rs +++ b/lib/src/array_stump.rs @@ -966,18 +966,24 @@ mod test { // Wiggle inside the same block a.data[0][0] = 5; assert_eq!(a.data, [vec![5, 4], vec![6, 8], vec![10, 12]]); - let transition = a.wiggle(Index::new(0,0)).unwrap(); + let transition = a.wiggle(Index::FIRST).unwrap(); assert_eq!(transition.new, Index::new(0, 1)); assert_eq!(a.data, [vec![4, 5], vec![6, 8], vec![10, 12]]); + // Correct transition of item above modified range -> nop tracer = a.fix_index(transition, tracer); assert_eq!(tracer, Index::new(1, 0)); // Wiggle over into another block a.data[0][0] = 11; - let transition = a.wiggle(Index::new(0, 0)).unwrap(); + let transition = a.wiggle(Index::FIRST).unwrap(); assert_eq!(transition.new, Index::new(2, 0)); assert_eq!(a.data, [vec![5, 6], vec![8, 10], vec![11, 12]]); + + // Translation should affect 'old' index directly + assert_eq!(a.fix_index(transition, Index::FIRST), transition.new); + + // Mutated item surpassed tracer from below -> drop tracer = a.fix_index(transition, tracer); assert_eq!(tracer, Index::new(0, 1)); @@ -986,6 +992,15 @@ mod test { let transition = a.wiggle(Index::new(2, 1)).unwrap(); assert_eq!(transition.new, Index::new(0, 0)); assert_eq!(a.data, [vec![1, 5], vec![6, 8], vec![10, 11]]); + + // Mutated item surpassed tracer from above -> rise + tracer = a.fix_index(transition, tracer); + assert_eq!(tracer, Index::new(1, 0)); + + a.data[2][0] = 12; + let transition = a.wiggle(Index::new(2, 0)).unwrap(); + + // Correct transition of item below modified range -> nop tracer = a.fix_index(transition, tracer); assert_eq!(tracer, Index::new(1, 0));