diff --git a/csx3/src/merge_lazy.md b/csx3/src/merge_lazy.md index 3b9e201..c3f487b 100644 --- a/csx3/src/merge_lazy.md +++ b/csx3/src/merge_lazy.md @@ -6,41 +6,39 @@ Need to perform "shallow" or lazy merge, that is, ## Lazy merge operation * Swapping of references instead of the actual data (light operation) * Ordering logic per iteration -```rust -use csx3::utils::VirtualSlice; +```rust,noplayground +use csx3::merge::Merge; let (s1, s2) = (&mut [5,6,7], &mut[1,2,3,4]); -let mut vs = VirtualSlice::new(); -vs.attach(s1); // attach to s1 -vs.merge_shallow(s2); // attach to s2 and do shallow merge with s1 +let mut vs = s1.merge_shallow(s2); // attach to s2 and do shallow merge with s1 -vs.iter() // ordered access of attached slices - .enumerate() // [&1, &2, &3, &4, &5, &6, &7] +vs.iter() // ordered access of attached slices + .enumerate() // [&1, &2, &3, &4, &5, &6, &7] .for_each(|(i,x)| assert_eq(*x,i+1) ); -assert_eq!(s1, &[5,6,7]); // while s1 & s2 are unaffected +assert_eq!(s1, &[5,6,7]); // while s1 & s2 are unaffected assert_eq!(s2, &[1,2,3,4]); ``` ## Deferred Mutability; Superimpose order * Straight swapping of data referenced (could end up a heavy heap operation) * No ordering logic per iteration -```rust -use csx3::utils::VirtualSlice; +```rust,noplayground + use csx3::merge::Merge; -let (s1, s2) = (&mut [5,6,7], &mut[1,2,3,4]); -let mut vs = VirtualSlice::new(); - -vs.attach(s1); // attach to s1 -vs.merge_shallow(s2); // attach to s2 and do shallow merge with s1 - - // vs == &[&1,&2,&3,&4,&5,&6,&7] - // s1 == &[5,6,7] - // s2 == &[1,2,3,4] -vs.impose_shallow_merge(); // superimpose order mask - -assert_eq!(s1, &[1,2,3]); -assert_eq!(s2, &[4,5,6,7]); + let s1 = &mut [5,6,7]; + let s2 = &mut [1,2,3,4]; + + let mut mask = s1.merge_virtual(s2); // mask mutably borrows s1 & s2 + + mask.iter() // iterate over merged contents + .enumerate() // while s1 and s2 are unaffected + .for_each(|(i,x)| assert_eq!(*x,i+1) ); + + mask.superimpose_shallow_merge(); // mutate the order back to s1 and s2 + // and drop mutable references + assert_eq!(s1, &[1,2,3]); + assert_eq!(s2, &[4,5,6,7]); ``` diff --git a/src/bin/main.rs b/src/bin/main.rs index 677e3f1..ff281a1 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,10 +1,17 @@ -use csx3::sort::{merge::*, quick::quick_sort, count::CountSort}; -use csx3::trees::*; -use csx3::linkedlists::*; -use csx3::select::*; use std::env::args; use std::str::FromStr; -use csx3::random_sequence; +use csx3::{ + sort::{ + merge::*, + quick::QuickSort, + count::CountSort + }, + merge::Merge, + select::Select, + trees::*, + linkedlists::*, + random_sequence +}; type MyType = i8; @@ -30,19 +37,13 @@ fn main() { println!("Random Selection"); let mut arr: Vec = list.iter().copied().collect(); - println!("1st order stat= {:?}", r_selection(&mut arr, 1)); - println!("2nd order stat= {:?}", r_selection(&mut arr, 2)); - println!("3rd order stat= {:?}", r_selection(&mut arr, 3)); + println!("1st order stat= {:?}", arr.r_selection(1)); + println!("2nd order stat= {:?}", arr.r_selection(2)); + println!("3rd order stat= {:?}", arr.r_selection(3)); - println!("Deterministic Selection"); - let mut arr: Vec = list.iter().copied().collect(); - println!("1st order stat= {:?}", d_selection(&mut arr, 1)); - println!("2nd order stat= {:?}", d_selection(&mut arr, 2)); - println!("3rd order stat= {:?}", d_selection(&mut arr, 3)); - - println!("MergeSort Immut: {:?}", mergesort(&v)); - println!("MergeSort Mut : ({}, {:?})", mergesort_mut(&mut v, merge_mut), v); - quick_sort(&mut v); + println!("MergeSort Immut: {:?}", v.mergesort()); + println!("MergeSort Mut : ({}, {:?})", v.mergesort_mut(Merge::merge_mut), v); + v.quick_sort(); println!("Quick Sort : {:?}", v); let mut arr: Vec = list.iter().copied().collect(); diff --git a/src/linkedlists/mod.rs b/src/linkedlists/mod.rs index d9baa6f..6c81ba1 100644 --- a/src/linkedlists/mod.rs +++ b/src/linkedlists/mod.rs @@ -1,5 +1,8 @@ use std::fmt::Debug; -use crate::sort::merge::{merge_mut_adjacent, mergesort_mut}; +use crate::{ + sort::merge::MergeSort, + merge::Merge +}; /// I could have done it differently /// but I wanted to see how far I could go with this simple enum structure @@ -108,7 +111,7 @@ impl List } pub fn sort_with_count(&self) -> (usize, Vec){ let mut s = self.iter().copied().collect::>(); - (mergesort_mut(&mut s[..], merge_mut_adjacent), s) + (s.mergesort_mut(Merge::merge_mut_adjacent), s) } } diff --git a/src/merge/mod.rs b/src/merge/mod.rs index 839166e..cc0beb1 100644 --- a/src/merge/mod.rs +++ b/src/merge/mod.rs @@ -1,11 +1,8 @@ -/// -/// -/// +pub mod vs; use std::cmp::Ordering; -use std::fmt::{Debug, Formatter}; -use std::ops::{Index, IndexMut, Range}; use std::iter::Peekable; +use vs::VirtualSlice; /// Takes two iterators as input with each iteration returning /// the next in order item out of the two, plus its inversions' count @@ -87,494 +84,117 @@ impl Iterator for MergeIterator } } -/// Constructing a VirtualSlice allowing us to operate over -/// multiple non-adjacent slice segments as a "continuous slice" -/// ``` -/// use csx3::merge::VirtualSlice; -/// -/// let v = &mut [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]; -/// let (s1, s2) = v.split_at_mut(5); -/// let _s3 = &mut [0, 0, 0, 0,0]; // Wedge this to break stack continuity -/// let s4 = &mut [2, 4, 6, 8, 10]; -/// -/// let mut v = VirtualSlice::new_adjacent(s1); -/// v.merge(s2); -/// v.iter() -/// .enumerate() -/// .for_each(|(i,x)| assert_eq!(*x,i+1) ); -/// -/// assert_eq!(s1, &mut [1, 2, 3, 4, 5]); -/// assert_eq!(s2, &mut [6, 7, 8, 9, 10]); -/// -/// let mut v = VirtualSlice::new(); -/// v.attach(s1); -/// v.attach(s4); -/// v[0] = 11; -/// v[5] = 9; -/// v.swap(0, 5); -/// -/// assert_eq!(s1, &mut [9, 2, 3, 4, 5]); -/// assert_eq!(s4, &mut [11, 4, 6, 8 , 10]); -/// -/// ``` -pub enum VirtualSlice<'a, T> where T: Ord { - /// The tuple holds a vector of mutable references and the Index Reflector - NonAdjacent( Vec<&'a mut T>, Option>), - /// Holds a mutable reference to the reconstructed parent slice out of two memory adjacent slices - Adjacent( &'a mut[T] ), +/// Merge capabilities for generic type arrays +pub trait Merge where T: Ord { + fn merge_virtual<'a>(&'a mut self, s:&'a mut[T]) -> VirtualSlice; + fn merge_mut_adjacent(&mut self, s:&mut[T]) -> usize; + fn merge_mut(&mut self, s:&mut[T]) -> usize; } -use VirtualSlice::{NonAdjacent, Adjacent}; - -impl<'a, T> VirtualSlice<'a, T> where T: Ord { - /// Create a new VirtualSlice for use with non-adjacent slice segments - pub fn new() -> VirtualSlice<'a, T> { - NonAdjacent( Vec::new(), None ) - } - /// Create a new VirtualSlice for use with adjacent slice segments - pub fn new_adjacent(s: &'a mut[T]) -> VirtualSlice<'a, T> { - Adjacent( s ) - } - /// Current length of the VirtualSlice is equal to sum of all attached slice segments - pub fn len(&self) -> usize { - match self { - NonAdjacent(v,_) => v.len(), - Adjacent(s) => s.len(), - } - } - pub fn is_empty(&self) -> bool { - match self { - NonAdjacent(v,_) => v.is_empty(), - Adjacent(s) => s.is_empty(), - } - } - /// Get a mutable iterator over the VirtualSlice that return mutable references &mut T - pub fn iter_mut<'b: 'a>(&'b mut self) -> VSIterMut<'b, T> where T: 'b { - VSIterMut::new(self) - } - /// Get an immutable iterator over the VirtualSlice that return mutable references &mut T - pub fn iter(&self) -> VSIter<'_, T> { - VSIter::new(self) - } - /// Attach a slice segment onto the VirtualSlice - pub fn attach(&mut self, s: &'a mut [T]) { - match self { - NonAdjacent(v, _) => { - s.iter_mut() - .for_each(|item| { - v.push(item); - }); - } - Adjacent(s0) => { - let fs: &mut [T]; - unsafe { - fs = &mut *std::ptr::slice_from_raw_parts_mut::((*s0).as_mut_ptr(), s0.len() + s.len()); - // checking they are aligned and adjacent, - // if not panic! so we prevent unpredictable behaviour - assert!(&s[0] == &fs[s0.len()]); - } - *self = VirtualSlice::new_adjacent(fs); - } - } - } - /// Deep swap; swaps the two references to the positions of the underlying slice segments - /// Operates at both adjacent and non-adjacent slices - pub fn swap(&mut self, a: usize, b:usize) { - if a == b { - return; - } - // we cannot use vv.swap as this will simply swap the position of the pointers - // rather where the pointers point to. Hence we use the pointers to swap the memory contents - unsafe { - std::ptr::swap::( - &mut self[a] as *mut T, - &mut self[b] as *mut T - ) - } - } - /// Perform a deep merge by ordering the referred values hence mutating the slice segments - pub fn merge(&mut self, s: &'a mut [T]) -> usize - where T: Ord + Debug { - // we are not interested in the index reflector here so we don't store it - let (inversion, _) = self._merge(s, VirtualSlice::swap); - inversion - } - /// Shallow swap; swaps the references of the underlying slice segments. The segments aren't affected - /// Operates only with non-adjacent slices - pub fn swap_shallow(&mut self, a: usize, b:usize) { - if let NonAdjacent(v, _) = self { - v.swap(a, b); - } else { - panic!("Not applicable for Adjacent VirtualSlices; use with VirtualSlice::new() instead"); - } - } - /// Perform a shallow merge by ordering the VirtualSlice's references and not the referred values. - /// The VirtualSlice can be used as sort-mask layer above the slice segments, which later can be superimposed over - /// In case of non-adjacent slices only. - pub fn merge_shallow(&mut self, s: &'a mut [T]) -> usize - where T: Ord + Debug { - let (inversions, idx_rfl) = self._merge(s, VirtualSlice::swap_shallow); - - match self { - Adjacent(_) => panic!("merge_shallow(): cannot operate in adjacent mode"), - NonAdjacent(_, idx_reflector) => { - // we need to store index reflector in case we want to mutate the attached slices via the impose method - *idx_reflector = idx_rfl; - inversions - } - } - } - - /// Superimposes O(n-1) the derived order onto the attached slice segments. - /// The stored Index Reflector contains the order per reference - pub fn superimpose_shallow_merge(&mut self) { - // total operations must be len()-1 as we use 1 position as temp swap location - let total_swaps = self.len() - 2; - // Count total number of swaps occurred - let mut swap_count = 0usize; - // holds the current temp swap position - let mut temp_idx = 0usize; - - // make sure entry conditions are correct - // prefer to panic as non of those scenarios should be recoverable - // otherwise, extract internal data and proceed with algorithm - match self { - Adjacent(_) => panic!("superimpose_shallow_merge(): call doesn't work over adjacent slice segments"), - NonAdjacent(_, None) => panic!("superimpose_shallow_merge(): Index Reflector does not exist. Did merge_shallow() run ?"), - NonAdjacent(vs, Some(idx)) => { +impl Merge for [T] + where T: Ord { - // Exit conditions are either, - // - total swaps == total number of elements - 1 OR - // - current tmp index position has reached the end of VirtualSlice (Case: virtualslice already ordered; zero swaps) - while swap_count < total_swaps && temp_idx < total_swaps - { - let mut i; - // Exit condition - // - current swap index == correct ordered position, (item is positioned where it should be) - while temp_idx != idx[temp_idx] { - i = idx[temp_idx]; - idx.swap(temp_idx, i); - unsafe { - // we need to overcome Rust's borrow checking - // as we cannot use self.swap() here - std::ptr::swap::(&mut *vs[temp_idx] as *mut T, &mut *vs[i] as *mut T); - } - swap_count += 1; - } - temp_idx += 1; - } - } - } - } - - /// Merge Self with another non-adjacent slice using in-place memory swaps - /// For the algorithm to work we need the following components - /// - Append VirtualSlice with given &mut slice so to form a "continuous slice" - /// - Use for slice comparison an "Index Reflector (idx_rfl)" table to "project" (c,i') positions upon the "continuous slice" as (c', i) - /// - Swap action at once both (a) continuous slice and (b) Index Reflector + /// Returns a merged representation (virtual slice) that attaches onto `self` with another slice without mutating their contents + /// The virtual slice can then be used for further ordered operations across the attached slices, + /// Using its `superimpose_merge()` method you can mutate the order back to the attached slices. /// ``` - /// //Slice 1 Slice 2 VirtualSlice Index Reflector - /// //======= ========= ========================= ============= - /// //[5,6,7] <> [1,2,3,4] [5(c'/i),6,7,1(j),2,3,4] [1(c/i'),2,3,] - /// //[1,6,7] <> [5,2,3,4] [1,6(i),7,5(c'),2(j),3,4] [4(c),2(i'),3] - /// //[1,2,7] <> [5,6,3,4] [1,2,7(i),5(c'),6,3(j),4] [4(c),5,3(i')] - /// //[1,2,3] <> [5,6,7,4] [1,2,3,5(c'/i),6,7,4(j)] [4(c/i'),5,6,] - /// //[1,2,3] <> [4,6,7,5] [1,2,3,4,6(i),7,5(c')](j) [7(c),5(i'),6] <-- Phase 1: Main merge finished but still i < c' - /// //[1,2,3] <> [4,5,7,6] [1,2,3,4,5,7(i),6(c')](j) [5,7(c),6(i')] Trick: reflector knows the right order remaining - /// //[1,2,3] <> [4,6,7,5] [1,2,3,4,5,6,7(i/c')](j) [5,6,7(c/i') ] <-- Phase 2: finished merging (reflects starting position) + /// use csx3::merge::Merge; /// - /// use csx3::merge::VirtualSlice; /// let s1 = &mut [5,6,7]; - /// let _s = &[0,0,0,0,0,0]; // wedge to break adjacency /// let s2 = &mut [1,2,3,4]; /// - /// let mut vs = VirtualSlice::new(); + /// let mut mask = s1.merge_virtual(s2); // mask mutably borrows s1 & s2 /// - /// vs.merge(s1); - /// vs.merge(s2); + /// mask.iter() // iterate over merged contents + /// .enumerate() // while s1 and s2 are unaffected + /// .for_each(|(i,x)| assert_eq!(*x,i+1) ); /// + /// mask.superimpose_shallow_merge(); // mutate the order back to s1 and s2 + /// // and drop mutable references /// assert_eq!(s1, &[1,2,3]); /// assert_eq!(s2, &[4,5,6,7]); /// ``` - fn _merge(&mut self, s: &'a mut [T], mut f_swap: F) -> (usize, Option>) - where T: Ord + Debug, - F: FnMut(&mut Self, usize, usize) { - - if self.is_empty() { - self.attach(s); - return (0, None) - } - - // j = s2[j] equivalent position within the working slice (j') and index reflector (j) - let mut j = self.len(); - - // attach slice to be merged with self - self.attach(s); - - // i = partition position in working slice so that ... [merged elements] < ws[i] < [unmerged elements] - // p = index reflector partition bound where i's position is always upper bounded by p - // c = s1[c] equivalent position in the index reflector, so that idx_rfl[c] == c' == s1[c] equivalent in ws[c'], - // used for optimising finding i pos in index array - let (mut inv_count, mut c, mut i, p) = (0usize, 0usize, 0usize, j); - - // ws_len = working slice's length = self.len + s.len - let ws_len = self.len(); - - // Memory Optimisation: we could build the index reflector of size [ 0 .. size of left slice] since the following properties apply - // - c & i' will never exceed size of left slice - // - j == j' always be the same position - let mut idx_rfl = (0..ws_len).into_iter().collect::>(); - - //println!("Merge:{:?} :: {:?} ({:?},{:?},{:?})", self, idx_rfl, i, j, c); - - let c_bound = p-1; - // Memory Optimisation: if idx_len() = s1.len() then use: - //let c_bound = idx_rfl.len()-1; - let i_bound = ws_len-1; - loop { - // Flattening/de-normalising the workflow logic - // ============================================ - // A: (i != j or j < ws_len ) => Any more comparisons required ? is everything in ws[..i] << ws[j] ? - // B: ( i != [c] where i < ws_len-1, c < p-1 ) => Have all left slice elements been processed ? Have we reached the end where i == [c] ? - // +------+-------+----------+--------------------------------------- - // | A | B | if Guard | Action - // +------+-------+----------+--------------------------------------- - // | true | true | l > r | Phase 1: swap right with pivot - // | true | false | N/A | Exit: Merge completed; finished left part, right part remaining is ordered - // | true | true | l > r | Phase 1: l<=r implied; swap left with pivot - // |false | true | ANY | Phase 2: finish remaining left items - // |false | false | N/A | Exit: Merge completed - // +------+-------+----------+--------------------------------------- - // - match (j < ws_len && i != j, i < i_bound && c < c_bound) { - (true, _) if self[idx_rfl[c]].cmp(&self[j]) == Ordering::Greater => { - // count the equivalent inversions - inv_count += j - i; - - // swap right slice's item in the working slice with merged partition edge ws[i] - // swap( ws[i] with ws[j'] where j' = index_reflector[j], but j' == j so - f_swap(self, i, j); - - // swap index_reflect[j] with index_reflector[i'] - // i' == index_reflector[x]; where x == i; - // e.g. i = 3rd pos, hence i' = index_reflector[x] where x == 3; - let idx = idx_rfl[c..p].iter().position(|x| *x == i).unwrap() + c; - // swap( i' with j ) - idx_rfl.swap(idx, j); - // or since always j == j' we just copy the value over no need to swap - //idx_rfl[idx] = j; - //print!("\tr:"); - // point to the next in order position (right slice) - j += 1; - }, - (_, true) => { - // condition saves cpu-cycles from zero-impact operations when i == c' (no swap) - // otherwise it has no algorithmic impact - if i != idx_rfl[c] { - // swap left slice's item in the working slice with merged partition edge ws[i] - // swap( ws[i] with ws[c'] where c' = index_reflector[c] - f_swap(self, i, idx_rfl[c]); - - // swap index_reflect[c] with index_reflector[i'] - // i' == index_reflector[x]; where x == i; - // e.g. i = 3rd pos, hence i' = index_reflector[x] where x == 3; - let idx = idx_rfl[c..p].iter().position(|x| *x == i).unwrap() + c; - //swap( i' with c ) - idx_rfl.swap(idx, c); - //print!("\tl:"); - } - // point to the next in order position (left slice) - c += 1; - }, - (_, _) => break, - }; - // Move partition by one so that [merged partition] < ws[i] < [unmerged partition] - i += 1; - //println!("Merge:{:?} :: {:?} ({:?},{:?},{:?})", self, idx_rfl, i, j, c); - }; - - //println!("Merge Done"); - (inv_count, Some(idx_rfl)) - } -} - -pub enum VSIter<'b, T> where T: Ord + 'b { - NonAdjacent( std::slice::Iter<'b, &'b mut T> ), - Adjacent( std::slice::Iter<'b, T> ), -} -impl<'b, T> VSIter<'b, T> where T: Ord + 'b{ - pub fn new(vs: &'b VirtualSlice<'b, T>) -> VSIter<'b, T> { - match vs { - NonAdjacent(v, _) => VSIter::NonAdjacent(v.iter()), - Adjacent(s) => VSIter::Adjacent(s.iter()), - } - } -} -impl<'b, T> Iterator for VSIter<'b, T> where T: Ord + 'b { - type Item = &'b T; - - fn next(&mut self) -> Option { - match self { - VSIter::NonAdjacent( vi) => { - if let Some(val) = vi.next() { - Some(*val) - } else { - None - } - }, - VSIter::Adjacent( si) => si.next(), - } - } -} -pub enum VSIterMut<'b, T> where T: Ord + 'b { - NonAdjacent( std::slice::IterMut<'b, &'b mut T> ), - Adjacent( std::slice::IterMut<'b, T> ), -} -impl<'b, T> VSIterMut<'b, T> where T: Ord + 'b { - pub fn new(vs: &'b mut VirtualSlice<'b, T>) -> VSIterMut<'b, T> { - match vs { - NonAdjacent(v, _) => VSIterMut::NonAdjacent(v.iter_mut()), - Adjacent(s) => VSIterMut::Adjacent(s.iter_mut()), - } - } -} -impl<'b, T> Iterator for VSIterMut<'b, T> - where T: Ord + 'b { - type Item = &'b mut T; - - fn next(&mut self) -> Option { - match self { - VSIterMut::NonAdjacent( vi) => { - if let Some(val) = vi.next() { - Some(*val) - } else { - None - } - }, - VSIterMut::Adjacent( si) => si.next(), - } - } -} - -impl Default for VirtualSlice<'_, T> where T: Ord { - fn default() -> Self { - VirtualSlice::new() - } -} -impl Debug for VirtualSlice<'_, T> where T : Ord + Debug { - - /// extract and display the slice subsegments attached to the virtualslice - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_list() - .entries( - // returns a VSIter that serves &'a T - self.iter() - ) - .finish() - } -} -impl Index for VirtualSlice<'_, T> where T: Ord { - type Output = T; - - /// Index implementation so that VirtualSlice[x] will return a &T to the underlying slice segment - fn index(&self, index: usize) -> &Self::Output { - match self { - // syntactic overkill as rust will automatically dereference the chain of references - // but it feels good to be explicit!! - NonAdjacent(vv, _) => &(*vv[index]), - Adjacent(s) => &s[index], - } - } -} -impl IndexMut for VirtualSlice<'_, T> where T: Ord { - - /// Index implementation so that VirtualSlice[x] will return a &mut T to the underlying slice segment - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - // syntactic overkill as rust will automatically dereference the chain of references - // but it feels good to be explicit!! - match self { - NonAdjacent(vv, _) => &mut (*vv[index]), - Adjacent(s) => &mut s[index], - } + fn merge_virtual<'a>(&'a mut self, s: &'a mut [T]) -> VirtualSlice { + let mut vs = VirtualSlice::new(); + vs.attach(self); + vs.merge_shallow(s); + vs } -} -impl<'a, T> Index> for VirtualSlice<'a, T> where T: Ord { - type Output = [&'a mut T]; - fn index(&self, index: Range) -> &Self::Output { - if let NonAdjacent(vv, _) = self { - &vv[index] - } else { - panic!() - } - } -} -impl<'a, T> IndexMut> for VirtualSlice<'a, T> where T: Ord { - fn index_mut(&mut self, index: Range) -> &mut Self::Output { - if let NonAdjacent(vv, _) = self { - &mut vv[index] - } else { - panic!() - } - } -} -impl<'a, T> PartialOrd for VirtualSlice<'a, T> where T: Ord { - /// Enable VirtualSlice comparison so we can write things like + /// Applies memory efficient in-place merging when two slices are adjacent to each other. /// ``` - /// use csx3::merge::VirtualSlice; - /// let s = &mut [1,2,3,4,5]; - /// let vs = VirtualSlice::new_adjacent(s); - /// assert_eq!( vs, VirtualSlice::Adjacent( &mut [1,2,3,4,5] ) ); + /// use csx3::merge::Merge; + /// + /// let mut input = vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 10]; + /// let (s1,s2) = input.split_at_mut(5); + /// + /// s1.merge_mut_adjacent(s2); + /// assert_eq!(input, vec![1,2,3,4,5,6,7,8,9,10]); /// ``` - fn partial_cmp(&self, other: &Self) -> Option { - match (self, other) { - (NonAdjacent(v, _), NonAdjacent(o, _)) => v.partial_cmp(o), - (Adjacent(s), Adjacent(o)) => s.partial_cmp(o), - ( _, _ ) => panic!(), - } + /// Panics in case the two slices are found not to be adjacent. For safety, always use *ONLY* against slices that have been mutable split from an existing slice + /// #[should_panic] + /// let s1 = &mut [3, 5, 7]; + /// let s2 = &mut [1, 3, 5]; // wedge this between the two + /// let s3 = &mut [2, 4, 6]; + /// + /// s1.merge_mut_adjacent(s3); // this should throw a panic + /// + /// There is no warranty that Rust will maintain two slice adjacent in a case like this. + /// let s1 = &mut [3, 5, 7]; + /// let s3 = &mut [2, 4, 6]; + /// + /// s1.merge_mut_adjacent(s3); // this may not always work + /// + fn merge_mut_adjacent(&mut self, s:&mut[T]) -> usize { + let mut ws = VirtualSlice::new_adjacent(self); + ws.merge(s) } -} -impl<'a, T> PartialEq for VirtualSlice<'a, T> where T: Ord { - /// Enable VirtualSlice comparison so we can write things like + /// Merge two non-adjacent slices using in-place memory swaps and without use of rotations /// ``` - /// use csx3::merge::VirtualSlice; - /// let s = &mut [1,2,3,4,5]; - /// let vs = VirtualSlice::new_adjacent(s); - /// assert_eq!( vs, VirtualSlice::Adjacent( &mut [1,2,3,4,5] ) ); + /// use csx3::merge::Merge; + /// + /// let s1 = &mut [5,6,7]; + /// let _s = &[0,0,0,0,0,0]; // wedge to break adjacency + /// let s2 = &mut [1,2,3,4]; + /// + /// let inv = s1.merge_mut(s2); + /// + /// assert_eq!(s1, &[1,2,3]); + /// assert_eq!(s2, &[4,5,6,7]); /// ``` - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (NonAdjacent(v, _), NonAdjacent(o,_)) => v.eq(o), - (Adjacent(s), Adjacent(o)) => s.eq(o), - ( _, _ ) => panic!(), - } + fn merge_mut(&mut self, s:&mut[T]) -> usize { + let mut ws = VirtualSlice::new(); + ws.attach(self); + ws.merge(s) } } + #[cfg(test)] mod test { use super::*; + #[test] - #[should_panic] - fn test_virtual_slice_impose_negative_1() { - let s1 = &mut [5, 6, 7]; - let mut vs = VirtualSlice::new(); - vs.attach(s1); - vs.superimpose_shallow_merge(); // there is no index_reflector yet, should do nothing - } - #[test] - #[should_panic] - fn test_virtual_slice_impose_negative_2() { - let [(s1, s2)]: [(&mut [i32], &mut [i32]); 1] = [(&mut [5, 6, 7], &mut [1, 2, 3, 4])]; - let mut vs = VirtualSlice::new(); + fn test_merge_shallow() { + let s1 = &mut [1, 3, 5, 7, 9]; + let s2 = &mut [2, 4, 6, 8, 10]; + + let vs = s1.merge_virtual(s2); - vs.attach(s1); - vs.merge(s2); // deep merge creates a reflector - vs.superimpose_shallow_merge(); // it should do nothing as the vs is already ordered + assert_eq!( vs, VirtualSlice::NonAdjacent( + vec![&mut 1, &mut 2, &mut 3, &mut 4, &mut 5, &mut 6, &mut 7, &mut 8, &mut 9,&mut 10], + None + )); + vs.iter() + .enumerate() + .for_each(|(i,x)| assert_eq!(*x,i+1) ); + + assert_eq!( s1, &mut [1, 3, 5, 7, 9] ); + assert_eq!( s2, &mut [2, 4, 6, 8, 10] ); } #[test] - fn test_virtual_slice_impose() { + fn test_merge_superimpose() { let data: [(&mut[i32], &mut[i32], &[i32],&[i32]); 3] = [ (&mut [-88,-29,4,84], &mut [-127,-113,-71,-54], &[-127, -113, -88, -71], &[-54, -29, 4, 84]), (&mut [5,6,7], &mut[1,2,3,4], &[1,2,3], &[4,5,6,7]), @@ -582,9 +202,7 @@ mod test { ]; for (s1,s2, o1, o2) in data { - let mut vs = VirtualSlice::new(); - vs.merge_shallow(s1); - vs.merge_shallow(s2); + let mut vs = s1.merge_virtual(s2); vs.superimpose_shallow_merge(); println!("{:?}",vs); assert_eq!(s1, o1); @@ -592,171 +210,78 @@ mod test { } } #[test] - #[should_panic] - fn test_virtual_slice_adjacent_panic() { - let s1 = &mut [1, 3, 5, 7, 9]; - let _s = &mut [0,0,0,0]; - let s2 = &mut [2, 4, 6, 8, 10]; - let mut vs = VirtualSlice::new_adjacent(s1); - vs.attach(s2); - } - #[test] - fn test_virtual_slice_iter_mut_adjacent() { - let mut input = vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 10]; - let (s1, s2) = input.split_at_mut(5); - - let mut vs = VirtualSlice::new_adjacent(s1); - vs.attach(s2); - println!("{:?}", vs); - assert_eq!(vs, Adjacent(&mut [1, 3, 5, 7, 9, 2, 4, 6, 8, 10])); - vs.iter_mut() - .for_each(|x| { - *x = 12; - }); - assert_eq!( s1, &mut [12, 12, 12, 12, 12] ); - assert_eq!( s2, &mut [12, 12, 12, 12, 12] ); - } - #[test] - fn test_virtual_slice_index_adjacent() { - let mut input = vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 10]; - let (s1, s2) = input.split_at_mut(5); - - let mut vs = VirtualSlice::new_adjacent(s1); - vs.attach(s2); - vs[0] = 11; - vs[5] = 9; - println!("{:?}", vs); - - assert_eq!(vs, Adjacent(&mut [11, 3, 5, 7, 9, 9, 4, 6, 8, 10])); - assert_eq!( s1, &mut [11, 3, 5, 7, 9] ); - assert_eq!( s2, &mut [9, 4, 6, 8, 10] ); + fn test_merge() { + let s1 = &[34, 36, 80, 127]; + let s2 = &[-36, -22, -3, 109]; + + let mut iter = MergeIterator::new(s1.iter(), s2.iter()); + + assert_eq!(iter.next(), Some( (4,&-36) )); + assert_eq!(iter.next(), Some( (4,&-22) )); + assert_eq!(iter.next(), Some( (4,&-3) )); + assert_eq!(iter.next(), Some( (0,&34) )); + assert_eq!(iter.next(), Some( (0,&36) )); + assert_eq!(iter.next(), Some( (0,&80) )); + assert_eq!(iter.next(), Some( (1,&109) )); + assert_eq!(iter.next(), Some( (0,&127) )); + assert_eq!(iter.next(), None); } #[test] - fn test_virtual_slice_swap_shallow() { - let s1 = &mut [1, 3, 5, 7, 9]; - let s2 = &mut [2, 4, 6, 8 , 10]; - - let mut vs = VirtualSlice::new(); - vs.attach(s1); - vs.attach(s2); - vs[0] = 11; - vs[5] = 22; - println!("{:?}", vs); - assert_eq!(vs, NonAdjacent( - vec![&mut 11, &mut 3, &mut 5, &mut 7, &mut 9, &mut 22, &mut 4, &mut 6, &mut 8, &mut 10], - None - )); - - vs.swap_shallow(0,5); - // references have been swapped - assert_eq!(vs, NonAdjacent( - vec![&mut 22, &mut 3, &mut 5, &mut 7, &mut 9, &mut 11, &mut 4, &mut 6, &mut 8, &mut 10], - None - )); - // however segments haven't been affected - assert_eq!( s1, &mut [11, 3, 5, 7, 9] ); - assert_eq!( s2, &mut [22, 4, 6, 8, 10] ); - } - #[test] - fn test_virtual_slice_merge() { - let test_data: [(&mut[i32], &mut[i32], &[i32],&[i32]); 6] = [ - (&mut[-88,-29,4,84], &mut[-127,-113,-71,-54], - &[-127,-113,-88,-71], &[-54,-29,4,84]), - (&mut[5,6,7], &mut[1,2,3,4], - &[1,2,3], &[4,5,6,7]), - (&mut[-127, -81, -55, -38, 40, 78, 122, 124], &mut[-126, -123, -102, -78, -51, -44, -29, 17], - &[-127, -126, -123, -102, -81, -78, -55, -51], &[-44, -38, -29, 17, 40, 78, 122, 124]), - (&mut[-69, -18, -8, 3, 38, 68, 69, 74], &mut[-119, -83, -81, -76, -37, -13, 40, 77], - &[-119, -83, -81, -76, -69, -37, -18, -13], &[-8, 3, 38, 40, 68, 69, 74, 77]), - (&mut[-106, -82, -64, -57, 5, 23, 67, 79], &mut[-103, -85, -85, -49, -42, -38, -37, 86], - &[-106, -103, -85, -85, -82, -64, -57, -49], &[-42, -38, -37, 5, 23, 67, 79, 86]), - (&mut[-122, -19, 3, 51, 69, 77, 78, 115], &mut[-118, -99, 23, 23, 35, 59, 63, 75], - &[-122, -118, -99, -19, 3, 23, 23, 35], &[51, 59, 63, 69, 75, 77, 78, 115]) + fn test_merge_mut_adjacent() { + let arr:[(&mut[i32],&[i32]);11] = [ + (&mut [34, 36, 80, 127, -36, -22, -3, 109], &[-36, -22, -3, 34, 36, 80, 109, 127]), + (&mut [2,4,6,1,3,5], &[1,2,3,4,5,6]), + (&mut [1,3,5,2,4,6], &[1,2,3,4,5,6]), + (&mut [2,4,1,3,5], &[1,2,3,4,5]), + (&mut [1,3,2,4,5], &[1,2,3,4,5]), + (&mut [1,2,3,4,5], &[1,2,3,4,5]), + (&mut [2,1,4], &[1,2,4]), + (&mut [3,1,2], &[1,2,3]), + (&mut [1,2,3], &[1,2,3]), + (&mut [2,1], &[1,2]), + (&mut [1,2], &[1,2]), ]; - - for (s1,s2, c1, c2) in test_data { - let mut vs = VirtualSlice::new(); - vs.merge(s1); - vs.merge(s2); - assert_eq!(s1, c1); - assert_eq!(s2, c2); - } - - } - #[test] - fn test_virtual_slice_merge_multiple() - { - let s1 = &mut [5,6,7]; - let _x = &[0,0,0,0,0,0]; // wedge to break adjacency - let s2 = &mut [1,2,3,4]; - let _y = &[0,0,0,0,0,0]; // wedge to break adjacency - let s3 = &mut [10,12,14]; - let _z = &[0,0,0,0,0,0]; // wedge to break adjacency - let s4 = &mut [8,9,15,16]; - - let mut vs = VirtualSlice::new(); - vs.merge(s1); - vs.merge(s2); - vs.merge(s3); - vs.merge(s4); - - assert_eq!(s1, &mut [1,2,3]); - assert_eq!(s2, &mut [4,5,6,7]); - assert_eq!(s3, &mut [8,9,10]); - assert_eq!(s4, &mut [12,14,15,16]); + arr.into_iter() + .for_each(| (input, output) | { + let len = input.len(); + let (s1, s2) = input.split_at_mut(len >> 1); + s1.merge_mut_adjacent(s2); + assert_eq!(input, output); + }) } #[test] - fn test_virtual_slice_merge_shallow() { - let s1 = &mut [1, 3, 5, 7, 9]; - let s2 = &mut [2, 4, 6, 8, 10]; - - let mut vs = VirtualSlice::new(); - vs.attach(s1); - vs.merge_shallow(s2); - - assert_eq!( vs, NonAdjacent( - vec![&mut 1, &mut 2, &mut 3, &mut 4, &mut 5, &mut 6, &mut 7, &mut 8, &mut 9,&mut 10], - None - )); - vs.iter() - .enumerate() - .for_each(|(i,x)| assert_eq!(*x,i+1) ); + #[should_panic] + fn test_merge_mut_panic() { + let s1 = &mut [3, 5, 7]; + let _s2 = &mut [1, 3, 5]; + let s3 = &mut [2, 4, 6]; - assert_eq!( s1, &mut [1, 3, 5, 7, 9] ); - assert_eq!( s2, &mut [2, 4, 6, 8, 10] ); + // non-adjacent slices hence it should panic + s1.merge_mut_adjacent(s3); } #[test] - fn test_virtual_slice_new_iter_swap() { - let s1 = &mut [1, 3, 5, 7, 9]; - let _s3 = &mut [0, 0, 0]; - let s2 = &mut [2, 4, 6, 8 , 10]; - - { - let mut v = VirtualSlice::new(); - v.attach(s1); - v.attach(s2); - - v.iter_mut() - .for_each(|ptr| { - *ptr = 12; - }); - } - { - let mut v = VirtualSlice::new(); - v.attach(s1); - v.attach(s2); - v[0] = 11; - v[5] = 9; - } - assert_eq!(s1, &mut [11, 12, 12, 12, 12]); - assert_eq!(s2, &mut [9, 12, 12, 12, 12]); - { - let mut v = VirtualSlice::new(); - v.attach(s1); - v.attach(s2); - v.swap(0, 5); - } - assert_eq!(s1, &mut [9, 12, 12, 12, 12]); - assert_eq!(s2, &mut [11, 12, 12, 12, 12]); + fn test_merge_mut() { + let arr:[(&mut[i32],&[i32]);13] = [ + (&mut [34, 36, 80, 127, -36, -22, -3, 109], &[-36, -22, -3, 34, 36, 80, 109, 127]), + (&mut [2,4,6,1,3,5], &[1,2,3,4,5,6]), + (&mut [1,3,5,2,4,6], &[1,2,3,4,5,6]), + (&mut [5,6,7,1,2,3,4], &[1,2,3,4,5,6,7]), + (&mut [1,2,3,4,5,6,7], &[1,2,3,4,5,6,7]), + (&mut [2,4,1,3,5], &[1,2,3,4,5]), + (&mut [1,3,2,4,5], &[1,2,3,4,5]), + (&mut [1,2,3,4,5], &[1,2,3,4,5]), + (&mut [2,1,4], &[1,2,4]), + (&mut [3,1,2], &[1,2,3]), + (&mut [1,2,3], &[1,2,3]), + (&mut [2,1], &[1,2]), + (&mut [1,2], &[1,2]), + ]; + arr.into_iter() + .for_each(| (input, output) | { + let len = input.len(); + let (s1, s2) = input.split_at_mut(len >> 1); + s1.merge_mut(s2); + assert_eq!(input, output); + }) } } \ No newline at end of file diff --git a/src/merge/vs.rs b/src/merge/vs.rs new file mode 100644 index 0000000..0b9b76a --- /dev/null +++ b/src/merge/vs.rs @@ -0,0 +1,647 @@ +use std::cmp::Ordering; +use std::fmt::{Debug, Formatter}; +use std::ops::{Index, IndexMut, Range}; + +/// Constructing a VirtualSlice allowing us to operate over +/// multiple non-adjacent slice segments as a "continuous slice" +/// ``` +/// use csx3::merge::vs::VirtualSlice; +/// +/// let v = &mut [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]; +/// let (s1, s2) = v.split_at_mut(5); +/// let _s3 = &mut [0, 0, 0, 0,0]; // Wedge this to break stack continuity +/// let s4 = &mut [2, 4, 6, 8, 10]; +/// +/// let mut v = VirtualSlice::new_adjacent(s1); +/// v.merge(s2); +/// v.iter() +/// .enumerate() +/// .for_each(|(i,x)| assert_eq!(*x,i+1) ); +/// +/// assert_eq!(s1, &mut [1, 2, 3, 4, 5]); +/// assert_eq!(s2, &mut [6, 7, 8, 9, 10]); +/// +/// let mut v = VirtualSlice::new(); +/// v.attach(s1); +/// v.attach(s4); +/// v[0] = 11; +/// v[5] = 9; +/// v.swap(0, 5); +/// +/// assert_eq!(s1, &mut [9, 2, 3, 4, 5]); +/// assert_eq!(s4, &mut [11, 4, 6, 8 , 10]); +/// +/// ``` +pub enum VirtualSlice<'a, T> where T: Ord { + /// The tuple holds a vector of mutable references and the Index Reflector + NonAdjacent( Vec<&'a mut T>, Option>), + /// Holds a mutable reference to the reconstructed parent slice out of two memory adjacent slices + Adjacent( &'a mut[T] ), +} + +use VirtualSlice::{NonAdjacent, Adjacent}; + +impl<'a, T> VirtualSlice<'a, T> where T: Ord { + /// Create a new VirtualSlice for use with non-adjacent slice segments + pub fn new() -> VirtualSlice<'a, T> { + NonAdjacent( Vec::new(), None ) + } + /// Create a new VirtualSlice for use with adjacent slice segments + pub fn new_adjacent(s: &'a mut[T]) -> VirtualSlice<'a, T> { + Adjacent( s ) + } + /// Current length of the VirtualSlice is equal to sum of all attached slice segments + pub fn len(&self) -> usize { + match self { + NonAdjacent(v,_) => v.len(), + Adjacent(s) => s.len(), + } + } + pub fn is_empty(&self) -> bool { + match self { + NonAdjacent(v,_) => v.is_empty(), + Adjacent(s) => s.is_empty(), + } + } + /// Get a mutable iterator over the VirtualSlice that return mutable references &mut T + pub fn iter_mut<'b: 'a>(&'b mut self) -> VSIterMut<'b, T> where T: 'b { + VSIterMut::new(self) + } + /// Get an immutable iterator over the VirtualSlice that return mutable references &mut T + pub fn iter(&self) -> VSIter<'_, T> { + VSIter::new(self) + } + /// Attach a slice segment onto the VirtualSlice + pub fn attach(&mut self, s: &'a mut [T]) { + match self { + NonAdjacent(v, _) => { + s.iter_mut() + .for_each(|item| { + v.push(item); + }); + } + Adjacent(s0) => { + let fs: &mut [T]; + unsafe { + fs = &mut *std::ptr::slice_from_raw_parts_mut::((*s0).as_mut_ptr(), s0.len() + s.len()); + // checking they are aligned and adjacent, + // if not panic! so we prevent unpredictable behaviour + assert!(&s[0] == &fs[s0.len()]); + } + *self = VirtualSlice::new_adjacent(fs); + } + } + } + /// Deep swap; swaps the two references to the positions of the underlying slice segments + /// Operates at both adjacent and non-adjacent slices + pub fn swap(&mut self, a: usize, b:usize) { + if a == b { + return; + } + // we cannot use vv.swap as this will simply swap the position of the pointers + // rather where the pointers point to. Hence we use the pointers to swap the memory contents + unsafe { + std::ptr::swap::( + &mut self[a] as *mut T, + &mut self[b] as *mut T + ) + } + } + /// Perform a deep merge by ordering the referred values hence mutating the slice segments + pub fn merge(&mut self, s: &'a mut [T]) -> usize + where T: Ord { + // we are not interested in the index reflector here so we don't store it + let (inversion, _) = self._merge(s, VirtualSlice::swap); + inversion + } + /// Shallow swap; swaps the references of the underlying slice segments. The segments aren't affected + /// Operates only with non-adjacent slices + pub fn swap_shallow(&mut self, a: usize, b:usize) { + if let NonAdjacent(v, _) = self { + v.swap(a, b); + } else { + panic!("Not applicable for Adjacent VirtualSlices; use with VirtualSlice::new() instead"); + } + } + /// Perform a shallow merge by ordering the VirtualSlice's references and not the referred values. + /// The VirtualSlice can be used as sort-mask layer above the slice segments, which later can be superimposed over + /// In case of non-adjacent slices only. + pub fn merge_shallow(&mut self, s: &'a mut [T]) -> usize + where T: Ord { + let (inversions, idx_rfl) = self._merge(s, VirtualSlice::swap_shallow); + + match self { + Adjacent(_) => panic!("merge_shallow(): cannot operate in adjacent mode"), + NonAdjacent(_, idx_reflector) => { + // we need to store index reflector in case we want to mutate the attached slices via the impose method + *idx_reflector = idx_rfl; + inversions + } + } + } + + /// Superimposes O(n-1) the derived order onto the attached slice segments. + /// The stored Index Reflector contains the order per reference + pub fn superimpose_shallow_merge(&mut self) { + // total operations must be len()-1 as we use 1 position as temp swap location + let total_swaps = self.len() - 2; + // Count total number of swaps occurred + let mut swap_count = 0usize; + // holds the current temp swap position + let mut temp_idx = 0usize; + + // make sure entry conditions are correct + // prefer to panic as non of those scenarios should be recoverable + // otherwise, extract internal data and proceed with algorithm + match self { + Adjacent(_) => panic!("superimpose_shallow_merge(): call doesn't work over adjacent slice segments"), + NonAdjacent(_, None) => panic!("superimpose_shallow_merge(): Index Reflector does not exist. Did merge_shallow() run ?"), + NonAdjacent(vs, Some(idx)) => { + + // Exit conditions are either, + // - total swaps == total number of elements - 1 OR + // - current tmp index position has reached the end of VirtualSlice (Case: virtualslice already ordered; zero swaps) + while swap_count < total_swaps && temp_idx < total_swaps + { + let mut i; + // Exit condition + // - current swap index == correct ordered position, (item is positioned where it should be) + while temp_idx != idx[temp_idx] { + i = idx[temp_idx]; + idx.swap(temp_idx, i); + unsafe { + // we need to overcome Rust's borrow checking + // as we cannot use self.swap() here + std::ptr::swap::(&mut *vs[temp_idx] as *mut T, &mut *vs[i] as *mut T); + } + swap_count += 1; + } + temp_idx += 1; + } + } + } + } + + /// Merge Self with another non-adjacent slice using in-place memory swaps + /// For the algorithm to work we need the following components + /// - Append VirtualSlice with given &mut slice so to form a "continuous slice" + /// - Use for slice comparison an "Index Reflector (idx_rfl)" table to "project" (c,i') positions upon the "continuous slice" as (c', i) + /// - Swap action at once both (a) continuous slice and (b) Index Reflector + /// ``` + /// //Slice 1 Slice 2 VirtualSlice Index Reflector + /// //======= ========= ========================= ============= + /// //[5,6,7] <> [1,2,3,4] [5(c'/i),6,7,1(j),2,3,4] [1(c/i'),2,3,] + /// //[1,6,7] <> [5,2,3,4] [1,6(i),7,5(c'),2(j),3,4] [4(c),2(i'),3] + /// //[1,2,7] <> [5,6,3,4] [1,2,7(i),5(c'),6,3(j),4] [4(c),5,3(i')] + /// //[1,2,3] <> [5,6,7,4] [1,2,3,5(c'/i),6,7,4(j)] [4(c/i'),5,6,] + /// //[1,2,3] <> [4,6,7,5] [1,2,3,4,6(i),7,5(c')](j) [7(c),5(i'),6] <-- Phase 1: Main merge finished but still i < c' + /// //[1,2,3] <> [4,5,7,6] [1,2,3,4,5,7(i),6(c')](j) [5,7(c),6(i')] Trick: reflector knows the right order remaining + /// //[1,2,3] <> [4,6,7,5] [1,2,3,4,5,6,7(i/c')](j) [5,6,7(c/i') ] <-- Phase 2: finished merging (reflects starting position) + /// + /// use csx3::merge::vs::VirtualSlice; + /// let s1 = &mut [5,6,7]; + /// let _s = &[0,0,0,0,0,0]; // wedge to break adjacency + /// let s2 = &mut [1,2,3,4]; + /// + /// let mut vs = VirtualSlice::new(); + /// + /// vs.merge(s1); + /// vs.merge(s2); + /// + /// assert_eq!(s1, &[1,2,3]); + /// assert_eq!(s2, &[4,5,6,7]); + /// ``` + fn _merge(&mut self, s: &'a mut [T], mut f_swap: F) -> (usize, Option>) + where T: Ord , + F: FnMut(&mut Self, usize, usize) { + + if self.is_empty() { + self.attach(s); + return (0, None) + } + + // j = s2[j] equivalent position within the working slice (j') and index reflector (j) + let mut j = self.len(); + + // attach slice to be merged with self + self.attach(s); + + // i = partition position in working slice so that ... [merged elements] < ws[i] < [unmerged elements] + // p = index reflector partition bound where i's position is always upper bounded by p + // c = s1[c] equivalent position in the index reflector, so that idx_rfl[c] == c' == s1[c] equivalent in ws[c'], + // used for optimising finding i pos in index array + let (mut inv_count, mut c, mut i, p) = (0usize, 0usize, 0usize, j); + + // ws_len = working slice's length = self.len + s.len + let ws_len = self.len(); + + // Memory Optimisation: we could build the index reflector of size [ 0 .. size of left slice] since the following properties apply + // - c & i' will never exceed size of left slice + // - j == j' always be the same position + let mut idx_rfl = (0..ws_len).into_iter().collect::>(); + + //println!("Merge:{:?} :: {:?} ({:?},{:?},{:?})", self, idx_rfl, i, j, c); + + let c_bound = p-1; + // Memory Optimisation: if idx_len() = s1.len() then use: + //let c_bound = idx_rfl.len()-1; + let i_bound = ws_len-1; + loop { + // Flattening/de-normalising the workflow logic + // ============================================ + // A: (i != j or j < ws_len ) => Any more comparisons required ? is everything in ws[..i] << ws[j] ? + // B: ( i != [c] where i < ws_len-1, c < p-1 ) => Have all left slice elements been processed ? Have we reached the end where i == [c] ? + // +------+-------+----------+--------------------------------------- + // | A | B | if Guard | Action + // +------+-------+----------+--------------------------------------- + // | true | true | l > r | Phase 1: swap right with pivot + // | true | false | N/A | Exit: Merge completed; finished left part, right part remaining is ordered + // | true | true | l > r | Phase 1: l<=r implied; swap left with pivot + // |false | true | ANY | Phase 2: finish remaining left items + // |false | false | N/A | Exit: Merge completed + // +------+-------+----------+--------------------------------------- + // + match (j < ws_len && i != j, i < i_bound && c < c_bound) { + (true, _) if self[idx_rfl[c]].cmp(&self[j]) == Ordering::Greater => { + // count the equivalent inversions + inv_count += j - i; + + // swap right slice's item in the working slice with merged partition edge ws[i] + // swap( ws[i] with ws[j'] where j' = index_reflector[j], but j' == j so + f_swap(self, i, j); + + // swap index_reflect[j] with index_reflector[i'] + // i' == index_reflector[x]; where x == i; + // e.g. i = 3rd pos, hence i' = index_reflector[x] where x == 3; + let idx = idx_rfl[c..p].iter().position(|x| *x == i).unwrap() + c; + // swap( i' with j ) + idx_rfl.swap(idx, j); + // or since always j == j' we just copy the value over no need to swap + //idx_rfl[idx] = j; + //print!("\tr:"); + // point to the next in order position (right slice) + j += 1; + }, + (_, true) => { + // condition saves cpu-cycles from zero-impact operations when i == c' (no swap) + // otherwise it has no algorithmic impact + if i != idx_rfl[c] { + // swap left slice's item in the working slice with merged partition edge ws[i] + // swap( ws[i] with ws[c'] where c' = index_reflector[c] + f_swap(self, i, idx_rfl[c]); + + // swap index_reflect[c] with index_reflector[i'] + // i' == index_reflector[x]; where x == i; + // e.g. i = 3rd pos, hence i' = index_reflector[x] where x == 3; + let idx = idx_rfl[c..p].iter().position(|x| *x == i).unwrap() + c; + //swap( i' with c ) + idx_rfl.swap(idx, c); + //print!("\tl:"); + } + // point to the next in order position (left slice) + c += 1; + }, + (_, _) => break, + }; + // Move partition by one so that [merged partition] < ws[i] < [unmerged partition] + i += 1; + //println!("Merge:{:?} :: {:?} ({:?},{:?},{:?})", self, idx_rfl, i, j, c); + }; + + //println!("Merge Done"); + (inv_count, Some(idx_rfl)) + } +} + +pub enum VSIter<'b, T> where T: Ord + 'b { + NonAdjacent( std::slice::Iter<'b, &'b mut T> ), + Adjacent( std::slice::Iter<'b, T> ), +} +impl<'b, T> VSIter<'b, T> where T: Ord + 'b{ + pub fn new(vs: &'b VirtualSlice<'b, T>) -> VSIter<'b, T> { + match vs { + NonAdjacent(v, _) => VSIter::NonAdjacent(v.iter()), + Adjacent(s) => VSIter::Adjacent(s.iter()), + } + } +} +impl<'b, T> Iterator for VSIter<'b, T> where T: Ord + 'b { + type Item = &'b T; + + fn next(&mut self) -> Option { + match self { + VSIter::NonAdjacent( vi) => { + if let Some(val) = vi.next() { + Some(*val) + } else { + None + } + }, + VSIter::Adjacent( si) => si.next(), + } + } +} +pub enum VSIterMut<'b, T> where T: Ord + 'b { + NonAdjacent( std::slice::IterMut<'b, &'b mut T> ), + Adjacent( std::slice::IterMut<'b, T> ), +} +impl<'b, T> VSIterMut<'b, T> where T: Ord + 'b { + pub fn new(vs: &'b mut VirtualSlice<'b, T>) -> VSIterMut<'b, T> { + match vs { + NonAdjacent(v, _) => VSIterMut::NonAdjacent(v.iter_mut()), + Adjacent(s) => VSIterMut::Adjacent(s.iter_mut()), + } + } +} +impl<'b, T> Iterator for VSIterMut<'b, T> + where T: Ord + 'b { + type Item = &'b mut T; + + fn next(&mut self) -> Option { + match self { + VSIterMut::NonAdjacent( vi) => { + if let Some(val) = vi.next() { + Some(*val) + } else { + None + } + }, + VSIterMut::Adjacent( si) => si.next(), + } + } +} + +impl Default for VirtualSlice<'_, T> where T: Ord { + fn default() -> Self { + VirtualSlice::new() + } +} +impl Debug for VirtualSlice<'_, T> where T : Ord + Debug { + + /// extract and display the slice subsegments attached to the virtualslice + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list() + .entries( + // returns a VSIter that serves &'a T + self.iter() + ) + .finish() + } +} +impl Index for VirtualSlice<'_, T> where T: Ord { + type Output = T; + + /// Index implementation so that VirtualSlice[x] will return a &T to the underlying slice segment + fn index(&self, index: usize) -> &Self::Output { + match self { + // syntactic overkill as rust will automatically dereference the chain of references + // but it feels good to be explicit!! + NonAdjacent(vv, _) => &(*vv[index]), + Adjacent(s) => &s[index], + } + } +} +impl IndexMut for VirtualSlice<'_, T> where T: Ord { + + /// Index implementation so that VirtualSlice[x] will return a &mut T to the underlying slice segment + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + // syntactic overkill as rust will automatically dereference the chain of references + // but it feels good to be explicit!! + match self { + NonAdjacent(vv, _) => &mut (*vv[index]), + Adjacent(s) => &mut s[index], + } + } +} +impl<'a, T> Index> for VirtualSlice<'a, T> where T: Ord { + type Output = [&'a mut T]; + + fn index(&self, index: Range) -> &Self::Output { + if let NonAdjacent(vv, _) = self { + &vv[index] + } else { + panic!() + } + } +} +impl<'a, T> IndexMut> for VirtualSlice<'a, T> where T: Ord { + fn index_mut(&mut self, index: Range) -> &mut Self::Output { + if let NonAdjacent(vv, _) = self { + &mut vv[index] + } else { + panic!() + } + } +} +impl<'a, T> PartialOrd for VirtualSlice<'a, T> where T: Ord { + /// Enable VirtualSlice comparison so we can write things like + /// ``` + /// use csx3::merge::vs::VirtualSlice; + /// let s = &mut [1,2,3,4,5]; + /// let vs = VirtualSlice::new_adjacent(s); + /// assert_eq!( vs, VirtualSlice::Adjacent( &mut [1,2,3,4,5] ) ); + /// ``` + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (NonAdjacent(v, _), NonAdjacent(o, _)) => v.partial_cmp(o), + (Adjacent(s), Adjacent(o)) => s.partial_cmp(o), + ( _, _ ) => panic!(), + } + } +} + +impl<'a, T> PartialEq for VirtualSlice<'a, T> where T: Ord { + /// Enable VirtualSlice comparison so we can write things like + /// ``` + /// use csx3::merge::vs::VirtualSlice; + /// let s = &mut [1,2,3,4,5]; + /// let vs = VirtualSlice::new_adjacent(s); + /// assert_eq!( vs, VirtualSlice::Adjacent( &mut [1,2,3,4,5] ) ); + /// ``` + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (NonAdjacent(v, _), NonAdjacent(o,_)) => v.eq(o), + (Adjacent(s), Adjacent(o)) => s.eq(o), + ( _, _ ) => panic!(), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + #[should_panic] + fn test_virtual_slice_impose_negative_1() { + let s1 = &mut [5, 6, 7]; + let mut vs = VirtualSlice::new(); + vs.attach(s1); + vs.superimpose_shallow_merge(); // there is no index_reflector yet, should do nothing + } + + #[test] + #[should_panic] + fn test_virtual_slice_impose_negative_2() { + let [(s1, s2)]: [(&mut [i32], &mut [i32]); 1] = [(&mut [5, 6, 7], &mut [1, 2, 3, 4])]; + let mut vs = VirtualSlice::new(); + + vs.attach(s1); + vs.merge(s2); // deep merge creates a reflector + vs.superimpose_shallow_merge(); // it should do nothing as the vs is already ordered + } + + #[test] + #[should_panic] + fn test_virtual_slice_adjacent_panic() { + let s1 = &mut [1, 3, 5, 7, 9]; + let _s = &mut [0, 0, 0, 0]; + let s2 = &mut [2, 4, 6, 8, 10]; + let mut vs = VirtualSlice::new_adjacent(s1); + vs.attach(s2); + } + + #[test] + fn test_virtual_slice_iter_mut_adjacent() { + let mut input = vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 10]; + let (s1, s2) = input.split_at_mut(5); + + let mut vs = VirtualSlice::new_adjacent(s1); + vs.attach(s2); + println!("{:?}", vs); + assert_eq!(vs, Adjacent(&mut [1, 3, 5, 7, 9, 2, 4, 6, 8, 10])); + vs.iter_mut() + .for_each(|x| { + *x = 12; + }); + assert_eq!(s1, &mut [12, 12, 12, 12, 12]); + assert_eq!(s2, &mut [12, 12, 12, 12, 12]); + } + + #[test] + fn test_virtual_slice_index_adjacent() { + let mut input = vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 10]; + let (s1, s2) = input.split_at_mut(5); + + let mut vs = VirtualSlice::new_adjacent(s1); + vs.attach(s2); + vs[0] = 11; + vs[5] = 9; + println!("{:?}", vs); + + assert_eq!(vs, Adjacent(&mut [11, 3, 5, 7, 9, 9, 4, 6, 8, 10])); + assert_eq!(s1, &mut [11, 3, 5, 7, 9]); + assert_eq!(s2, &mut [9, 4, 6, 8, 10]); + } + + #[test] + fn test_virtual_slice_swap_shallow() { + let s1 = &mut [1, 3, 5, 7, 9]; + let s2 = &mut [2, 4, 6, 8, 10]; + + let mut vs = VirtualSlice::new(); + vs.attach(s1); + vs.attach(s2); + vs[0] = 11; + vs[5] = 22; + println!("{:?}", vs); + assert_eq!(vs, NonAdjacent( + vec![&mut 11, &mut 3, &mut 5, &mut 7, &mut 9, &mut 22, &mut 4, &mut 6, &mut 8, &mut 10], + None + )); + + vs.swap_shallow(0, 5); + // references have been swapped + assert_eq!(vs, NonAdjacent( + vec![&mut 22, &mut 3, &mut 5, &mut 7, &mut 9, &mut 11, &mut 4, &mut 6, &mut 8, &mut 10], + None + )); + // however segments haven't been affected + assert_eq!(s1, &mut [11, 3, 5, 7, 9]); + assert_eq!(s2, &mut [22, 4, 6, 8, 10]); + } + + #[test] + fn test_virtual_slice_merge() { + let test_data: [(&mut [i32], &mut [i32], &[i32], &[i32]); 6] = [ + (&mut [-88, -29, 4, 84], &mut [-127, -113, -71, -54], + &[-127, -113, -88, -71], &[-54, -29, 4, 84]), + (&mut [5, 6, 7], &mut [1, 2, 3, 4], + &[1, 2, 3], &[4, 5, 6, 7]), + (&mut [-127, -81, -55, -38, 40, 78, 122, 124], &mut [-126, -123, -102, -78, -51, -44, -29, 17], + &[-127, -126, -123, -102, -81, -78, -55, -51], &[-44, -38, -29, 17, 40, 78, 122, 124]), + (&mut [-69, -18, -8, 3, 38, 68, 69, 74], &mut [-119, -83, -81, -76, -37, -13, 40, 77], + &[-119, -83, -81, -76, -69, -37, -18, -13], &[-8, 3, 38, 40, 68, 69, 74, 77]), + (&mut [-106, -82, -64, -57, 5, 23, 67, 79], &mut [-103, -85, -85, -49, -42, -38, -37, 86], + &[-106, -103, -85, -85, -82, -64, -57, -49], &[-42, -38, -37, 5, 23, 67, 79, 86]), + (&mut [-122, -19, 3, 51, 69, 77, 78, 115], &mut [-118, -99, 23, 23, 35, 59, 63, 75], + &[-122, -118, -99, -19, 3, 23, 23, 35], &[51, 59, 63, 69, 75, 77, 78, 115]) + ]; + + for (s1, s2, c1, c2) in test_data { + let mut vs = VirtualSlice::new(); + vs.merge(s1); + vs.merge(s2); + assert_eq!(s1, c1); + assert_eq!(s2, c2); + } + } + + #[test] + fn test_virtual_slice_merge_multiple() + { + let s1 = &mut [5, 6, 7]; + let _x = &[0, 0, 0, 0, 0, 0]; // wedge to break adjacency + let s2 = &mut [1, 2, 3, 4]; + let _y = &[0, 0, 0, 0, 0, 0]; // wedge to break adjacency + let s3 = &mut [10, 12, 14]; + let _z = &[0, 0, 0, 0, 0, 0]; // wedge to break adjacency + let s4 = &mut [8, 9, 15, 16]; + + let mut vs = VirtualSlice::new(); + vs.merge(s1); + vs.merge(s2); + vs.merge(s3); + vs.merge(s4); + + assert_eq!(s1, &mut [1, 2, 3]); + assert_eq!(s2, &mut [4, 5, 6, 7]); + assert_eq!(s3, &mut [8, 9, 10]); + assert_eq!(s4, &mut [12, 14, 15, 16]); + } + #[test] + fn test_virtual_slice_new_iter_swap() { + let s1 = &mut [1, 3, 5, 7, 9]; + let _s3 = &mut [0, 0, 0]; + let s2 = &mut [2, 4, 6, 8 , 10]; + + { + let mut v = VirtualSlice::new(); + v.attach(s1); + v.attach(s2); + + v.iter_mut() + .for_each(|ptr| { + *ptr = 12; + }); + } + { + let mut v = VirtualSlice::new(); + v.attach(s1); + v.attach(s2); + v[0] = 11; + v[5] = 9; + } + assert_eq!(s1, &mut [11, 12, 12, 12, 12]); + assert_eq!(s2, &mut [9, 12, 12, 12, 12]); + { + let mut v = VirtualSlice::new(); + v.attach(s1); + v.attach(s2); + v.swap(0, 5); + } + assert_eq!(s1, &mut [9, 12, 12, 12, 12]); + assert_eq!(s2, &mut [11, 12, 12, 12, 12]); + } + +} \ No newline at end of file diff --git a/src/select/mod.rs b/src/select/mod.rs index 7317674..59038f5 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -1,53 +1,60 @@ use std::fmt::Debug; use std::cmp::Ordering; use rand::Rng; -use crate::sort::{ - partition_at_index, - merge::{ - mergesort_mut, - merge_mut_adjacent - } +use crate::{ + sort::{ + partition_at_index, + merge::MergeSort, + }, + merge::Merge }; // ANCHOR: selection_r -/// Find the nth order statistic within an unordered set with O(n) performance -/// using nth_min as 1 will return the smallest item; 2 the second smallest, etc -/// When function returns, the input array has been rearranged so that ```item == array[ nth order ]``` -/// ``` -/// use csx3::select::r_selection; -/// -/// let (arr, nth_order) = (&mut [23,43,8,22,15,11], 1usize); -/// -/// let ret_val = r_selection(arr, nth_order); -/// assert_eq!(ret_val, &8); -/// assert_eq!(&arr[nth_order-1], &8); -/// ``` -pub fn r_selection(v: &mut [T], nth_min: usize) -> &T - where T: Copy + Ord + Debug { - - // println!("Input: {:?}::{}th", v, order_nth); - if v.len() == 1 { - return &v[0]; - } - - // pick an index at random based on a uniform distribution - let idx = rand::thread_rng().gen_range(0..(v.len()-1) ); - // find out the nth order of this sample - let (left_partition, nth, right_partition) = partition_at_index(v, idx); - - let order = left_partition.len()+1; - // println!("\tAsked:{}ord Picked:{}th, {:?} {:?}ord {:?}", nth_min, idx, left_partition, order, right_partition); +pub trait Select { + fn r_selection(&mut self, nth_min: usize) -> &T; +} - // is nth order sampled over, equal or above the desired nth_min ? - match nth_min.cmp(&order) { - // we've found the item in nth_min order - Ordering::Equal => nth, - // the nth_min is below the nth found so recurse on the left partition - Ordering::Less => - r_selection(left_partition, nth_min), - // the nth_min is above the nth found so recurse on the right partition with adjusted order - Ordering::Greater => - r_selection(right_partition, nth_min - order), +impl Select for [T] + where T: Copy + Ord { + /// Find the nth order statistic within an unordered set with O(n) performance + /// using nth_min as 1 will return the smallest item; 2 the second smallest, etc + /// When function returns, the input array has been rearranged so that ```item == array[ nth order ]``` + /// ``` + /// use csx3::select::Select; + /// + /// let (arr, nth_order) = (&mut [23,43,8,22,15,11], 1usize); + /// + /// let ret_val = arr.r_selection(nth_order); + /// assert_eq!(ret_val, &8); + /// assert_eq!(&arr[nth_order-1], &8); + /// ``` + fn r_selection(&mut self, nth_min: usize) -> &T + { + + // println!("Input: {:?}::{}th", v, order_nth); + if self.len() == 1 { + return &self[0]; + } + + // pick an index at random based on a uniform distribution + let idx = rand::thread_rng().gen_range(0..(self.len() - 1)); + // find out the nth order of this sample + let (left_partition, nth, right_partition) = partition_at_index(self, idx); + + let order = left_partition.len() + 1; + // println!("\tAsked:{}ord Picked:{}th, {:?} {:?}ord {:?}", nth_min, idx, left_partition, order, right_partition); + + // is nth order sampled over, equal or above the desired nth_min ? + match nth_min.cmp(&order) { + // we've found the item in nth_min order + Ordering::Equal => nth, + // the nth_min is below the nth found so recurse on the left partition + Ordering::Less => + left_partition.r_selection(nth_min), + // the nth_min is above the nth found so recurse on the right partition with adjusted order + Ordering::Greater => + right_partition.r_selection(nth_min - order), + } } } // ANCHOR_END: selection_r @@ -58,7 +65,7 @@ pub fn r_selection(v: &mut [T], nth_min: usize) -> &T /// The algorithm aims to find the best pivot deterministically rather pick a random value /// pub fn d_selection(v: &mut [T], nth_min: usize) -> &T - where T: Copy + Ord + Debug { + where T: Copy + Ord { // println!("DS Input: {:?}::{}th", v, nth_min); if v.len() == 1 { @@ -97,19 +104,20 @@ pub fn d_selection(v: &mut [T], nth_min: usize) -> &T } } + // ANCHOR: selection_median /// Returns a vector of N/5 medians where N = input array length /// It breaks array into N/5 sub-arrays of length 5 for cheap sorting and picking the median value /// pub fn medians_of_medians(v:&mut [T]) -> Vec - where T : Copy + Ord + Debug { + where T : Copy + Ord { // extract median of medians array // split input slice into n/5 groups of 5 v.chunks_mut(5) .map(|chunk| { // sort each group - mergesort_mut(chunk, merge_mut_adjacent); + chunk.mergesort_mut(Merge::merge_mut_adjacent); // pull the median out chunk[ chunk.len() >> 1] }) @@ -183,7 +191,7 @@ mod test { test_data.into_iter() .for_each(|(input, order, item)| { - let ret_val = r_selection(input, order); + let ret_val = input.r_selection(order); assert_eq!(item, ret_val); assert_eq!(&input[order - 1], item); }) diff --git a/src/sort/count.rs b/src/sort/count.rs index ed12c2f..2bfec29 100644 --- a/src/sort/count.rs +++ b/src/sort/count.rs @@ -99,7 +99,7 @@ fn min_max(s: &[T]) -> (T, T) where T: Copy + Ord { #[cfg(test)] mod test { use crate::random_sequence; - use crate::sort::merge::mergesort; + use crate::sort::merge::*; use super::*; #[test] fn test_countsort_head_to_head() @@ -109,7 +109,7 @@ mod test { let mut v2 = v1.clone(); v2.as_mut_slice().count_sort(); - let (_, v) = mergesort(&v1); + let (_, v) = v1.mergesort(); assert_eq!( &v, &v2 ); } } diff --git a/src/sort/merge.rs b/src/sort/merge.rs index 4653a57..eecea3e 100644 --- a/src/sort/merge.rs +++ b/src/sort/merge.rs @@ -1,172 +1,125 @@ -use std::fmt::Debug; -use crate::merge::{MergeIterator, VirtualSlice}; +use crate::merge::MergeIterator; -/// Applies memory efficient in-place merging when two slices are adjacent to each other. -/// ``` -/// use csx3::sort::merge::merge_mut_adjacent; -/// -/// let mut input = vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 10]; -/// let (s1,s2) = input.split_at_mut(5); -/// -/// merge_mut_adjacent(s1,s2); -/// assert_eq!(input, vec![1,2,3,4,5,6,7,8,9,10]); -/// ``` -/// Panics in case the two slices are found not to be adjacent. For safety, always use *ONLY* against slices that have been mutable split from an existing slice -/// #[should_panic] -/// let s1 = &mut [3, 5, 7]; -/// let s2 = &mut [1, 3, 5]; // wedge this between the two -/// let s3 = &mut [2, 4, 6]; -/// -/// merge_mut_adjacent(s1,s3); // this should throw a panic -/// -/// There is no warranty that Rust will maintain two slice adjacent in a case like this. -/// let s1 = &mut [3, 5, 7]; -/// let s3 = &mut [2, 4, 6]; -/// -/// merge_mut_adjacent(s1,s3); // this may not always work -/// -pub fn merge_mut_adjacent(s1: &mut[T], s2:&mut[T]) -> usize - where T: Ord + Debug -{ - // println!("\tInput: {:?},{:?}", s1, s2); - - let mut ws = VirtualSlice::new_adjacent(s1); - ws.merge(s2) -} - -/// Merge two non-adjacent slices using in-place memory swaps and without use of rotations -/// ``` -/// use csx3::sort::merge::merge_mut; -/// -/// let s1 = &mut [5,6,7]; -/// let _s = &[0,0,0,0,0,0]; // wedge to break adjacency -/// let s2 = &mut [1,2,3,4]; -/// -/// let inv = merge_mut(s1,s2); -/// -/// assert_eq!(s1, &[1,2,3]); -/// assert_eq!(s2, &[4,5,6,7]); -/// ``` -pub fn merge_mut(s1: &mut[T], s2:&mut[T]) -> usize - where T: Ord + Debug { - - //println!("Merge Input: {:?},{:?}", s1, s2); - - let mut ws = VirtualSlice::new(); - - ws.merge(s1); - ws.merge(s2) -} // ANCHOR: sort_merge_mut -/// Sort function based on the merge sort algorithm -/// Sorts the mutable vector with in-place operations -/// while it returns the total count of inversions occurred -/// -/// The following functions are available to use as passing parameter -/// - merge_mut : safe to use with non-adjacent; time: O(n+m), space: O(2n+m)*usize -/// - merge_mut_adjacent : use only when slices are adjacent in memory: time: O(n+m), space: O(n)*usize -/// -/// ``` -/// use csx3::sort::merge::{merge_mut_adjacent, mergesort_mut}; -/// -/// let input = &mut [8, 4, 2, 1]; -/// -/// assert_eq!( mergesort_mut(input, merge_mut_adjacent), 6 ); -/// assert_eq!( input, &[1,2,4,8] ); -/// ``` -pub fn mergesort_mut(v: &mut [T], mut fn_merge: F ) -> usize - where T: Ord + Debug, - F: Copy + FnMut(&mut[T], &mut[T]) -> usize { - - let len = v.len(); +pub trait MergeSort + where T : Ord { + fn mergesort_mut(&mut self, fn_merge: F ) -> usize + where F: Copy + FnMut(&mut[T], &mut[T]) -> usize; + fn mergesort(&self) -> (usize, Vec); +} - //println!("\tInput: ({}){:?} =>", len, v); - match len { - // unity slice, just return it - 0..=1 => (0), - // sort the binary slice and exit - // use a local variable to eliminate the need for &mut as input - // and given we output a new vector - 2 => { - if v[0] > v[1] { - v.swap(0, 1); - return 1usize +impl MergeSort for [T] + where T: Copy + Clone + Ord { + /// Sort function based on the merge sort algorithm + /// Sorts the mutable vector with in-place operations + /// while it returns the total count of inversions occurred + /// + /// The following functions are available to use as passing parameter + /// - merge_mut : safe to use with non-adjacent; time: O(n+m), space: O(2n+m)*usize + /// - merge_mut_adjacent : use only when slices are adjacent in memory: time: O(n+m), space: O(n)*usize + /// + /// ``` + /// use csx3::{ merge::Merge, sort::merge::MergeSort }; + /// + /// let input = &mut [8, 4, 2, 1]; + /// + /// assert_eq!( input.mergesort_mut(Merge::merge_mut_adjacent), 6 ); + /// assert_eq!( input, &[1,2,4,8] ); + /// ``` + fn mergesort_mut(&mut self, mut fn_merge: F ) -> usize + where F: Copy + FnMut(&mut[T], &mut[T]) -> usize { + + let len = self.len(); + + //println!("\tInput: ({}){:?} =>", len, v); + match len { + // unity slice, just return it + 0..=1 => (0), + // sort the binary slice and exit + // use a local variable to eliminate the need for &mut as input + // and given we output a new vector + 2 => { + if self[0] > self[1] { + self.swap(0, 1); + return 1usize + } + 0usize + }, + // if slice length longer than 2 then split recursively + _ => { + let (left, right) = self.split_at_mut(len >> 1); + let left_inv = left.mergesort_mut(fn_merge); + let right_inv = right.mergesort_mut(fn_merge); + + // merge the two slices taking an in-place merging approach - no additional memory + // plus return the total inversions occured + let merge_inv = fn_merge(left, right); + + //println!("\tMerged: {:?}{:?} => {}", left, right, left_inv + right_inv + merge_inv); + left_inv + right_inv + merge_inv } - 0usize - }, - // if slice length longer than 2 then split recursively - _ => { - let (left, right) = v.split_at_mut(len >> 1); - let left_inv = mergesort_mut(left, fn_merge); - let right_inv = mergesort_mut(right, fn_merge); - - // merge the two slices taking an in-place merging approach - no additional memory - // plus return the total inversions occured - let merge_inv = fn_merge(left, right); - - //println!("\tMerged: {:?}{:?} => {}", left, right, left_inv + right_inv + merge_inv); - left_inv + right_inv + merge_inv } } -} -// ANCHOR_END: sort_merge_mut - -// ANCHOR: sort_merge -/// Sort function based on the merge sort algorithm -/// Returns a new sorted vector given an input reference slice - heap allocations -/// along with the total count of inversions occurred -/// ``` -/// use csx3::sort::merge::mergesort; -/// -/// let input = &[8, 4, 2, 1]; -/// -/// assert_eq!( mergesort(input), (6, vec![1,2,4,8]) ); -/// ``` -pub fn mergesort(v: &[T]) -> (usize, Vec) - where T: Copy + Clone + Ord { - - let len = v.len(); - - //println!("\tInput: ({}){:?} =>", len, v); - match len { - // unity slice, just return it - 0..=1 => (0, v.to_vec()), - // sort the binary slice and exit - // use a local variable to eliminate the need for &mut as input - // and given we output a new vector - 2 => { - let mut inv_count = 0usize; - let mut output = v.to_vec(); - if v[0] > v[1] { - output.swap(0, 1); - inv_count += 1; + // ANCHOR_END: sort_merge_mut + + // ANCHOR: sort_merge + /// Sort function based on the merge sort algorithm + /// Returns a new sorted vector given an input reference slice - heap allocations + /// along with the total count of inversions occurred + /// ``` + /// use csx3::sort::merge::MergeSort; + /// + /// let input = &[8, 4, 2, 1]; + /// + /// assert_eq!( input.mergesort(), (6, vec![1,2,4,8]) ); + /// ``` + fn mergesort(&self) -> (usize, Vec) { + + let len = self.len(); + + //println!("\tInput: ({}){:?} =>", len, v); + match len { + // unity slice, just return it + 0..=1 => (0, self.to_vec()), + // sort the binary slice and exit + // use a local variable to eliminate the need for &mut as input + // and given we output a new vector + 2 => { + let mut inv_count = 0usize; + let mut output = self.to_vec(); + if self[0] > self[1] { + output.swap(0, 1); + inv_count += 1; + } + (inv_count, output) + }, + // if slice length longer than 2 then split recursively + _ => { + let (left, right) = self.split_at(len >> 1); + let (left_inv, left) = left.mergesort(); + let (right_inv, right) = right.mergesort(); + + // return a vector of the merged but ordered slices + // plus inversions vector; inversion count per position + let (merge_vec, output ):( Vec<_>, Vec) = MergeIterator::new(left.iter(),right.iter()).unzip(); + // println!("\tInversion Vector: {:?}", &merge_vec); + + // sum up the inversion count vector + let merge_inv : usize = merge_vec.into_iter().filter(|x| *x > 0).sum(); + //println!("\tInversion Vector: {:?}", &merge_vec); + + //println!("\tMerged: {:?}{:?} => {}", left, right, left_inv + right_inv + merge_inv); + (left_inv + right_inv + merge_inv, output) } - (inv_count, output) - }, - // if slice length longer than 2 then split recursively - _ => { - let (left, right) = v.split_at(len >> 1); - let (left_inv, left) = mergesort(left); - let (right_inv, right) = mergesort(right); - - // return a vector of the merged but ordered slices - // plus inversions vector; inversion count per position - let (merge_vec, output ):( Vec<_>, Vec) = MergeIterator::new(left.iter(),right.iter()).unzip(); - // println!("\tInversion Vector: {:?}", &merge_vec); - - // sum up the inversion count vector - let merge_inv : usize = merge_vec.into_iter().filter(|x| *x > 0).sum(); - //println!("\tInversion Vector: {:?}", &merge_vec); - - //println!("\tMerged: {:?}{:?} => {}", left, right, left_inv + right_inv + merge_inv); - (left_inv + right_inv + merge_inv, output) } } -} // ANCHOR_END: sort_merge +} + #[cfg(test)] mod test { use crate::random_sequence; + use crate::merge::Merge; use super::*; #[test] fn test_merge_sort_mut() { @@ -182,7 +135,7 @@ mod test { test_data.into_iter() .for_each(|(input,(inv_count, output))| { - assert_eq!(mergesort_mut(input, merge_mut), inv_count ); + assert_eq!(input.mergesort_mut(Merge::merge_mut), inv_count ); assert_eq!( input, output ); }) } @@ -199,94 +152,19 @@ mod test { test_data.into_iter() .for_each(|(input,(inv_count, output))| { - assert_eq!(mergesort_mut(input, merge_mut_adjacent), inv_count ); + assert_eq!(input.mergesort_mut(Merge::merge_mut_adjacent), inv_count ); assert_eq!( input, output ); }) } #[test] - fn test_merge() { - let s1 = &[34, 36, 80, 127]; - let s2 = &[-36, -22, -3, 109]; - - let mut iter = MergeIterator::new(s1.iter(), s2.iter()); - - assert_eq!(iter.next(), Some( (4,&-36) )); - assert_eq!(iter.next(), Some( (4,&-22) )); - assert_eq!(iter.next(), Some( (4,&-3) )); - assert_eq!(iter.next(), Some( (0,&34) )); - assert_eq!(iter.next(), Some( (0,&36) )); - assert_eq!(iter.next(), Some( (0,&80) )); - assert_eq!(iter.next(), Some( (1,&109) )); - assert_eq!(iter.next(), Some( (0,&127) )); - assert_eq!(iter.next(), None); - } - #[test] - fn test_merge_mut_adjacent() { - let arr:[(&mut[i32],&[i32]);11] = [ - (&mut [34, 36, 80, 127, -36, -22, -3, 109], &[-36, -22, -3, 34, 36, 80, 109, 127]), - (&mut [2,4,6,1,3,5], &[1,2,3,4,5,6]), - (&mut [1,3,5,2,4,6], &[1,2,3,4,5,6]), - (&mut [2,4,1,3,5], &[1,2,3,4,5]), - (&mut [1,3,2,4,5], &[1,2,3,4,5]), - (&mut [1,2,3,4,5], &[1,2,3,4,5]), - (&mut [2,1,4], &[1,2,4]), - (&mut [3,1,2], &[1,2,3]), - (&mut [1,2,3], &[1,2,3]), - (&mut [2,1], &[1,2]), - (&mut [1,2], &[1,2]), - ]; - arr.into_iter() - .for_each(| (input, output) | { - let len = input.len(); - let (s1, s2) = input.split_at_mut(len >> 1); - merge_mut_adjacent(s1, s2); - assert_eq!(input, output); - }) - } - #[test] - #[should_panic] - fn test_merge_mut_panic() { - let s1 = &mut [3, 5, 7]; - let _s2 = &mut [1, 3, 5]; - let s3 = &mut [2, 4, 6]; - - // non-adjacent slices hence it should panic - merge_mut_adjacent(s1, s3); - } - #[test] - fn test_merge_mut() { - let arr:[(&mut[i32],&[i32]);13] = [ - (&mut [34, 36, 80, 127, -36, -22, -3, 109], &[-36, -22, -3, 34, 36, 80, 109, 127]), - (&mut [2,4,6,1,3,5], &[1,2,3,4,5,6]), - (&mut [1,3,5,2,4,6], &[1,2,3,4,5,6]), - (&mut [5,6,7,1,2,3,4], &[1,2,3,4,5,6,7]), - (&mut [1,2,3,4,5,6,7], &[1,2,3,4,5,6,7]), - (&mut [2,4,1,3,5], &[1,2,3,4,5]), - (&mut [1,3,2,4,5], &[1,2,3,4,5]), - (&mut [1,2,3,4,5], &[1,2,3,4,5]), - (&mut [2,1,4], &[1,2,4]), - (&mut [3,1,2], &[1,2,3]), - (&mut [1,2,3], &[1,2,3]), - (&mut [2,1], &[1,2]), - (&mut [1,2], &[1,2]), - ]; - arr.into_iter() - .for_each(| (input, output) | { - let len = input.len(); - let (s1, s2) = input.split_at_mut(len >> 1); - merge_mut(s1, s2); - assert_eq!(input, output); - }) - } - #[test] fn test_mergesort_head_to_head() { for _ in 0..127 { let v1: Vec = random_sequence(512); let mut v2 = v1.clone(); - let inv = mergesort_mut(&mut v2, merge_mut); - assert_eq!( mergesort(&v1), (inv, v2) ); + let inv = v2.mergesort_mut(Merge::merge_mut); + assert_eq!( v1.mergesort(), (inv, v2) ); } } } \ No newline at end of file diff --git a/src/sort/quick.rs b/src/sort/quick.rs index d60c634..06c37d1 100644 --- a/src/sort/quick.rs +++ b/src/sort/quick.rs @@ -1,33 +1,39 @@ -use std::fmt::Debug; use rand::Rng; use super::partition_at_index; // ANCHOR: sort_quick -/// Sorts a given array using the Quick Sort algorithm. -/// The function rearranges the array contents rather than returning a new sorted copy of the input array -/// ``` -/// use csx3::sort::quick::quick_sort; -/// -/// let v = &mut [3,5,8,1,2,4,6,0]; -/// -/// quick_sort(v); -/// assert_eq!(v, &[0,1,2,3,4,5,6,8]); -/// ``` -pub fn quick_sort(v: &mut [T]) - where T: Copy + Clone + Ord + Debug { +pub trait QuickSort { + fn quick_sort(&mut self); +} - // have we reached the end of the recursion ? - if v.len() < 2 { - return; - } - // pick an index at random based on a uniform distribution - let idx = rand::thread_rng().gen_range(0..(v.len()-1) ); - // partition the array into to mutable slices for further sorting - let (left_partition,_ , right_partition) = partition_at_index(v, idx); +impl QuickSort for [T] + where T: Copy + Clone + Ord { + + /// Sorts a given array using the Quick Sort algorithm. + /// The function rearranges the array contents rather than returning a new sorted copy of the input array + /// ``` + /// use csx3::sort::quick::QuickSort; + /// + /// let v = &mut [3,5,8,1,2,4,6,0]; + /// + /// v.quick_sort(); + /// assert_eq!(v, &[0,1,2,3,4,5,6,8]); + /// ``` + fn quick_sort(&mut self) { - // Recurse against left an right partitions - quick_sort(left_partition); - quick_sort(right_partition); + // have we reached the end of the recursion ? + if self.len() < 2 { + return; + } + // pick an index at random based on a uniform distribution + let idx = rand::thread_rng().gen_range(0..(self.len()-1) ); + // partition the array into to mutable slices for further sorting + let (left_partition,_ , right_partition) = partition_at_index(self, idx); + + // Recurse against left an right partitions + left_partition.quick_sort(); + right_partition.quick_sort(); + } } // ANCHOR_END: sort_quick @@ -48,7 +54,7 @@ mod test { test_data.into_iter() .for_each( | (input, output) | { - quick_sort(input); + input.quick_sort(); assert_eq!(input, output); }) }