diff --git a/src/lib.rs b/src/lib.rs index a60538a..c8cc051 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ use std::{ - fmt::{Debug, Display, Write}, + fmt::{Debug, Display}, ops::Range, }; @@ -11,10 +11,34 @@ pub enum Operation { /// No operation Nop, /// Insert + /// + /// Insert n items at the given position. + /// + /// **Example:** + /// ```txt + /// Ins { pos: 1, val: [4, 5] } + /// [1,|2, 3] => [1, 4, 5, 2, 3] + /// ``` Ins { pos: usize, val: Vec }, /// Delete + /// + /// Delete n items at the given position. + /// + /// **Example:** + /// ```txt + /// Del { pos: 1, n: 2 } + /// [1, (2, 3), 4, 5] => [1, 4, 5] + /// ``` Del { pos: usize, n: usize }, /// Move + /// + /// Take n items at the given position and move them to another position. + /// + /// **Example:** + /// ```txt + /// Mov { pos: 0, n: 2, to: 4 } + /// [(1, 2), 3, 4,|5] => [3, 4, (1, 2), 5] + /// ``` Mov { pos: usize, n: usize, to: usize }, /// Sequence of operations Seq { ops: Vec> }, @@ -22,9 +46,14 @@ pub enum Operation { impl Operation { /// Get the number of elemets affected by the operation + /// + /// # Panics + /// + /// Panics if the operation is a [`Operation::Seq`] pub fn len(&self) -> usize { match self { - Operation::Nop | Operation::Seq { .. } => 0, + Operation::Nop => 0, + Operation::Seq { .. } => panic!("SEQ has no len"), Operation::Ins { val, .. } => val.len(), Operation::Del { n, .. } => *n, Operation::Mov { n, .. } => *n, @@ -32,9 +61,13 @@ impl Operation { } /// Get the starting position of the operation + /// + /// # Panics + /// + /// Panics if the operation is a [`Operation::Nop`] or [`Operation::Seq`] pub fn pos(&self) -> usize { match self { - Operation::Nop | Operation::Seq { .. } => 0, + Operation::Nop | Operation::Seq { .. } => panic!("NOP/SEQ has no pos"), Operation::Ins { pos, .. } | Operation::Del { pos, .. } | Operation::Mov { pos, .. } => *pos, @@ -42,9 +75,13 @@ impl Operation { } /// Get the range of the operation + /// + /// # Panics + /// + /// Panics if the operation is a [`Operation::Nop`] or [`Operation::Seq`] pub fn range(&self) -> Range { match self { - Operation::Nop | Operation::Seq { .. } => 0..0, + Operation::Nop | Operation::Seq { .. } => panic!("NOP/SEQ has no range"), Operation::Ins { pos, val } => *pos..(*pos + val.len()), Operation::Del { pos, n } => *pos..(*pos + *n), Operation::Mov { pos, n, .. } => *pos..(*pos + *n), @@ -52,9 +89,13 @@ impl Operation { } /// Get the target range of the operation + /// + /// # Panics + /// + /// Panics if the operation is a [`Operation::Nop`] or [`Operation::Seq`] pub fn target_range(&self) -> Range { match self { - Operation::Nop | Operation::Seq { .. } => 0..0, + Operation::Nop | Operation::Seq { .. } => panic!("NOP/SEQ has no target"), Operation::Ins { pos, val } => *pos..(*pos + val.len()), Operation::Del { pos, .. } => *pos..*pos, Operation::Mov { to, n, .. } => *to..(*to + *n), @@ -68,7 +109,7 @@ impl Operation { /// Panics if the operation is a [`Operation::Nop`] or [`Operation::Seq`] fn set_pos(&mut self, n_pos: usize) { match self { - Operation::Nop | Operation::Seq { .. } => panic!("cannot move nop/seq"), + Operation::Nop | Operation::Seq { .. } => panic!("cannot move NOP/SEQ"), Operation::Ins { pos, .. } | Operation::Del { pos, .. } | Operation::Mov { pos, .. } => { @@ -134,11 +175,23 @@ impl Operation { Operation::Nop => true, Operation::Ins { val, .. } => val.is_empty(), Operation::Del { n, .. } => *n == 0, - Operation::Mov { pos, n, to } => *n == 0 || pos == to, + Operation::Mov { pos, n, to } => { + *n == 0 || pos == to || (*to >= *pos && *to <= *pos + *n) + } Operation::Seq { ops } => ops.iter().all(Self::is_empty), } } + fn opname(&self) -> &'static str { + match self { + Operation::Nop => "NOP", + Operation::Ins { .. } => "INS", + Operation::Del { .. } => "DEL", + Operation::Mov { .. } => "MOV", + Operation::Seq { .. } => "SEQ", + } + } + /// Print the operation as it is applied on the given vector pub fn print_on(&self, vec: &[T]) -> Vec where @@ -151,300 +204,92 @@ impl Operation { } return v; } + + print!("// {}: [", self.opname()); + if matches!(self, Operation::Nop) { - print!(" NOP:"); - for itm in vec { - print!(" {itm} "); + for (i, itm) in vec.iter().enumerate() { + if i > 0 { + print!(", "); + } + print!("{itm}"); } - println!(); + println!("]"); return vec.to_vec(); } let r = self.range(); + let r2 = self.target_range(); let ins = matches!(self, Operation::Ins { .. }); + let del = matches!(self, Operation::Del { .. }); + let mov = matches!(self, Operation::Mov { .. }); - print!("FROM:"); for (i, itm) in vec.iter().enumerate() { + if i > 0 { + print!(", "); + } if i == r.start { if ins { print!("|"); } else { print!("("); } - } else { - print!(" "); + } + if mov && i == r2.start { + print!("|"); } print!("{itm}"); if i == r.end.saturating_sub(1) && !ins { print!(")"); - } else { - print!(" "); } } - println!(); + print!("] => ["); + + let rt = if mov && r2.start > r.start { + (r2.start - r2.len())..r2.start + } else { + r2.clone() + }; - let r2 = self.target_range(); - let del = matches!(self, Operation::Del { .. }); let mut v2 = vec.to_vec(); self.clone().apply(&mut v2); - print!(" TO:"); for (i, itm) in v2.iter().enumerate() { - if i == r2.start { + if i > 0 { + print!(", "); + } + if i == rt.start { if del { print!("|"); } else { print!("("); } - } else { - print!(" "); } print!("{itm}"); - if i == r2.end.saturating_sub(1) && !del { + if i == rt.end.saturating_sub(1) && !del { print!(")"); - } else { - print!(" "); } } - println!(); + println!("]"); v2 } - /// Print the operation as it is applied on the given vector - pub fn print_on_string(&self, vec: &[T]) -> (Vec, String) - where - T: Display + Clone, - { - let mut res = String::new(); - - if let Operation::Seq { ops } = self { - let mut v = vec.to_vec(); - for op in ops { - let ores = op.print_on_string(&v); - v = ores.0; - res += &ores.1; - } - return (v, res); - } - if matches!(self, Operation::Nop) { - res += " NOP:"; - for itm in vec { - _ = write!(&mut res, " {itm} "); - } - res.push('\n'); - return (vec.to_vec(), res); - } - - let r = self.range(); - let ins = matches!(self, Operation::Ins { .. }); - - res += "FROM:"; - for (i, itm) in vec.iter().enumerate() { - if i == r.start { - if ins { - res.push('|'); - } else { - res.push('('); - } - } else { - res.push(' '); - } - _ = write!(&mut res, "{itm}"); - if i == r.end.saturating_sub(1) && !ins { - res.push(')'); - } else { - res.push(' '); - } - } - res.push('\n'); - - let r2 = self.target_range(); - let del = matches!(self, Operation::Del { .. }); - let mut v2 = vec.to_vec(); - self.clone().apply(&mut v2); - res += " TO:"; - for (i, itm) in v2.iter().enumerate() { - if i == r2.start { - if del { - res.push('|'); - } else { - res.push('('); - } - } else { - res.push(' '); - } - _ = write!(&mut res, "{itm}"); - if i == r2.end.saturating_sub(1) && !del { - res.push(')'); - } else { - res.push(' '); - } - } - res.push('\n'); - (v2, res) - } - fn sanitize(&mut self) { - if self.is_empty() { - *self = Operation::Nop; + if let Operation::Seq { ops } = self { + ops.iter_mut().for_each(Operation::sanitize); + for i in (0..ops.len()).rev() { + if matches!(ops[i], Operation::Nop) { + ops.remove(i); + } + } + if ops.is_empty() { + *self = Operation::Nop; + } else if ops.len() == 1 { + *self = ops.pop().unwrap(); + } return; } - if let Operation::Mov { pos, n, to } = self { - // Invalid move operation - if *to >= *pos && *to <= *pos + *n { - *self = Operation::Nop; - return; - } - } - } - - /// During mov/del transformations with multiple items, mov operations are split - /// into multiple 1-item long operations. - /// - /// After transformation, these can be merged together if the positions of subsequent - /// operations line up. - /// - /// The function also collapses nested sequences and removes null operations. - fn consolidate_seq(&mut self) { - if let Operation::Seq { ops } = self { - struct MovTmp { - pos: usize, - to: usize, - n: usize, - dir: Option, - } - - struct DelTmp { - pos: usize, - n: usize, - dir: Option, - } - - let mut mov_tmp: Option = None; - let mut del_tmp: Option = None; - let mut buf = Vec::new(); - - fn do_push( - buf: &mut Vec>, - op: Option>, - mov_tmp: &mut Option, - del_tmp: &mut Option, - ) { - if let Some(dt) = del_tmp { - buf.push(Operation::Del { - pos: dt.pos, - n: dt.n, - }); - *del_tmp = None; - } - - if let Some(mt) = mov_tmp { - buf.push(Operation::Mov { - pos: if mt.dir.unwrap_or_default() { - mt.pos + 1 - mt.n - } else { - mt.pos - }, - n: mt.n, - to: if mt.dir.unwrap_or_default() { - mt.to + 1 - mt.n - } else { - mt.to - }, - }); - *mov_tmp = None; - } - - if let Some(op) = op { - buf.push(op); - } - } - - fn proc_op( - buf: &mut Vec>, - op: Operation, - mov_tmp: &mut Option, - del_tmp: &mut Option, - ) { - match op { - Operation::Ins { .. } => do_push(buf, Some(op), mov_tmp, del_tmp), - Operation::Del { pos, n } => { - if let Some(dt) = del_tmp { - if dt.dir.unwrap_or(true) && pos == dt.pos { - // delete from left to right (n times same index) - dt.dir = Some(true); - dt.n += n; - } else if !dt.dir.unwrap_or_default() && pos + n == dt.pos { - // delete from right to left (i-n) - dt.dir = Some(false); - dt.pos = pos; - dt.n += n; - } else { - do_push(buf, None, mov_tmp, del_tmp); - *del_tmp = Some(DelTmp { pos, n, dir: None }); - } - } else { - *del_tmp = Some(DelTmp { pos, n, dir: None }); - } - } - Operation::Mov { pos, n, to } => { - if n == 1 { - if let Some(mt) = mov_tmp { - if mt.dir.unwrap_or(true) - && mt.pos.checked_sub(mt.n).is_some_and(|p| pos == p) - && mt.to.checked_sub(mt.n).is_some_and(|t| to == t) - { - // move right (to > pos) - mt.dir = Some(true); - mt.n += 1; - } else if !mt.dir.unwrap_or(false) - && pos == mt.pos + mt.n - && to == mt.to + mt.n - { - // move left (to <= pos) - mt.dir = Some(false); - mt.n += 1; - } else { - do_push(buf, None, mov_tmp, del_tmp); - *mov_tmp = Some(MovTmp { - pos, - to, - n: 1, - dir: None, - }); - } - } else { - *mov_tmp = Some(MovTmp { - pos, - to, - n: 1, - dir: None, - }); - } - } else { - do_push(buf, Some(op), mov_tmp, del_tmp); - } - } - Operation::Seq { ops } => { - ops.into_iter() - .for_each(|op| proc_op(buf, op, mov_tmp, del_tmp)); - } - Operation::Nop => {} - } - } - - ops.reverse(); - while let Some(op) = ops.pop() { - proc_op(&mut buf, op, &mut mov_tmp, &mut del_tmp); - } - do_push(&mut buf, None, &mut mov_tmp, &mut del_tmp); - - *self = if buf.is_empty() { - Operation::Nop - } else if buf.len() == 1 { - buf.pop().unwrap() - } else { - Operation::Seq { ops: buf } - }; + if self.is_empty() { + *self = Operation::Nop; } } } @@ -486,8 +331,8 @@ impl Debug for Operation { /// ``` pub fn transform(a: &mut Operation, b: &mut Operation) { transform_internal(a, b); - // a.consolidate_seq(); - // b.consolidate_seq(); + a.sanitize(); + b.sanitize(); } fn transform_internal(a: &mut Operation, b: &mut Operation) { @@ -613,14 +458,6 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo to: m_to, } = a { - /* - if *m_n != 1 && matches!(b, Operation::Mov { .. }) { - let new_a = mov2seq(*m_pos, *m_n, *m_to); - *a = new_a; - return transform_internal(a, b); - } - */ - let m_to_right = m_to > m_pos; let m_to_start = if m_to_right { *m_to - *m_n } else { *m_to }; let rtarget = m_to_start..(m_to_start + *m_n); @@ -669,12 +506,77 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo let (iba, ibt) = (rb.intersect_ext(&ra), rb.intersect_ext(&rtarget)); // delete within src - delete within dst - match (&iba, &ibt) { + match &iba { + IntersectionExt::Less => { + // DEL before MOV + if m_to_start <= *d_pos { + // moved left from DEL + *m_pos -= *d_n; + *d_pos += *m_n; + } else if matches!( + ibt, + IntersectionExt::Over | IntersectionExt::LessOverlap + ) { + // moved inside DEL + let split_l = *m_to - *d_pos; + *m_pos -= *d_n; + *m_to -= split_l; + + let del_ops = vec![ + Operation::Del { + pos: rtarget.end, + n: *d_n - split_l, + }, + Operation::Del { + pos: rtarget.start - split_l, + n: split_l, + }, + ]; + *b = Operation::Seq { ops: del_ops }; + } else { + // moved right from DEL + *m_pos -= *d_n; + *m_to -= *d_n; + } + } + IntersectionExt::Greater => { + // DEL after MOV + if matches!(ibt, IntersectionExt::Over | IntersectionExt::GreaterOverlap) { + let split_l = *m_to - *d_pos; + if split_l == *d_n { + *m_to -= *d_n; + *d_pos -= *m_n; + } else { + // moved inside DEL + *m_to -= split_l; + + let del_ops = vec![ + Operation::Del { + pos: rtarget.end, + n: *d_n - split_l, + }, + Operation::Del { + pos: *d_pos - *m_n, + n: split_l, + }, + ]; + *b = Operation::Seq { ops: del_ops }; + } + } else if m_to_start < *d_pos { + if !matches!(ibt, IntersectionExt::Greater) { + *m_to -= *d_n; + *d_pos -= *m_n; + } + } else { + // moved right from DEL + *m_to -= *d_n; + *d_pos -= *m_n; + } + } // Part of the source is deleted - (IntersectionExt::LessOverlap, _) => { + IntersectionExt::LessOverlap => { // MOV right end of DEL - let d_pos_split = *m_pos; - let rest_len = d_pos_split - *d_pos; + let rest_len = *m_pos - *d_pos; if m_to_right { let del_ops = vec![ @@ -697,22 +599,28 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo &ibt, IntersectionExt::Over | IntersectionExt::LessOverlap ) { - let del_len = m_to_start + 1 - *d_pos; + /* + The DEL range is split by the MOV op in max 3 parts + 1. DEL before inserted items (d_pos..m_to) + 2. Inserted items: start may contain deleted items + DEL m_to..(m_to + l_intersect) + 3. DEL after inserted items (m_to+m_n..) + */ + + let intersection = ra.start.max(rb.start)..ra.end.min(rb.end); + let r_del_mov = *d_pos..(*m_to + intersection.len()); let del_ops = vec![ - // Delete rest (after MOV target) Operation::Del { pos: rtarget.end, - n: *d_n - del_len, + n: *d_n - r_del_mov.len(), }, - // Delete moved item Operation::Del { - pos: *d_pos, - n: del_len, + pos: r_del_mov.start, + n: r_del_mov.len(), }, ]; - *m_pos = *d_pos; - *m_n = ra.end - rb.end; + *a = Operation::Nop; *b = Operation::Seq { ops: del_ops }; } else { let del_ops = vec![ @@ -731,41 +639,767 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo *m_n = ra.end - rb.end; *b = Operation::Seq { ops: del_ops }; } - - a.sanitize(); } - (IntersectionExt::GreaterOverlap, _) => { + IntersectionExt::GreaterOverlap => { // MOV left end of DEL - let d_pos_split = *m_pos + *m_n; - unimplemented!("m_pos < d_pos"); + let rest_len = rb.end - ra.end; + let moved_len = *d_n - rest_len; + let intersection = ra.start.max(rb.start)..ra.end.min(rb.end); + + if !m_to_right { + // MOV to left + // todo!("GreaterOverlap => left"); + let del_ops = vec![ + // Delete rest + Operation::Del { + pos: rb.end - (*d_n - intersection.len()), + n: *d_n - intersection.len(), + }, + // Delete moved item + Operation::Del { + pos: rtarget.end - intersection.len(), + n: intersection.len(), + }, + ]; + *m_n -= intersection.len(); + *b = Operation::Seq { ops: del_ops }; + } else if matches!( + &ibt, + IntersectionExt::Over | IntersectionExt::GreaterOverlap + ) { + let n_right = *m_to - ra.end; + + let del_ops = vec![ + Operation::Del { + pos: rtarget.end - intersection.len(), + n: *d_n - n_right, + }, + Operation::Del { + pos: *m_pos, + n: n_right, + }, + ]; + *a = Operation::Nop; + *b = Operation::Seq { ops: del_ops }; + } else { + let del_ops = vec![ + // Delete moved item + Operation::Del { + pos: *m_to - moved_len, + n: moved_len, + }, + // Delete rest + Operation::Del { + pos: *m_pos, + n: rest_len, + }, + ]; + *m_n = *d_pos - *m_pos; + *m_to -= *d_n; + *b = Operation::Seq { ops: del_ops }; + } } // DEL middle of the MOV op - (IntersectionExt::Within, _) => { - unimplemented!("DEL middle of MOV op"); + IntersectionExt::Within => { + if m_to_right { + let delta = m_to_start - *m_pos; + *d_pos += delta; + *m_to -= *d_n; + } else { + let delta = *m_pos - m_to_start; + *d_pos -= delta; + } + *m_n -= *d_n; } // DEL is split by the MOV op - (IntersectionExt::Over, _) => { - unimplemented!("DEL is split by the MOV op"); + IntersectionExt::Over => { + let intersection = ra.start.max(rb.start)..ra.end.min(rb.end); + if m_to_right { + let del_ops = vec![ + Operation::Del { + pos: rtarget.end - intersection.len(), + n: intersection.len(), + }, + Operation::Del { + pos: *d_pos, + n: *d_n - intersection.len(), + }, + ]; + *b = Operation::Seq { ops: del_ops }; + } else { + let del_ops = vec![ + Operation::Del { + pos: rb.end - (*d_n - intersection.len()), + n: *d_n - intersection.len(), + }, + Operation::Del { + pos: (*m_to).min(*d_pos), + n: intersection.len(), + }, + ]; + *b = Operation::Seq { ops: del_ops }; + } + *a = Operation::Nop; } // Entire source is deleted - (IntersectionExt::Same, _) => { + IntersectionExt::Same => { // Delete all moved items in A' *d_pos = m_to_start; *a = Operation::Nop; - return; } - // Entire destination is deleted - // (_, Intersection::Full) => {} - (IntersectionExt::Empty, _) | (_, IntersectionExt::Empty) => {} - _ => unimplemented!("mov-del constellation {iba:?};{ibt:?}"), + IntersectionExt::Empty => {} } } Operation::Mov { - pos: b_pos, - n: b_n, - to: b_to, + pos: m2_pos, + n: m2_n, + to: m2_to, } => { - unimplemented!("mov-mov"); + // todo!("MOV-MOV"); + let m2_to_right = m2_to > m2_pos; + let m2_to_start = if m2_to_right { *m2_to - *m2_n } else { *m2_to }; + let rtarget2 = m2_to_start..(m2_to_start + *m2_n); + let rm2 = *m2_pos..(*m2_pos + *m2_n); + let im2 = ra.intersect_ext(&rm2); + + let a_split_by_b = *m2_to > ra.start && *m2_to < ra.end; + let b_split_by_a = *m_to > rm2.start && *m_to < rm2.end; + + if ra == rm2 { + // Asymmetrical, B has priority + *m2_pos = m_to_start; + *m2_to = m2_to_start; + if m2_to > m2_pos { + *m2_to += *m_n; + } + *a = Operation::Nop; + return; + } + if matches!(im2, IntersectionExt::Within) { + // A within B; B has priority, so A should be undone + let ops = vec![ + undo_mov(*m_pos, *m_n, m_to_start), + Operation::Mov { + pos: *m2_pos, + n: *m2_n, + to: *m2_to, + }, + ]; + *b = Operation::Seq { ops }; + *a = Operation::Nop; + return; + } + if matches!(im2, IntersectionExt::Over) { + // A over B + let m2_inside_offset = *m2_pos - *m_pos; + let a_before = *m_pos..*m2_pos; + let a_after = rm2.end..ra.end; + + if a_split_by_b { + // Split + *m2_pos = rtarget.start + m2_inside_offset; + if m_to_right { + *m2_to += rtarget.start - *m_pos; + } else { + *m2_to -= *m_pos - rtarget.start; + } + } else { + // A=>B2: + let m2_pos_n = rtarget.start + m2_inside_offset; + let m2_n_n = *m_n - (a_before.len() + a_after.len()); + let mut m2_to_n = *m2_to; + + if *m_pos < *m2_to && rtarget.end >= *m2_to { + m2_to_n -= *m_n; + } else if *m_pos >= *m2_to && *m_to < *m2_to { + m2_to_n += *m_n; + } + + // B=>A2: outer ranges a_before and a_after are lined up + // at m_pos if m2_to_right; at m_pos + m2_n if m2_to_left + let ab_together_start = if m2_to_right { + a_before.start + } else { + a_before.start + *m2_n + }; + + let m_pos_n = ab_together_start; + let m_n_n = a_before.len() + a_after.len(); + let mut m_to_n = *m_to; + + // Target can be shifted by m2_n to the left if the B operation + // source < m_to AND targetB.end >= m_to (B is moved right over m_to) + if *m2_pos < *m_to && rtarget2.end >= *m_to { + m_to_n -= *m2_n; + } + // Target shifted by m2_n to the right if the B operation + // source >= m_to AND m2_to < m_to (B is moved left over m_to) + else if *m2_pos >= *m_to && *m2_to < *m_to { + m_to_n += *m2_n; + } + + // If both operations have the same target or split, + // the B operation takes priority, A is inserted after it + if m_to == m2_to { + m2_to_n += *m2_n; + if rtarget.contains(&m2_to_n) { + m2_to_n = rtarget.end; + } + } + + *m_pos = m_pos_n; + *m_n = m_n_n; + *m_to = m_to_n; + *m2_pos = m2_pos_n; + *m2_n = m2_n_n; + *m2_to = m2_to_n; + } + return; + } + + if matches!(im2, IntersectionExt::LessOverlap) { + let intersection = *m2_pos..ra.end; + match (a_split_by_b, b_split_by_a) { + (true, _) => { + // Range A is split by B insertion + // B: to_left + // A=>B2: move intersection of A/B to m_pos + let left_of_m2_to = *m_pos..*m2_to; + let b_delta = *m2_pos - rtarget2.start; + let nleft = *m2_pos - *m_pos; + + let m2_pos_n = project_mov_point(*m_pos, *m_n, *m_to, *m2_pos); + let m2_n_n = intersection.len(); + let mut m2_to_n = *m_pos; + if !m_to_right { + m2_to_n += *m_n; + } + + // B=>A2: + // 1. move part left of m2_to to m_to + // 2. move part right of m2 target to m_to + let wi = rm2.contains(m_to); // m_to within m2 source + let mut mto = *m_to; + if wi { + mto -= b_delta; + } + let a_ops = vec![ + Operation::Mov { + pos: left_of_m2_to.start, + n: left_of_m2_to.len(), + to: mto, + }, + Operation::Mov { + pos: if wi || !m_to_right { + rtarget2.end + } else { + rtarget2.end - left_of_m2_to.len() + }, + n: nleft - left_of_m2_to.len(), + to: if m_to_right { + mto + } else { + mto + left_of_m2_to.len() + }, + }, + ]; + *a = Operation::Seq { ops: a_ops }; + *m2_pos = m2_pos_n; + *m2_n = m2_n_n; + *m2_to = m2_to_n; + return; + } + (false, true) => { + // Range B is split by A insertion + let mut b_ops = None; + + // A=>B2 move intersection of A/B to m2_to + let mdelta = rtarget.start - *m_pos; + let m2_pos_n = intersection.start + mdelta; + + if m2_to < m_pos { + let r_before = + (*m2_to + intersection.len())..(*m_pos + intersection.len()); + b_ops = Some(vec![ + Operation::Mov { + pos: m2_pos_n, + n: intersection.len(), + to: *m2_to, + }, + Operation::Mov { + pos: r_before.start, + n: r_before.len(), + to: rm2.end, + }, + ]); + } + if m2_to_right && m2_to > m_to { + let delta = *m2_to - rm2.end; // delta of first op (->) + b_ops = Some(vec![ + Operation::Mov { + pos: *m_pos, + n: rm2.end - *m_pos, + to: *m2_to, + }, + Operation::Mov { + pos: project_mov_point( + *m_pos, + *m_n, + *m_to, + intersection.start, + ) + delta, + n: intersection.len(), + to: *m_pos + delta, + }, + ]); + } + + // B=>A2 move part left of intersection of A/B to m_to + if m2_to_right { + let mdelta = rtarget2.start - *m2_pos; + *m_to += mdelta; + } else { + let mdelta = *m2_pos - rtarget2.start; + *m_pos += *m2_n; + *m_to -= mdelta; + } + + *m_n -= intersection.len(); + + if let Some(ops) = b_ops { + *b = Operation::Seq { ops }; + } else { + *m2_n = intersection.len(); + *m2_pos = m2_pos_n; + } + return; + } + (false, false) => { + // LessOverlap + let intersection = ra.start.max(rm2.start)..ra.end.min(rm2.end); + let m_n_n = *m_n - intersection.len(); + let m_pos_n = project_mov_point(*m2_pos, *m2_n, *m2_to, *m_pos); + + let mut m_to_n = + project_mov_point(*m2_pos, *m2_n, *m2_to, m_to.saturating_sub(1)); + if *m_to > 0 { + m_to_n += 1; + } + + // A=>B2: + if m_to == m2_to { + *m2_n -= intersection.len(); + if m_to_right { + *m2_pos = m_pos_n; + *m2_to = *m_to; + } else { + *m2_pos = ra.end; + *m2_to = rtarget.end; + m_to_n = *m_to; + } + } else { + // undo A, apply B + let b_ops = vec![ + undo_mov(*m_pos, *m_n, m_to_start), + Operation::Mov { + pos: *m2_pos, + n: *m2_n, + to: *m2_to, + }, + Operation::Mov { + pos: m_pos_n, + n: m_n_n, + to: m_to_n, + }, + ]; + *b = Operation::Seq { ops: b_ops }; + } + + // B=>A2 mov part left from m2 to m_to + *m_pos = m_pos_n; + *m_n = m_n_n; + *m_to = m_to_n; + return; + } + } + } + + if matches!(im2, IntersectionExt::GreaterOverlap) { + let intersection = *m_pos..rm2.end; + match (a_split_by_b, b_split_by_a) { + (true, _) => { + // Range A is split by B insertion + // B: to_right + + // A=>B2: move intersection of A/B to m_pos + let right_of_m2_to = *m2_to..ra.end; + let b_delta = rtarget2.start - *m2_pos; + let nright = ra.end - rm2.end; // right from overlap + + let m2_pos_n = + project_mov_point(*m_pos, *m_n, *m_to, intersection.start); + let m2_n_n = intersection.len(); + let mut m2_to_n = *m_pos; + if !m_to_right { + m2_to_n += *m_n; + } + + // B=>A2: + // 1. move part right of m2_to to m_to + // 2. move part left of m2 target to m_to + let wi = rm2.contains(m_to); // m_to within m2 source + let mut mto = *m_to; + if wi { + mto += b_delta; + } + let prm2_end = project_mov_point(*m2_pos, *m2_n, *m2_to, rm2.end); + let a_ops = vec![ + Operation::Mov { + pos: right_of_m2_to.start, + n: right_of_m2_to.len(), + to: mto, + }, + Operation::Mov { + pos: if m_to_right || wi { + prm2_end + } else { + prm2_end + right_of_m2_to.len() + }, + n: nright - right_of_m2_to.len(), + to: if m_to_right { + mto - right_of_m2_to.len() + } else { + mto + }, + }, + ]; + *a = Operation::Seq { ops: a_ops }; + *m2_pos = m2_pos_n; + *m2_n = m2_n_n; + *m2_to = m2_to_n; + return; + } + (false, true) => { + // Range B is split by A insertion + + // A=>B2 move intersection of A/B to m2_to + let m2_pos_n = rtarget.start; + let mut b_ops = None; + + // B=>A2 + let r_mov = rm2.end..ra.end; + *m_pos = project_mov_point(*m2_pos, *m2_n, *m2_to, r_mov.start); + *m_n = r_mov.len(); + if m2_to_right { + let m2delta = rtarget2.start - *m2_pos; + *m_to += m2delta; + + if *m2_to > ra.end { + b_ops = Some(vec![ + Operation::Mov { + pos: m2_pos_n, + n: intersection.len(), + to: *m2_to, + }, + Operation::Mov { + pos: *m2_pos, + n: ra.end - *m2_pos - intersection.len(), + to: *m2_to - intersection.len(), + }, + ]); + } + } else { + let m2delta = *m2_pos - rtarget2.start; + *m_to -= m2delta; + let d1 = *m2_pos - *m2_to; + + b_ops = Some(vec![ + Operation::Mov { + pos: *m2_pos, + n: ra.end - *m2_pos, + to: *m2_to, + }, + Operation::Mov { + pos: m2_pos_n - d1, + n: intersection.len(), + to: ra.end - d1, + }, + ]); + } + + if let Some(b_ops) = b_ops { + *b = Operation::Seq { ops: b_ops } + } else { + *m2_pos = m2_pos_n; + *m2_n = intersection.len(); + } + return; + } + (false, false) => { + // GreaterOverlap + let intersection = ra.start.max(rm2.start)..ra.end.min(rm2.end); + let m_n_n = *m_n - intersection.len(); + let m_pos_pre = ra.end - m_n_n; + + let m_pos_n = project_mov_point(*m2_pos, *m2_n, *m2_to, m_pos_pre); + let mut m_to_n = project_mov_point(*m2_pos, *m2_n, *m2_to, *m_to); + + // A=>B2: + if m_to == m2_to { + *m2_n -= intersection.len(); + if m_to_right { + *m2_pos = m_pos_n; + *m2_to = rtarget.start; + m_to_n = *m_to; + } else { + *m2_pos = project_mov_point(*m_pos, *m_n, *m_to, rm2.start); + *m2_to = *m_to; + } + } else { + // undo A, apply B + let b_ops = vec![ + undo_mov(*m_pos, *m_n, m_to_start), + Operation::Mov { + pos: *m2_pos, + n: *m2_n, + to: *m2_to, + }, + Operation::Mov { + pos: m_pos_n, + n: m_n_n, + to: m_to_n, + }, + ]; + *b = Operation::Seq { ops: b_ops }; + } + + // B=>A2 mov part left from m2 to m_to + *m_pos = m_pos_n; + *m_n = m_n_n; + *m_to = m_to_n; + + return; + } + } + } + + if matches!(im2, IntersectionExt::Less) { + match (a_split_by_b, b_split_by_a) { + (true, false) => { + if m_to_right { + let mdelta = rtarget.start - *m_pos; + + // A=>B2: mov m2_src to m2_pos + let m2_pos_n = project_mov_point(*m_pos, *m_n, *m_to, *m2_pos); + + // B=>A2: mov m1_range with inserted m2 items to m_to + *m_n += *m2_n; + if rm2.end > *m_to { + *m_to += *m2_n; + } + + *m2_pos = m2_pos_n; + *m2_to += mdelta; + } else { + let mdelta = *m_pos - rtarget.start; + let m2_pos_n = project_mov_point(*m_pos, *m_n, *m_to, *m2_pos); + + *m_n += *m2_n; + + *m2_pos = m2_pos_n; + *m2_to -= mdelta; + } + return; + } + (false, true) => { + if m2_to_right { + let m2delta = rtarget2.start - *m2_pos; + let m2_pos_n = project_mov_point(*m2_pos, *m2_n, *m2_to, *m_pos); + + *m2_pos = project_mov_point(*m_pos, *m_n, *m_to, *m2_pos); + *m2_n += *m_n; + + *m_pos = m2_pos_n; + *m_to += m2delta; + } else { + let m2delta = *m2_pos - rtarget2.start; + let m2_pos_n = project_mov_point(*m2_pos, *m2_n, *m2_to, *m_pos); + + *m2_pos = project_mov_point(*m_pos, *m_n, *m_to, *m2_pos); + *m2_n += *m_n; + if ra.end <= *m2_to { + *m2_to -= *m_n; + } + + *m_pos = m2_pos_n; + *m_to -= m2delta; + } + return; + } + (true, true) => { + // A=>B2: move m2 items (split in 2 ranges) to m2_to + let mdelta = rtarget.start - *m_pos; + let left_r = *m2_pos..*m_to; + let to = *m2_to + mdelta; + let b_ops = vec![ + Operation::Mov { + pos: rtarget.end - *m_n - left_r.len(), + n: left_r.len(), + to, + }, + Operation::Mov { + pos: rtarget.end, + n: *m2_n - left_r.len(), + to, + }, + ]; + + let between = ra.end..rm2.start; + if between.is_empty() { + *a = Operation::Nop; + } else { + *m_pos = project_mov_point(*m2_pos, *m2_n, *m2_to, between.start); + *m_n = between.len(); + *m_to = ra.start; + } + + *b = Operation::Seq { ops: b_ops }; + return; + } + (false, false) => {} + } + } + + if matches!(im2, IntersectionExt::Greater) { + match (a_split_by_b, b_split_by_a) { + (true, false) => { + if m_to_right { + let mdelta = rtarget.start - *m_pos; + + // A=>B2: mov m2_src to m2_pos + let m2_pos_n = project_mov_point(*m_pos, *m_n, *m_to, *m2_pos); + + // B=>A2: mov m1_range with inserted m2 items to m_to + let nl = *m2_to - *m_pos; + *m_pos = *m2_to - *m2_n - nl; + *m_n += *m2_n; + + *m2_pos = m2_pos_n; + *m2_to += mdelta; + } else { + let mdelta = *m_pos - rtarget.start; + let m2_pos_n = project_mov_point(*m_pos, *m_n, *m_to, *m2_pos); + + let nl = *m2_to - *m_pos; + *m_pos = *m2_to - *m2_n - nl; + *m_n += *m2_n; + if *m_to > 0 && rm2.end <= *m_to { + *m_to -= *m2_n; + } + + *m2_pos = m2_pos_n; + *m2_to -= mdelta; + } + return; + } + (false, true) => { + if m2_to_right { + let m2delta = rtarget2.start - *m2_pos; + let m2_pos_n = project_mov_point(*m2_pos, *m2_n, *m2_to, *m_pos); + + *m2_pos = project_mov_point(*m_pos, *m_n, *m_to, *m2_pos); + *m2_n += *m_n; + if *m2_to < ra.end { + *m2_to += *m_n; + } + + *m_pos = m2_pos_n; + *m_to += m2delta; + } else { + let m2delta = *m2_pos - rtarget2.start; + let m2_pos_n = project_mov_point(*m2_pos, *m2_n, *m2_to, *m_pos); + + *m2_pos = project_mov_point(*m_pos, *m_n, *m_to, *m2_pos); + *m2_n += *m_n; + + *m_pos = m2_pos_n; + *m_to -= m2delta; + } + return; + } + (true, true) => { + // A=>B2: move m2 items (split in 2 ranges) to m2_to + let mdelta = *m_pos - rtarget.start; + let left_r = *m2_pos..*m_to; + let to = *m2_to - mdelta; + let b_ops = vec![ + Operation::Mov { + pos: rtarget.end - *m_n - left_r.len(), + n: left_r.len(), + to, + }, + Operation::Mov { + pos: rtarget.end, + n: *m2_n - left_r.len(), + to, + }, + ]; + + let between = rm2.end..ra.start; + if between.is_empty() { + *a = Operation::Nop; + } else { + *m_pos = project_mov_point(*m2_pos, *m2_n, *m2_to, between.start); + *m_n = between.len(); + *m_to = ra.end; + } + + *b = Operation::Seq { ops: b_ops }; + return; + } + (false, false) => {} + } + } + + let mut ia = Operation::<()>::Ins { + pos: *m_to, + val: vec![(); *m_n], + }; + let mut da = Operation::<()>::Del { + pos: if m_to_right { *m_pos } else { *m_pos + *m_n }, + n: *m_n, + }; + let mut ib = Operation::<()>::Ins { + pos: *m2_to, + val: vec![(); *m2_n], + }; + let mut db = Operation::<()>::Del { + pos: if m2_to_right { + *m2_pos + } else { + *m2_pos + *m2_n + }, + n: *m2_n, + }; + + transform_internal(&mut ia, &mut ib); + transform_internal(&mut da, &mut ib); + transform_internal(&mut ia, &mut db); + transform_internal(&mut da, &mut db); + + *m_pos = if m_to_right { + da.pos() + } else { + da.pos() - *m_n + }; + *m_to = ia.pos(); + + *m2_pos = if m2_to_right { + db.pos() + } else { + db.pos() - *m2_n + }; + *m2_to = ib.pos(); } Operation::Seq { ops } => transform_seq(ops, a, false), Operation::Nop => {} @@ -773,23 +1407,6 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } } -fn mov2seq(pos: usize, n: usize, to: usize) -> Operation { - Operation::Seq { - ops: if to < pos { - (0..n) - .rev() - .map(|_| Operation::Mov { - pos: pos + n - 1, - n: 1, - to, - }) - .collect() - } else { - (0..n).map(|_| Operation::Mov { pos, n: 1, to }).collect() - }, - } -} - fn splice_or_append(v1: &mut Vec, pos: usize, mut v2: Vec) { if pos < v1.len() { v1.splice(pos..pos, v2); @@ -798,22 +1415,91 @@ fn splice_or_append(v1: &mut Vec, pos: usize, mut v2: Vec) { } } -#[cfg(test)] -mod tests { - use crate::mov2seq; - - #[test] - fn t_mov2seq() { - let input = [0, 1, 2, 3, 4, 5, 6]; - let a = mov2seq::(1, 3, 6); - let b = mov2seq::(3, 3, 1); - - let mut ia = input.to_vec(); - a.apply(&mut ia); - assert_eq!(ia, &[0, 4, 5, 1, 2, 3, 6]); - - let mut ib = input.to_vec(); - b.apply(&mut ib); - assert_eq!(ib, &[0, 3, 4, 5, 1, 2, 6]); +fn undo_mov(pos: usize, n: usize, m_to_start: usize) -> Operation { + let mop1_to = if pos > m_to_start { pos + n } else { pos }; + Operation::Mov { + pos: m_to_start, + n, + to: mop1_to, + } +} + +fn project_mov_point(m_pos: usize, m_n: usize, m_to: usize, point: usize) -> usize { + let m_to_right = m_to > m_pos; + let m_to_start = if m_to_right { m_to - m_n } else { m_to }; + let rmov = m_pos..(m_pos + m_n); + let rtarget = m_to_start..(m_to_start + m_n); + let m_delta = m_to_start as i64 - m_pos as i64; + + if (point < m_pos && point < rtarget.start) || (point >= rmov.end && point >= m_to) { + // unaffected + point + } else if rmov.contains(&point) { + // point is moved + (point as i64 + m_delta) as usize + } else if m_to_right { + point - m_n + } else { + point + m_n + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn filter_to(size: usize, pos: usize, to: usize) -> usize { + if pos == to { + if to >= size - 1 { + filter_to(size, pos, 0) + } else { + filter_to(size, pos, to + 1) + } + } else { + to + } + } + + #[test] + fn t_project_mov_point() { + let size = 9; + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + + (0..size).into_iter().for_each(|m_pos| { + (1..(size - m_pos)).into_iter().for_each(|m_n| { + (0..size).into_iter().for_each(|m_to| { + (0..size).into_iter().for_each(|point| { + let m_to = filter_to(size, m_pos, m_to); + let m_n = m_n.min(size - m_pos.max(m_to)); + + let mov = Operation::Mov { + pos: m_pos, + n: m_n, + to: m_to, + }; + if mov.is_empty() { + return; + } + + let res = project_mov_point(m_pos, m_n, m_to, point); + + let mut data = input.to_vec(); + mov.clone().apply(&mut data); + let moved_to = data + .iter() + .enumerate() + .find(|(_, itm)| **itm == point as i32) + .unwrap() + .0; + + if res != moved_to { + mov.print_on(&input); + println!("Point: {point}"); + assert_eq!(res, moved_to); + } + }) + }) + }) + }); } } diff --git a/tests/proptest.rs b/tests/proptest.rs index 00af9c0..246d805 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -7,24 +7,17 @@ use otvec::Operation; use util::{print_cond_catch_ctr, test_transform_cond, test_transform_cond_catch, CondCatchCtr}; const ALL_SIZE: usize = 20; +const MIN_RANGE_LEN: usize = 1; + +const TEST_SIZE: usize = 9; +const TEST_MIN_RANGE_LEN: usize = 1; + const VEC_SIZE: usize = 100; fn testvec(len: usize) -> Vec { (0..len).collect::>() } -fn filter_to(size: usize, pos: usize, to: usize) -> usize { - if pos == to { - if to >= size - 1 { - filter_to(size, pos, 0) - } else { - filter_to(size, pos, to + 1) - } - } else { - to - } -} - fn t_ins_ins(size: usize, a_pos: usize, a_len: usize, b_pos: usize, b_len: usize) { let input = testvec(size); let a = Operation::Ins { @@ -83,15 +76,7 @@ fn t_mov_ins(size: usize, a_pos: usize, a_len: usize, a_to: usize, b_pos: usize, test_transform_cond(&b, &a, &input); } -fn t_mov_del( - size: usize, - a_pos: usize, - a_len: usize, - a_to: usize, - b_pos: usize, - b_len: usize, - ctr: Option<&CondCatchCtr>, -) { +fn t_mov_del(size: usize, a_pos: usize, a_len: usize, a_to: usize, b_pos: usize, b_len: usize) { let input = testvec(size); let a = Operation::Mov { pos: a_pos, @@ -102,6 +87,31 @@ fn t_mov_del( pos: b_pos, n: b_len, }; + test_transform_cond(&a, &b, &input); + test_transform_cond(&b, &a, &input); +} + +fn t_mov_mov_catch( + size: usize, + a_pos: usize, + a_len: usize, + a_to: usize, + b_pos: usize, + b_len: usize, + b_to: usize, + ctr: Option<&CondCatchCtr>, +) { + let input = testvec(size); + let a = Operation::Mov { + pos: a_pos, + n: a_len, + to: a_to, + }; + let b = Operation::Mov { + pos: b_pos, + n: b_len, + to: b_to, + }; test_transform_cond_catch(&a, &b, &input, ctr); test_transform_cond_catch(&b, &a, &input, ctr); } @@ -133,159 +143,247 @@ fn t_mov_mov( #[test] fn all_ins_ins() { (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (1..(ALL_SIZE - a_pos)).into_par_iter().for_each(|a_len| { - (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { - (1..(ALL_SIZE - b_pos)).into_par_iter().for_each(|b_len| { - t_ins_ins(ALL_SIZE, a_pos, a_len, b_pos, b_len); + (MIN_RANGE_LEN..=(ALL_SIZE - a_pos)) + .into_par_iter() + .for_each(|a_len| { + (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { + (MIN_RANGE_LEN..=(ALL_SIZE - b_pos)) + .into_par_iter() + .for_each(|b_len| { + t_ins_ins(ALL_SIZE, a_pos, a_len, b_pos, b_len); + }); }); }); - }); }); } #[test] fn all_del_del() { (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (1..(ALL_SIZE - a_pos)).into_par_iter().for_each(|a_len| { - (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { - (1..(ALL_SIZE - b_pos)).into_par_iter().for_each(|b_len| { - t_del_del(ALL_SIZE, a_pos, a_len, b_pos, b_len); + (MIN_RANGE_LEN..=(ALL_SIZE - a_pos)) + .into_par_iter() + .for_each(|a_len| { + (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { + (MIN_RANGE_LEN..=(ALL_SIZE - b_pos)) + .into_par_iter() + .for_each(|b_len| { + t_del_del(ALL_SIZE, a_pos, a_len, b_pos, b_len); + }); }); }); - }); }); } #[test] fn all_ins_del() { (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (1..(ALL_SIZE - a_pos)).into_par_iter().for_each(|a_len| { - (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { - (1..(ALL_SIZE - b_pos)).into_par_iter().for_each(|b_len| { - t_ins_del(ALL_SIZE, a_pos, a_len, b_pos, b_len); + (MIN_RANGE_LEN..=(ALL_SIZE - a_pos)) + .into_par_iter() + .for_each(|a_len| { + (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { + (MIN_RANGE_LEN..=(ALL_SIZE - b_pos)) + .into_par_iter() + .for_each(|b_len| { + t_ins_del(ALL_SIZE, a_pos, a_len, b_pos, b_len); + }); }); }); - }); }); } #[test] fn all_mov_ins() { (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (1..(ALL_SIZE - a_pos)).into_par_iter().for_each(|a_len| { - (0..ALL_SIZE).into_par_iter().for_each(|a_to| { - let a_to = filter_to(ALL_SIZE, a_pos, a_to); - let a_len = a_len.min(ALL_SIZE - a_pos.max(a_to)); - - (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { - (1..(ALL_SIZE - b_pos)).into_par_iter().for_each(|b_len| { - t_mov_ins(ALL_SIZE, a_pos, a_len, a_to, b_pos, b_len); + (MIN_RANGE_LEN..=(ALL_SIZE - a_pos)) + .into_par_iter() + .for_each(|a_len| { + (0..a_pos) + .into_par_iter() + .chain((a_pos + a_len + 1)..=ALL_SIZE) + .for_each(|a_to| { + (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { + (MIN_RANGE_LEN..=(ALL_SIZE - b_pos)) + .into_par_iter() + .for_each(|b_len| { + t_mov_ins(ALL_SIZE, a_pos, a_len, a_to, b_pos, b_len); + }); + }); }); - }); }); - }); }); } #[test] fn all_mov_del() { - let ctr = CondCatchCtr::default(); - (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (1..(ALL_SIZE - a_pos)).into_par_iter().for_each(|a_len| { - (0..ALL_SIZE).into_par_iter().for_each(|a_to| { - let a_to = filter_to(ALL_SIZE, a_pos, a_to); - let a_len = a_len.min(ALL_SIZE - a_pos.max(a_to)); - - (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { - (1..(ALL_SIZE - b_pos)).into_par_iter().for_each(|b_len| { - t_mov_del(ALL_SIZE, a_pos, a_len, a_to, b_pos, b_len, Some(&ctr)); + (MIN_RANGE_LEN..=(ALL_SIZE - a_pos)) + .into_par_iter() + .for_each(|a_len| { + (0..a_pos) + .into_par_iter() + .chain((a_pos + a_len + 1)..=ALL_SIZE) + .for_each(|a_to| { + (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { + (MIN_RANGE_LEN..=(ALL_SIZE - b_pos)) + .into_par_iter() + .for_each(|b_len| { + t_mov_del(ALL_SIZE, a_pos, a_len, a_to, b_pos, b_len); + }); + }); + }); + }); + }); +} + +// For development (non-parallel and panic-catching) +#[test] +fn dev_all_mov_mov() { + let ctr = CondCatchCtr::default(); + (0..TEST_SIZE).into_iter().for_each(|a_pos| { + (TEST_MIN_RANGE_LEN..=(TEST_SIZE - a_pos)) + .into_iter() + // .rev() + .for_each(|a_len| { + (0..a_pos) + .into_iter() + .chain((a_pos + a_len + 1)..=TEST_SIZE) + .for_each(|a_to| { + (0..TEST_SIZE).into_iter().for_each(|b_pos| { + (TEST_MIN_RANGE_LEN..=(TEST_SIZE - b_pos)) + .into_iter() + // .rev() + .for_each(|b_len| { + (0..b_pos) + .into_iter() + .chain((b_pos + b_len + 1)..=TEST_SIZE) + .for_each(|b_to| { + t_mov_mov_catch( + TEST_SIZE, + a_pos, + a_len, + a_to, + b_pos, + b_len, + b_to, + Some(&ctr), + ); + }); + }); + }); }); - }); }); - }); }); print_cond_catch_ctr(&ctr); } -/* #[test] fn all_mov_mov() { (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (1..(ALL_SIZE - a_pos)).into_par_iter().for_each(|a_len| { - (0..ALL_SIZE).into_par_iter().for_each(|a_to| { - let a_to = filter_to(ALL_SIZE, a_pos, a_to); - let a_len = a_len.min(ALL_SIZE - a_pos.max(a_to)); - - (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { - (1..(ALL_SIZE - b_pos)).into_par_iter().for_each(|b_len| { - (0..ALL_SIZE).into_par_iter().for_each(|b_to| { - let b_to = filter_to(ALL_SIZE, b_pos, b_to); - let b_len = b_len.min(ALL_SIZE - b_pos.max(b_to)); - - t_mov_mov(ALL_SIZE, a_pos, a_len, a_to, b_pos, b_len, b_to); + (MIN_RANGE_LEN..=(ALL_SIZE - a_pos)) + .into_par_iter() + .for_each(|a_len| { + (0..a_pos) + .into_par_iter() + .chain((a_pos + a_len + 1)..=ALL_SIZE) + .for_each(|a_to| { + (0..ALL_SIZE).into_par_iter().for_each(|b_pos| { + (MIN_RANGE_LEN..=(ALL_SIZE - b_pos)) + .into_par_iter() + .for_each(|b_len| { + (0..b_pos) + .into_par_iter() + .chain((b_pos + b_len + 1)..=ALL_SIZE) + .for_each(|b_to| { + t_mov_mov( + ALL_SIZE, a_pos, a_len, a_to, b_pos, b_len, b_to, + ); + }); + }); }); }); - }); }); - }); }); } -*/ + +fn test_params() -> impl Strategy { + ((MIN_RANGE_LEN + 1)..=VEC_SIZE) + .prop_flat_map(|vec_size| (Just(vec_size), mov_params(vec_size), mov_params(vec_size))) +} + +/// Parameters for a single test operation +fn mov_params(vec_size: usize) -> impl Strategy { + (0..(vec_size - MIN_RANGE_LEN)) + .prop_flat_map(move |pos| (Just(pos), MIN_RANGE_LEN..=(vec_size - pos))) + .prop_flat_map(move |(pos, len)| { + let rb = (pos + len + 1)..(vec_size + 1); + ( + Just(pos), + Just(len), + match (pos > 0, rb.end > rb.start) { + (true, true) => Strategy::boxed((0..pos).prop_union(rb)), + (true, false) => Strategy::boxed(0..pos), + (false, true) => Strategy::boxed(rb), + (false, false) => Strategy::boxed(Just(pos)), + }, + ) + }) +} proptest! { #[test] - fn transform_ins_ins(a_pos in 0usize..VEC_SIZE, a_len in 1usize..VEC_SIZE, b_pos in 0usize..VEC_SIZE, b_len in 1usize..VEC_SIZE) { - t_ins_ins(VEC_SIZE, a_pos, a_len, b_pos, b_len) + fn transform_ins_ins((vec_len, a, b) in test_params(), a_len in 1usize..VEC_SIZE, b_len in 1usize..VEC_SIZE) { + t_ins_ins(vec_len, a.0, a_len, b.0, b_len) } #[test] - fn transform_del_del(a_pos in 0usize..VEC_SIZE, a_len in 1usize..VEC_SIZE, b_pos in 0usize..VEC_SIZE, b_len in 1usize..VEC_SIZE) { - // Limit inputs - let a_len = a_len.min(VEC_SIZE - a_pos); - let b_len = b_len.min(VEC_SIZE - b_pos); - - t_del_del(VEC_SIZE, a_pos, a_len, b_pos, b_len) + fn transform_del_del((vec_len, a, b) in test_params()) { + t_del_del(vec_len, a.0, a.1, b.0, b.1) } #[test] - fn transform_ins_del(a_pos in 0usize..VEC_SIZE, a_len in 1usize..VEC_SIZE, b_pos in 0usize..VEC_SIZE, b_len in 1usize..VEC_SIZE) { - let b_len = b_len.min(VEC_SIZE - b_pos); - - t_ins_del(VEC_SIZE, a_pos, a_len, b_pos, b_len) + fn transform_ins_del((vec_len, a, b) in test_params(), i_len in 1usize..VEC_SIZE) { + t_ins_del(vec_len, a.0, i_len, b.0, b.1) } #[test] - fn transform_mov_ins(a_len in 1usize..VEC_SIZE, a_pos in 0usize..VEC_SIZE, a_to in 0usize..VEC_SIZE, b_pos in 0usize..VEC_SIZE, b_len in 1usize..VEC_SIZE) { - // Limit inputs - let a_to = filter_to(VEC_SIZE, a_pos, a_to); - let a_len = a_len.min(VEC_SIZE - a_pos.max(a_to)); - - t_mov_ins(VEC_SIZE, a_pos, a_len, a_to, b_pos, b_len) + fn transform_mov_ins((vec_len, a, b) in test_params(), i_len in 1usize..VEC_SIZE) { + t_mov_ins(vec_len, a.0, a.1, a.2, b.0, i_len) } - /* #[test] - fn transform_mov_del(a_pos in 0usize..VEC_SIZE, a_len in 0usize..VEC_SIZE, a_to in 0usize..VEC_SIZE, b_pos in 0usize..VEC_SIZE, b_len in 1usize..VEC_SIZE) { - // Limit inputs - let a_to = filter_to(VEC_SIZE, a_pos, a_to); - let a_len = a_len.min(VEC_SIZE - a_pos.max(a_to)); - let b_len = b_len.min(VEC_SIZE - b_pos); - - t_mov_del(VEC_SIZE, a_pos, a_len, a_to, b_pos, b_len, None) + fn transform_mov_del((vec_len, a, b) in test_params()) { + t_mov_del(vec_len, a.0, a.1, a.2, b.0, b.1) } - */ - /* #[test] - fn transform_mov_mov(a_pos in 0usize..VEC_SIZE, a_len in 0usize..VEC_SIZE, a_to in 0usize..VEC_SIZE, b_pos in 0usize..VEC_SIZE, b_len in 1usize..VEC_SIZE, b_to in 0usize..VEC_SIZE) { - // Limit inputs - let a_to = filter_to(VEC_SIZE, a_pos, a_to); - let a_len = a_len.min(VEC_SIZE - a_pos.max(a_to)); - let b_to = filter_to(VEC_SIZE, b_pos, b_to); - let b_len = b_len.min(VEC_SIZE - b_pos.max(b_to)); - - t_mov_mov(VEC_SIZE, a_pos, a_len, a_to, b_pos, b_len, b_to) + fn transform_mov_mov((vec_len, a, b) in test_params()) { + t_mov_mov(vec_len, a.0, a.1, a.2, b.0, b.1, b.2); + } + + #[test] + fn transform_seq((vec_len, a, b, a2, b2, a3, b3) in test_params().prop_flat_map(|(vec_len, a, b)| + (Just(vec_len), Just(a), Just(b), mov_params(vec_len), mov_params(vec_len), mov_params(vec_len), mov_params(vec_len))) + ) { + let input = testvec(vec_len); + let a = Operation::Seq { ops: vec![ + Operation::Mov { + pos: a.0, + n: a.1, + to: a.2, + }, + Operation::Ins { pos: a2.0, val: testvec(a3.1) }, + Operation::Del { pos: a3.0, n: a3.1 }, + ] }; + let b = Operation::Seq { ops: vec![ + Operation::Mov { + pos: b.0, + n: b.1, + to: b.2, + }, + Operation::Ins { pos: b2.0, val: testvec(b3.1) }, + Operation::Del { pos: b3.0, n: b3.1 }, + ] }; + test_transform_cond(&a, &b, &input); + test_transform_cond(&b, &a, &input); } - */ } diff --git a/tests/tests.rs b/tests/tests.rs index 0f1912f..e205ed0 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -16,6 +16,18 @@ fn apply_mov(#[case] pos: usize, #[case] n: usize, #[case] to: usize, #[case] ex assert_eq!(input, expect); } +#[test] +fn print() { + let input = vec![1, 2, 3, 4, 5]; + + let op = Operation::Mov { + pos: 1, + n: 2, + to: 4, + }; + op.print_on(&input); +} + #[test] fn transform_ins_ins() { let a = Operation::Ins { @@ -381,5 +393,5 @@ fn transform_mov_mov5() { n: 1, to: 3, }; - test_transform(&a, &b, &input, &[2, 3, 4, 1]); + test_transform(&a, &b, &input, &[2, 3, 1, 4]); } diff --git a/tests/tests_mov.rs b/tests/tests_mov.rs index c1817d5..76fe21e 100644 --- a/tests/tests_mov.rs +++ b/tests/tests_mov.rs @@ -1,42 +1,589 @@ mod util; use otvec::Operation; -use util::test_transform_sym; +use util::{test_transform, test_transform_sym}; use rstest::rstest; -// MOV: [0, 1, 2, 3, 4, 5, 6, 7, 8] => [0, 1, 2, 3, 4, 5, 6, 7, 8] -// DEL: [0, 1, 2, 3, 4, 5, 6, 7, 8] => [0, 1, 2, 3, 4, 5, 6, 7, 8] - #[rstest] -// MOV-DEL; Intersection::Overlap, Intersection::Empty; right end; to right +// 1 MOV-DEL; Intersection::Overlap, Intersection::Empty; right end; to right // MOV: [0, (1, 2, 3), 4, 5, 6, 7, 8] => [0, 4, 5, (1, 2, 3), 6, 7, 8] // DEL: [(0, 1), 2, 3, 4, 5, 6, 7, 8] => [2, 3, 4, 5, 6, 7, 8] #[case(Operation::Mov {pos: 1, n: 3, to: 6}, Operation::Del {pos: 0, n: 2}, &[4, 5, 2, 3, 6, 7, 8])] -// MOV-DEL; Intersection::Overlap, Intersection::Empty; right end; to left +// 2 MOV-DEL; Intersection::Overlap, Intersection::Empty; right end; to left // MOV: [0, 1, 2, (3, 4, 5), 6, 7, 8] => [0, (3, 4, 5), 1, 2, 6, 7, 8] // DEL: [0, 1, (2, 3), 4, 5, 6, 7, 8] => [0, 1, 4, 5, 6, 7, 8] #[case(Operation::Mov {pos: 3, n: 3, to: 1}, Operation::Del {pos: 2, n: 2}, &[0, 4, 5, 1, 6, 7, 8])] -// current debugging - -// MOV-DEL; Intersection::Overlap, Intersection::Over; +// 3 MOV-DEL; Intersection::LessOverlap, Intersection::Over; +// - // MOV: [0, 1, 2, 3, 4, (5, 6, 7), 8] => [0, 1, (5, 6, 7), 2, 3, 4, 8] // DEL: [0, (1, 2, 3, 4, 5), 6, 7, 8] => [0, 6, 7, 8] #[case(Operation::Mov {pos: 5, n: 3, to: 2}, Operation::Del {pos: 1, n: 5}, &[0, 6, 7, 8])] - -// MOV: [0, 1, 2, 3, 4, (5, 6, 7), 8] => [0, 1, 2, (5, 6, 7), 3, 4, 8] +// 4 ibt: over - +// MOV: [0, 1, 2,|3, 4, (5, 6, 7), 8] => [0, 1, 2, (5, 6, 7), 3, 4, 8] // DEL: [0, (1, 2, 3, 4, 5), 6, 7, 8] => [0, 6, 7, 8] #[case(Operation::Mov {pos: 5, n: 3, to: 3}, Operation::Del {pos: 1, n: 5}, &[0, 6, 7, 8])] -// MOV-DEL; Intersection::Overlap, Intersection::Over; -// MOV: [0, 1, (2, 3, 4, 5, 6), 7, 8] => [0, (2, 3, 4, 5, 6), 1, 7, 8] +// 5 MOV-DEL; Intersection::Overlap, Intersection::Over; ibt: LessOverlap +// - +// MOV: [0,|1, (2, 3, 4, 5, 6), 7, 8] => [0, (2, 3, 4, 5, 6), 1, 7, 8] // DEL: [(0, 1, 2), 3, 4, 5, 6, 7, 8] => [3, 4, 5, 6, 7, 8] #[case(Operation::Mov {pos: 2, n: 5, to: 1}, Operation::Del {pos: 0, n: 3}, &[3, 4, 5, 6, 7, 8])] +// 6 ibt: over ---- +// MOV: [|0, 1, 2, 3,(4, 5, 6), 7, 8] => [(4, 5, 6), 0, 1, 2, 3, 7, 8] => [(4, 5), 6, (0, 1, 2, 3), 7, 8] +// DEL: [(0, 1, 2, 3, 4, 5), 6, 7, 8] => [6, 7, 8] +#[case(Operation::Mov { pos: 4, n: 3, to: 0 }, Operation::Del { pos: 0, n: 6 }, &[6, 7, 8])] +// 7 +// MOV: [0, 1, 2, 3, 4, (5, 6), 7, 8] => [0, 1, 2, 3, (5, 6), 4, 7, 8] +// DEL: [(0, 1, 2, 3, 4, 5), 6, 7, 8] => [6, 7, 8] +#[case(Operation::Mov { pos: 5, n: 2, to: 4 }, Operation::Del { pos: 0, n: 6 }, &[6, 7, 8])] +// 8 less moved to DEL +// MOV: [0, 1, 2, 3, (4, 5),6, 7, 8] => [0,(4, 5),1, 2, 3, 6, 7, 8] => [0,(4, 5),3, 6, 7, 8] +// DEL: [0,(1, 2), 3, 4, 5, 6, 7, 8] => [0, 3,(4, 5), 6, 7, 8] => [0, (4, 5), 3, 6, 7, 8] +#[case(Operation::Mov { pos: 4, n: 2, to: 1 }, Operation::Del { pos: 1, n: 2 }, &[0, 4, 5, 3, 6, 7, 8])] +// 9 less moved right from del +// MOV: [0, 1, 2, 3, (4, 5),6, 7, 8] => [0, 1, 2, 3, 6, 7, (4, 5), 8] +// DEL: [0,(1, 2), 3, 4, 5, 6, 7, 8] => [0, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 4, n: 2, to: 8 }, Operation::Del { pos: 1, n: 2 }, &[0, 3, 6, 7, 4, 5, 8])] +// 10 less moved inside DEL +// MOV: [0, 1, 2, 3,(4, 5),6, 7, 8] => [0, (1), 4, 5, (2), 3, 6, 7, 8] +// DEL: [0,(1, 2),3, 4, 5, 6, 7, 8] => [0, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 4, n: 2, to: 2 }, Operation::Del { pos: 1, n: 2 }, &[0, 4, 5, 3, 6, 7, 8])] +// 11 less moved left from DEL +// MOV: [0, 1, 2, 3,(4, 5),6, 7, 8] => [(4, 5),0, 1, 2, 3, 6, 7, 8] +// DEL: [0,(1, 2),3, 4, 5, 6, 7, 8] => [0, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 4, n: 2, to: 0 }, Operation::Del { pos: 1, n: 2 }, &[4, 5, 0, 3, 6, 7, 8])] +// 12 greater moved right from DEL +// MOV: [0,(1, 2),3, 4, 5, 6, 7, 8] => [0, 3, (4, 5), 6, 7, 1, 2, 8] => [0, 3, 6, 7, 1, 2, 8] +// DEL: [0, 1, 2, 3,(4, 5),6, 7, 8] => [0, (1, 2), 3, 6, 7, 8] => [0, 3, 6, 7, 1, 2, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 8 }, Operation::Del { pos: 4, n: 2 }, &[0, 3, 6, 7, 1, 2, 8])] +// 13 greater moved left from DEL +// MOV: [(0), 1, 2, 3, 4, 5, 6, 7, 8] => [1, 0, 2, 3, 4, 5, 6, 7, 8] +// DEL: [0, (1), 2, 3, 4, 5, 6, 7, 8] => [0, 2, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 1, to: 2 }, Operation::Del { pos: 1, n: 1 }, &[0, 2, 3, 4, 5, 6, 7, 8])] +// 14 greater moved left from DEL +// MOV: [(0), 1, 2, 3, 4, 5, 6, 7, 8] => [1, 0, 2, 3, 4, 5, 6, 7, 8] +// DEL: [0, 1, (2), 3, 4, 5, 6, 7, 8] => [0, 1, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 1, to: 2 }, Operation::Del { pos: 2, n: 1 }, &[1, 0, 3, 4, 5, 6, 7, 8])] +// 15 greater moved left from DEL +// MOV: [(0, 1), 2, 3, 4, 5, 6, 7, 8] => [2, 0, 1, 3, 4, 5, 6, 7, 8] +// DEL: [0, 1, (2), 3, 4, 5, 6, 7, 8] => [0, 1, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Del { pos: 2, n: 1 }, &[0, 1, 3, 4, 5, 6, 7, 8])] +// 16 greater moved inside DEL +// MOV: [(0, 1), 2, 3, 4, 5, 6, 7, 8] => [(2), 0, 1, (3, 4), 5, 6, 7, 8] +// DEL: [0, 1, (2, 3, 4), 5, 6, 7, 8] => [0, 1, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Del { pos: 2, n: 3 }, &[0, 1, 5, 6, 7, 8])] -// MOV-DEL; Intersection::Overlap, Intersection::Empty; left end; to left -// MOV: [0, 1, 2, (3, 4, 5), 6, 7, 8] => [0, (3, 4, 5), 1, 2, 6, 7, 8] -// DEL: [0, 1, 2, 3, 4, (5, 6), 7, 8] => [0, 1, 2, 3, 4, 7, 8] -// #[case(Operation::Mov {pos: 3, n: 3, to: 1}, Operation::Del {pos: 5, n: 2}, &[0, 6, 7, 1, 2, 3, 8])] +// 14 greater moved to DEL +// MOV: [0,(1, 2),3, 4, 5, 6, 7, 8] => [0, 3, 4, 5,(1, 2),6, 7, 8] => [0, 3, (1, 2), 6, 7, 8] +// DEL: [0, 1, 2, 3,(4, 5),6, 7, 8] => [0, (1, 2), 3, 6, 7, 8] => [0, 3, (1, 2), 6, 7, 8] +// #[case(Operation::Mov { pos: 1, n: 2, to: 6 }, Operation::Del { pos: 4, n: 2 }, &[0, 3, 1, 2, 6, 7, 8])] +// +// MOV: [0, 1, 2, (3, 4, 5), 6, 7, 8] => [0, (3, 4, 5), 1, 2, 6, 7, 8] => [0, 4, 5, 6, 7, 8] +// DEL: [0, (1, 2, 3), 4, 5, 6, 7, 8] => [0, 4, 5, 6, 7, 8] +// #[case(Operation::Mov { pos: 3, n: 3, to: 1 }, Operation::Del { pos: 1, n: 3 }, &[0, 4, 5, 6, 7, 8])] fn transform(#[case] a: Operation, #[case] b: Operation, #[case] expect: &[i32]) { let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; test_transform_sym(&a, &b, &input, expect); } + +#[rstest] +// MOV: (0) 1 2 3 4 5 6 7 8 => 1 (0) 2 3 4 5 6 7 8 +// DEL: 0 (1 2) 3 4 5 6 7 8 => 0 |3 4 5 6 7 8 +#[case(Operation::Mov { pos: 0, n: 1, to: 2 }, Operation::Del { pos: 1, n: 2 }, &[0, 3, 4, 5, 6, 7, 8])] +#[case(Operation::Mov { pos: 0, n: 1, to: 3 }, Operation::Del { pos: 2, n: 2 }, &[1, 0, 4, 5, 6, 7, 8])] +#[case(Operation::Mov { pos: 0, n: 1, to: 3 }, Operation::Del { pos: 1, n: 2 }, &[0, 3, 4, 5, 6, 7, 8])] +// MOV: (0) 1 2 3 4 5 6 7 8 => 1 (0) 2 3 4 5 6 7 8 +// DEL: 0 1 (2) 3 4 5 6 7 8 => 0 1 3 4 5 6 7 8 +#[case(Operation::Mov { pos: 0, n: 1, to: 2 }, Operation::Del { pos: 2, n: 1 }, &[1, 0, 3, 4, 5, 6, 7, 8])] +// MOV: [(0), 1, 2, 3, 4, 5, 6, 7, 8] => [(1), 0, 2, 3, 4, 5, 6, 7, 8] +// DEL: [0, (1), 2, 3, 4, 5, 6, 7, 8] => [0, 2, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 1, to: 2 }, Operation::Del { pos: 1, n: 1 }, &[0, 2, 3, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, 3, 4, 5, 6, 7, 8] => [(2), 0, 1, 3, 4, 5, 6, 7, 8] +// DEL: [0, 1, (2), 3, 4, 5, 6, 7, 8] => [0, 1, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Del { pos: 2, n: 1 }, &[0, 1, 3, 4, 5, 6, 7, 8])] +fn transform2(#[case] a: Operation, #[case] b: Operation, #[case] expect: &[i32]) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform_sym(&a, &b, &input, expect); +} + +#[rstest] +// MOV: [(0, 1), 2, 3, 4, 5, 6, 7, 8] => [(2), 0, (1), 3, 4, 5, 6, 7, 8] +// DEL: [0, (1, 2), 3, 4, 5, 6, 7, 8] => [0, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Del { pos: 1, n: 2 }, &[0, 3, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2,|3, 4, 5, 6, 7, 8] => [(2),|0, (1, 3), 4, 5, 6, 7, 8] +// DEL: [0, (1, 2, 3), 4, 5, 6, 7, 8] => [0, 4, 5, 6, 7, 8] +// rtarget: 1-2 +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Del { pos: 1, n: 3 }, &[0, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, 3,|4, 5, 6, 7, 8] => [(2, 3), 0, (1), 4, 5, 6, 7, 8] +// DEL: [0, (1, 2, 3), 4, 5, 6, 7, 8] => [0, 4, 5, 6, 7, 8] +// rtarget: 2-3 +#[case(Operation::Mov { pos: 0, n: 2, to: 4 }, Operation::Del { pos: 1, n: 3 }, &[0, 4, 5, 6, 7, 8])] +// MOV: [(0,1, 2),3,|4, 5, 6, 7, 8] => [(3), 0, 1, (2, 4, 5), 6, 7, 8] +// DEL: [0, 1,(2, 3, 4, 5), 6, 7, 8] => [0, 1, 6, 7, 8] +// rtarget: 1-3 +#[case(Operation::Mov { pos: 0, n: 3, to: 4 }, Operation::Del { pos: 2, n: 4 }, &[0, 1, 6, 7, 8])] +// MOV: [(0,1, 2),3,|4, 5, 6, 7, 8] => [(3), 0, (1, 2, 4), 5, 6, 7, 8] +// DEL: [0,(1, 2, 3, 4),5, 6, 7, 8] => [0, 5, 6, 7, 8] +// rtarget: 1-3 +#[case(Operation::Mov { pos: 0, n: 3, to: 4 }, Operation::Del { pos: 1, n: 4 }, &[0, 5, 6, 7, 8])] +// MOV: [0, (1, 2), 3, 4, 5, 6, 7, 8] => [1, (2), 0, (3), 4, 5, 6, 7, 8] +// DEL: [0, 1, (2, 3), 4, 5, 6, 7, 8] => [0, (1), 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Del { pos: 2, n: 2 }, &[1, 0, 4, 5, 6, 7, 8])] +fn transform3(#[case] a: Operation, #[case] b: Operation, #[case] expect: &[i32]) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform_sym(&a, &b, &input, expect); +} + +#[rstest] +// MOV: [(0, 1, 2), 3,|4, 5, 6, 7, 8] => [3, (0, 1), 2, 4, 5, 6, 7, 8] +// DEL: [(0, 1), 2, 3, 4, 5, 6, 7, 8] => [(2), 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 4 }, Operation::Del { pos: 0, n: 2 }, &[3, 2, 4, 5, 6, 7, 8])] +// MOV: [0, (1, 2, 3), 4, 5, 6, 7, 8] => [(1, 2), 3, 0, 4, 5, 6, 7, 8] +// DEL: [0, (1, 2), 3, 4, 5, 6, 7, 8] => [0, (3), 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 3, to: 0 }, Operation::Del { pos: 1, n: 2 }, &[3, 0, 4, 5, 6, 7, 8])] +fn transform_mov_del_within( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform_sym(&a, &b, &input, expect); +} + +#[rstest] +// MOV: [(0, 1), 2,|3, 4, 5, 6, 7, 8] => [(2), (0, 1), 3, 4, 5, 6, 7, 8] +// DEL: [(0, 1, 2), 3, 4, 5, 6, 7, 8] => [3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 5 }, Operation::Del { pos: 0, n: 3 }, &[3, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, 3, 4,|5, 6, 7, 8] => [(2), 3, 4, (0, 1), 5, 6, 7, 8] +// DEL: [(0, 1, 2), 3, 4, 5, 6, 7, 8] => [3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 5 }, Operation::Del { pos: 0, n: 3 }, &[3, 4, 5, 6, 7, 8])] +// MOV: [0, (1, 2), 3, 4, 5, 6, 7, 8] => [(0), 3, (1, 2), 4, 5, 6, 7, 8] +// DEL: [(0, 1, 2), 3, 4, 5, 6, 7, 8] => [3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 4 }, Operation::Del { pos: 0, n: 3 }, &[3, 4, 5, 6, 7, 8])] +// MOV: [|0,(1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2), 0, (3), 4, 5, 6, 7, 8] +// DEL: [0, (1, 2, 3), 4, 5, 6, 7, 8] => [0, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Del { pos: 1, n: 3 }, &[0, 4, 5, 6, 7, 8])] +// MOV: [0,|1, (2, 3), 4, 5, 6, 7, 8] => [(0, 2, 3, 1), 4, 5, 6, 7, 8] +// DEL: [(0, 1, 2, 3), 4, 5, 6, 7, 8] => [4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 2, to: 1 }, Operation::Del { pos: 0, n: 4 }, &[4, 5, 6, 7, 8])] +fn transform_mov_del_over( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform_sym(&a, &b, &input, expect); +} + +#[rstest] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [2, (0, 1), 3,|4, 5, 6, 7, 8] m2_to+0 +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [2, 3, (0, 1), 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 0, n: 2, to: 4 }, &[2, 3, 0, 1, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [2,|3, (0,|1), 4, 5, 6, 7, 8] m2_to-mn +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [2, (0, 1), 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 4 }, Operation::Mov { pos: 0, n: 2, to: 3 }, &[2, 0, 1, 3, 4, 5, 6, 7, 8])] +// MOV: [|0, 1, (2, 3), 4, 5, 6, 7, 8] => [(2,|3), 0,|1, 4, 5, 6, 7, 8] m2_to+mn +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [0, (2, 3), 1, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 2, to: 0 }, Operation::Mov { pos: 2, n: 2, to: 1 }, &[0, 2, 3, 1, 4, 5, 6, 7, 8])] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2), 0, 3,|4, 5, 6, 7, 8] m2_to+0 +// MOV: [0, (1, 2), 3, |4, 5, 6, 7, 8] => [0, 3, (1, 2), 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Mov { pos: 1, n: 2, to: 4 }, &[0, 3, 1, 2, 4, 5, 6, 7, 8])] +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [|0, (2, 3), 1, 4, 5, 6, 7, 8] m2_to+0 +// MOV: [|0, 1, (2, 3), 4, 5, 6, 7, 8] => [(2, 3), 0, 1, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 2, to: 1 }, Operation::Mov { pos: 2, n: 2, to: 0 }, &[2, 3, 0, 1, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, 3, 4, |5, 6, 7, 8] => [2, 3, 4,|(0, 1), 5, 6, 7, 8] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [2, (0, 1), 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 5 }, Operation::Mov { pos: 0, n: 2, to: 3 }, &[2, 0, 1, 3, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, 3, 4, 5, |6, 7, 8] => [2, 3, 4, 5, (0, 1), 6, 7, 8] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [2, (0, 1), 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 6 }, Operation::Mov { pos: 0, n: 2, to: 3 }, &[2, 0, 1, 3, 4, 5, 6, 7, 8])] +fn transform_mov_mov_same( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform(&a, &b, &input, expect); +} + +#[rstest] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [|2, (0, 1), {3, 4}, 5, 6, 7, 8] Sa/Gr +// MOV: [|0, 1, 2, (3, 4), 5, 6, 7, 8] => [ (3, 4), {0, 1}, 2,|5, 6, 7, 8] => [3, 4, 2, (0, 1), 5, 6, 7, 8] +// +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 3, n: 2, to: 0 }, &[3, 4, 2, 0, 1, 5, 6, 7, 8], true)] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [|(1, 2), 0, {3, 4}, 5, 6, 7, 8] Go/Gr +// MOV: [|0, 1, 2, (3, 4), 5, 6, 7, 8] => [(3, 4),| 0, {1, 2}, 5, 6, 7, 8] +// #[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Mov { pos: 3, n: 2, to: 0 }, &[3, 4, 1, 2, 0, 5, 6, 7, 8], false)] +#[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Mov { pos: 3, n: 2, to: 0 }, &[1, 2, 3, 4, 0, 5, 6, 7, 8], false)] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2), 0,|{3, 4}, 5, 6, 7, 8] Sa/Gr +// MOV: [0, |1, 2, (3, 4), 5, 6, 7, 8] => [|0, (3, 4), {1, 2}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Mov { pos: 3, n: 2, to: 1 }, &[1, 2, 0, 3, 4, 5, 6, 7, 8], true)] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2), 0, {3, 4}, 5,|6, 7, 8] Le/Gr +// MOV: [0, 1, 2, (3, 4), 5, |6, 7, 8] => [|0, {1, 2}, 5, (3, 4), 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Mov { pos: 3, n: 2, to: 6 }, &[1, 2, 0, 5, 3, 4, 6, 7, 8], true)] +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [|0, (2, 3), 1, {4, 5}, 6, 7, 8] Gr/Gr +// MOV: [|0, 1, 2, 3, (4, 5), 6, 7, 8] => [(4, 5), 0,| 1, {2, 3}, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 2, to: 1 }, Operation::Mov { pos: 4, n: 2, to: 0 }, &[4, 5, 0, 2, 3, 1, 6, 7, 8], true)] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [|(1, 2), 0, {3, 4, 5}, 6, 7, 8] +// MOV: [|0, 1, 2, (3, 4, 5), 6, 7, 8] => [(3, 4, 5),|0, {1, 2}, 6, 7, 8] +// #[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Mov { pos: 3, n: 3, to: 0 }, &[3, 4, 5, 1, 2, 0, 6, 7, 8], false)] +#[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Mov { pos: 3, n: 3, to: 0 }, &[1, 2, 3, 4, 5, 0, 6, 7, 8], false)] +// MOV: [|0, 1, (2, 3), 4, 5, 6, 7, 8] => [(2, 3), 0,|1, {4, 5}, 6, 7, 8] Go/Gr +// MOV: [0, |1, 2, 3, (4, 5), 6, 7, 8] => [|0, (4, 5), 1, {2, 3}, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 2, to: 0 }, Operation::Mov { pos: 4, n: 2, to: 1 }, &[2, 3, 0, 4, 5, 1, 6, 7, 8], false)] + +// MOV: [(0, 1), 2, 3, 4, |5, 6, 7, 8] => [{2, 3}, 4, (0, 1), 5, 6, 7, 8] +// MOV: [|0, 1, (2, 3), 4, 5, 6, 7, 8] => [(2, 3), {0, 1}, 4,|5, 6, 7, 8] +// #[case(Operation::Mov { pos: 0, n: 2, to: 5 }, Operation::Mov { pos: 2, n: 2, to: 0 }, &[2, 3, 4, 0, 1, 5, 6, 7, 8], false)] +fn transform_mov_mov_less( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], + #[case] sym: bool, +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + if sym { + test_transform_sym(&a, &b, &input, expect); + } else { + test_transform(&a, &b, &input, expect); + } +} + +#[rstest] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [2, (0, 1), 3, 4, 5, 6, 7, 8] => [0, 1, 2, 3, 4, 5, 6, 7, 8] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2), 0,|3, 4, 5, 6, 7, 8] => [0, (1, 2), 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 1, n: 2, to: 0 }, &[1, 2, 0, 3, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [{2}, (0, {1}), 3, 4, 5, 6, 7, 8] => [{2}, 0, 3, 1, 4..] => [0, 3, 1, 2, 4, 5, 6, 7, 8] +// MOV: [0, (1, 2), 3, |4, 5, 6, 7, 8] => [0, 3, (1, 2), 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 1, n: 2, to: 4 }, &[3, 1, 2, 0, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [2, (0, 1), 3, 4, 5, 6, 7, 8] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2), 0, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 1, n: 2, to: 0 }, &[1, 2, 0, 3, 4, 5, 6, 7, 8])] +// MOV: [0, (1, 2), 3, |4, 5, 6, 7, 8] => [0, 3, (1, 2), 4, 5, 6, 7, 8] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [{2}, (0, 1), 3,|4, 5, 6, 7, 8] => [0, 1, 3, 2, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 4 }, Operation::Mov { pos: 0, n: 2, to: 3 }, &[0, 1, 3, 2, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [2, 3, (0, 1), 4, 5, 6, 7, 8] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2), 0, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 4 }, Operation::Mov { pos: 1, n: 2, to: 0 }, &[1, 2, 3, 0, 4, 5, 6, 7, 8])] +fn transform_mov_mov_truncated( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform(&a, &b, &input, expect); +} + +#[rstest] +// MOV: [(0), 1, 2, 3, 4, 5, 6, 7, |8] => [1, 2, 3, 4, 5, 6, 7, (0), 8] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [2, (0, 1), 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 1, to: 8 }, Operation::Mov { pos: 0, n: 2, to: 3 }, &[2, 0, 1, 3, 4, 5, 6, 7, 8])] +fn transform_mov_mov_within( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform(&a, &b, &input, expect); +} + +#[rstest] +// MOV: [(0, 1, 2), 3, |4, 5, 6, 7, 8] => [3,(0,{1},2),4, 5, 6,|7, 8] +// MOV: [ 0,(1), 2, 3, 4, 5, 6, |7, 8] => [{0, 2}, 3,|4, 5, 6,(1), 7, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 4 }, Operation::Mov { pos: 1, n: 1, to: 7 }, &[3, 0, 2, 4, 5, 6, 1, 7, 8])] +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [0, (2, 3), 1, 4, 5, 6, 7, 8] => [0, (2), 3, 1, 4, 5, 6, 7,|8] +// MOV: [0, 1, (2), 3, 4, 5, 6, 7, |8] => [0, 1, 3, 4, 5, 6, 7, (2), 8] => [0,|1, (3), 4, 5, 6, 7, 2, 8] !! +#[case(Operation::Mov { pos: 2, n: 2, to: 1 }, Operation::Mov { pos: 2, n: 1, to: 8 }, &[0, 3, 1, 4, 5, 6, 7, 2, 8])] +// MOV: [|0,*(1, 2, 3), 4, 5, 6, 7, 8] => [(1, 2, 3), 0,*4, 5, 6, 7, 8] => [1, (2, 3), 0,|4, 5, 6, 7, 8] !!b2 +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [0, (2, 3), 1, 4, 5, 6, 7, 8] => [|0, 2, 3, (1), 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 3, to: 0 }, Operation::Mov { pos: 2, n: 2, to: 1 }, &[1, 0, 2, 3, 4, 5, 6, 7, 8])] +// #equal targets +// MOV: [(0, 1, 2), 3, |4, 5, 6, 7, 8] => [3, (0, 1, 2), 4, 5, 6, 7, 8] => [ 3, (0, 1), 2,|4, 5, 6, 7, 8] ? +m2_n +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [2, 3, (0, 1), 4, 5, 6, 7, 8] => [(2), 3,|0, 1, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 4 }, Operation::Mov { pos: 0, n: 2, to: 4 }, &[3, 2, 0, 1, 4, 5, 6, 7, 8])] +// MOV: [(0, 1, 2), 3, 4, |5, 6, 7, 8] => [3, 4, ({0, 1},|2),|5, 6, 7, 8] +// MOV: [(0, 1), 2, 3, 4, |5, 6, 7, 8] => [{2}, 3, 4,|(0, 1), 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 5 }, Operation::Mov { pos: 0, n: 2, to: 5 }, &[3, 4, 2, 0, 1, 5, 6, 7, 8])] +// MOV: [|0, (1, 2, 3), 4, 5, 6, 7, 8] => [({1, 2}, 3),|0, 4, 5, 6, 7, 8] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [|(1, 2), 0, {3}, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 3, to: 0 }, Operation::Mov { pos: 1, n: 2, to: 0 }, &[3, 1, 2, 0, 4, 5, 6, 7, 8])] +// #split +// MOV: [|0, (1, 2, 3,*4), 5, 6, 7, 8] => [|({1, 2}, 3,|4), 0, 5, 6, 7, 8] => [3, 1, 2, 4, 0, 5, 6, 7, 8] +// MOV: [0, (1, 2), 3,| 4, 5, 6, 7, 8] => [|0, {3, (1, 2), 4}, 5, 6, 7, 8] => [3, 1, 2, 4, 0, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 4, to: 0 }, Operation::Mov { pos: 1, n: 2, to: 4 }, &[3, 1, 2, 4, 0, 5, 6, 7, 8])] +// MOV: [|0, (1,*2, 3, 4), 5, 6, 7, 8] => [ (1,|2, {3, 4}), 0, 5, 6, 7, 8] +// MOV: [ 0, 1,|2, (3, 4), 5, 6, 7, 8] => [|0, {1,(3, 4), 2}, 5, 6, 7, 8] => [1, 3, 4, 2, 0, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 4, to: 0 }, Operation::Mov { pos: 3, n: 2, to: 2 }, &[1, 3, 4, 2, 0, 5, 6, 7, 8])] +fn transform_mov_mov_over( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform(&a, &b, &input, expect); +} + +#[rstest] +// b split by a +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [|2, (0, {1}), 3, 4, 5, 6, 7, 8] +// MOV: [|0, (1, 2, 3), 4, 5, 6, 7, 8] => [(1, 2,|3), {0}, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 1, n: 3, to: 0 }, &[1, 2, 0, 3, 4, 5, 6, 7, 8])] +// MOV: [0, (1, 2), 3, |4, 5, 6, 7, 8] => [|{0}, 3, (1, {2}), 4,|5, 6, 7, 8] => [2, (0), 3, 1, 4,|5, 6, 7, 8] +// MOV: [|0, 1, (2, 3, 4), 5, 6, 7, 8] => [(2, 3,|4), 0, {1}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 4 }, Operation::Mov { pos: 2, n: 3, to: 0 }, &[2, 3, 1, 4, 0, 5, 6, 7, 8])] +// MOV: [0, (1, 2), 3, 4, |5, 6, 7, 8] => [0, 3, 4, (1, {2}), 5, 6, 7, 8] => [2, {0}, 3, 4, 1, 5,|6, 7, 8] +// MOV: [|0, 1, (2, 3, 4, 5), 6, 7, 8] => [(2, 3, 4,|5), 0, {1}, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 5 }, Operation::Mov { pos: 2, n: 4, to: 0 }, &[2, 3, 4, 1, 5, 0, 6, 7, 8])] +// MOV: [(0, 1, 2), 3, |4, 5, 6, 7, 8] => [{3, (0, 1, 2), 4}, 5,|6, 7, 8] => [5,|3, 0, 1, {2}, 4, 6, 7, 8] => [5, 2, 3, 0, 1, 4, 6, 7, 8] +// MOV: [0, 1, (2, 3, 4), 5, |6, 7, 8] => [(0, 1), 5, (2, 3,|4), 6, 7, 8] => [5, 2, 3, 0, 1, 4, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 4 }, Operation::Mov { pos: 2, n: 3, to: 6 }, &[5, 2, 3, 0, 1, 4, 6, 7, 8])] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [{2, (0, 1), 3}, 4, 5,|6, 7, 8] => [4, 5,|(2, 0, {1}, 3), 6, 7, 8] +// MOV: [0, (1, 2, 3), 4, 5, |6, 7, 8] => [{0}, 4, 5, (1, 2,|3), 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 1, n: 3, to: 6 }, &[4, 5, 1, 2, 0, 3, 6, 7, 8])] +// a split by b +// MOV: [(0, 1, 2), 3, |4, 5, 6, 7, 8] => [|3, (0, 1, {2}), 4, 5, 6, 7, 8] => [(2), 3, 0, 1, 4, 5, 6, 7, 8] +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [{0},(2, 3), {1},|4, 5, 6, 7, 8] => [2, 3, (0), (1), 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 4 }, Operation::Mov { pos: 2, n: 2, to: 1 }, &[2, 3, 0, 1, 4, 5, 6, 7, 8])] +// MOV: [(0, 1, 2, 3), 4, |5, 6, 7, 8] => [|4, (0, 1, {2, 3}), 5, 6, 7, 8] => [(2, 3), 4, 0, 1, 5, 6, 7, 8] +// MOV: [0, |1, (2, 3, 4), 5, 6, 7, 8] => [{0}, (2, 3, 4), {1},|5, 6, 7, 8] => [2, 3, 4, 0, 1, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 4, to: 5 }, Operation::Mov { pos: 2, n: 3, to: 1 }, &[2, 3, 4, 0, 1, 5, 6, 7, 8])] +// MOV: [(0, 1, 2), 3, 4, 5, |6, 7, 8] => [3, 4, 5, (0, 1, {2}), 6, 7, 8] => [2, 3, 4, 5, 0, 1, 6, 7, 8] +// MOV: [0, |1, (2, 3, 4), 5, 6, 7, 8] => [{0}, (2, 3, 4), {1}, 5,|6, 7, 8] => [2, 3, 4, 5, (0), (1), 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 6 }, Operation::Mov { pos: 2, n: 3, to: 1 }, &[2, 3, 4, 5, 0, 1, 6, 7, 8])] +// MOV: [|0, (1, 2, 3), 4, 5, 6, 7, 8] => [(1, 2, {3}), 0,| 4, 5, 6, 7, 8] +// MOV: [0, 1, |2, (3, 4), 5, 6, 7, 8] => [|0, {1}, (3, 4), {2}, 5, 6, 7, 8] => [1, 2, 0, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 3, to: 0 }, Operation::Mov { pos: 3, n: 2, to: 2 }, &[1, 2, 0, 3, 4, 5, 6, 7, 8])] +// both split +// MOV: [(0, 1, 2, 3), 4, |5, 6, 7, 8] => [|4, (0, 1, {2, 3}), 5, 6, 7, 8] => [2, 3, 4, 0, 1, 5, 6, 7, 8] +// MOV: [0, |1, (2, 3, 4, 5, 6, 7), 8] => [{0}, (2, 3, 4, 5, 6, 7), {1}, 8] => [2, 3, 4, 0, 1, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 4, to: 5 }, Operation::Mov { pos: 2, n: 6, to: 1 }, &[2, 3, 4, 0, 1, 5, 6, 7, 8])] +fn transform_mov_mov_split_ol_less( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform(&a, &b, &input, expect); +} + +#[rstest] +// a split by b +// MOV: [|0, (1, 2, 3, 4, 5, 6, 7), 8] => [ ({1, 2, 3}, 4, 5, 6, 7), 0,|8] +// MOV: [(0, 1, 2, 3), 4, |5, 6, 7, 8] => [|{4}, (0, 1, 2, 3), {5, 6, 7}, 8] => [5, 6, 7, {4}, 0, 1, 2, 3, 8] +#[case(Operation::Mov { pos: 1, n: 7, to: 0 }, Operation::Mov { pos: 0, n: 4, to: 5 }, &[4, 5, 6, 7, 0, 1, 2, 3, 8])] +// MOV: [|0, (1, 2, 3), 4, 5, 6, 7, 8] => [({1}, 2, 3), 0,|4, 5, 6, 7, 8] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [{2}, (0, 1), {3}, 4, 5, 6, 7, 8] => [2, 3, 0, 1, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 3, to: 0 }, Operation::Mov { pos: 0, n: 2, to: 3 }, &[2, 3, 0, 1, 4, 5, 6, 7, 8])] +// MOV: [|0, 1, (2, 3, 4, 5, 6, 7), 8] => [({2, 3}, 4, 5, 6, 7), 0, 1,|8] => [4, 5, 6, 7, 0, 1, 2, 3, 8] +// MOV: [(0, 1, 2, 3), 4, |5, 6, 7, 8] => [|{4}, (0, 1, 2, 3), {5, 6, 7}, 8] => [4, 5, 6, 7, 0, 1, 2, 3, 8] +#[case(Operation::Mov { pos: 2, n: 6, to: 0 }, Operation::Mov { pos: 0, n: 4, to: 5 }, &[4, 5, 6, 7, 0, 1, 2, 3, 8])] +// MOV: [|0, 1, (2, 3, 4), 5, 6, 7, 8] => [({2}, 3, 4), 0, 1,|5, 6, 7, 8] +// MOV: [0, (1, 2), 3, |4, 5, 6, 7, 8] => [|0, {3}, (1, 2), {4}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 3, to: 0 }, Operation::Mov { pos: 1, n: 2, to: 4 }, &[3, 4, 0, 1, 2, 5, 6, 7, 8])] +// MOV: [0, (1, 2, 3), 4, |5, 6, 7, 8] => [0,|4, ({1}, 2, 3), 5, 6, 7, 8] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [{2}, (0, 1), {3}, 4,|5, 6, 7, 8] => [{2}, 0, 1, 4,|(3), 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 3, to: 5 }, Operation::Mov { pos: 0, n: 2, to: 3 }, &[0, 1, 4, 2, 3, 5, 6, 7, 8])] +// MOV: [0, |1, (2, 3, 4), 5, 6, 7, 8] => [0, ({2}, 3, 4), 1,|5, 6, 7, 8] wi +// MOV: [(0, 1, 2), 3, |4, 5, 6, 7, 8] => [{3}, (0,|1, 2), {4}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 3, to: 1 }, Operation::Mov { pos: 0, n: 3, to: 4 }, &[0, 3, 4, 1, 2, 5, 6, 7, 8])] +// b split by a, R +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [0, ({2}, 3), 1,|4, 5, 6, 7, 8] +// MOV: [(0, 1, 2), 3, |4, 5, 6, 7, 8] => [{3}, (0,|1, 2), 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 2, to: 1 }, Operation::Mov { pos: 0, n: 3, to: 4 }, &[0, 3, 1, 2, 4, 5, 6, 7, 8])] +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [{0, ({2}, 3), 1}, 4,|5, 6, 7, 8] => [{0, 3, 1}, 4,|2, 5, 6, 7, 8] +// MOV: [(0, 1, 2), 3, 4, |5, 6, 7, 8] => [{3}, 4, (0,|1, 2), 5, 6, 7, 8] => [4, 0, (3), 1, 2, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 2, to: 1 }, Operation::Mov { pos: 0, n: 3, to: 5 }, &[4, 0, 3, 1, 2, 5, 6, 7, 8])] +// b split by a, L +// MOV: [0, 1, |2, (3, 4), 5, 6, 7, 8] => [|0, {1, (3, 4), 2}, 5, 6, 7, 8] => [1, {3}, 4, 2,|0, 5, 6, 7, 8] +// MOV: [|0, (1, 2, 3), 4, 5, 6, 7, 8] => [(1,|2, 3), 0, {4}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 3, n: 2, to: 2 }, Operation::Mov { pos: 1, n: 3, to: 0 }, &[1, 4, 2, 3, 0, 5, 6, 7 ,8])] +fn transform_mov_mov_split_ol_greater( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform(&a, &b, &input, expect); +} + +#[rstest] +// MOV: [0, 1, (2, 3, 4), 5, |6, 7, 8] => [0, 1, 5, (2, 3, 4), 6, 7, 8] +// MOV: [(0, 1, 2, 3), 4, |5, 6, 7, 8] => [4, (0, 1, 2, 3), 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 3, to: 6 }, Operation::Mov { pos: 0, n: 4, to: 5 }, &[0, 1, 2, 3, 5, 4, 6, 7, 8])] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2), 0, 3, 4, 5, 6, 7, 8] +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [2, 3, (0, 1), 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Mov { pos: 0, n: 2, to: 4 }, &[3, 2, 0, 1, 4, 5, 6, 7, 8])] +// MOV: [0, 1, (2, 3), 4, |5, 6, 7, 8] => [0, 1, 4, (2, 3), 5, 6, 7, 8] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2), 0, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 2, to: 5 }, Operation::Mov { pos: 1, n: 2, to: 0 }, &[1, 2, 0, 4, 3, 5, 6, 7, 8])] +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [0, (2, 3), 1, 4, 5, 6, 7, 8] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2), 0, 3, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 2, to: 1 }, Operation::Mov { pos: 1, n: 2, to: 0 }, &[3, 1, 2, 0, 4, 5, 6, 7, 8])] +// same destination +// MOV: [0, (1, 2), 3, |4, 5, 6, 7, 8] => [{0}, 3,|(1, 2), 4, 5, 6, 7, 8] +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [{2}, 3, (0, 1),|4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 4 }, Operation::Mov { pos: 0, n: 2, to: 4 }, &[3, 0, 1, 2, 4, 5, 6, 7, 8])] +// MOV: [0, (1, 2, 3), 4, |5, 6, 7, 8] => [{0}, 4,|(1, 2, 3), 5, 6, 7, 8] +// MOV: [(0, 1), 2, 3, 4, |5, 6, 7, 8] => [{2, 3}, 4, (0, 1),|5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 3, to: 5 }, Operation::Mov { pos: 0, n: 2, to: 5 }, &[4, 0, 1, 2, 3, 5, 6, 7, 8])] +// MOV: [|0, 1, (2, 3), 4, 5, 6, 7, 8] => [|(2, 3),0, {1}, 4, 5, 6, 7, 8] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2),|0, {3}, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 2, to: 0 }, Operation::Mov { pos: 1, n: 2, to: 0 }, &[1, 2, 3, 0, 4, 5, 6, 7, 8])] +// MOV: [|0, 1, (2, 3, 4), 5, 6, 7, 8] => [|(2, 3, 4), 0, {1}, 5, 6, 7, 8] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2),|0, {3, 4}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 3, to: 0 }, Operation::Mov { pos: 1, n: 2, to: 0 }, &[1, 2, 3, 4, 0, 5, 6, 7, 8])] +// MOV: [0, |1, 2, (3, 4), 5, 6, 7, 8] => [0,|(3, 4), 1, {2}, 5, 6, 7, 8] +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [0, (2, 3),|1, {4}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 3, n: 2, to: 1 }, Operation::Mov { pos: 2, n: 2, to: 1 }, &[0, 2, 3, 4, 1, 5, 6, 7, 8])] +fn transform_mov_mov_ol_greater( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform(&a, &b, &input, expect); +} + +#[rstest] +// MOV: [(0, 1, 2, 3), 4, |5, 6, 7, 8] => [|4, (0, 1, {2, 3}),*5, 6, 7, 8] => [|(2, 3), 4, 0, 1, {5}, 6, 7, 8] => [5, 2, 3, 4, 0, 1, 6, 7, 8] +// MOV: [0, 1, (2, 3, 4), 5, |6, 7, 8] => [{0, 1}, 5, (2, 3, 4),|6, 7, 8] => [5, 2, 3, 4, (0, 1), 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 4, to: 5 }, Operation::Mov { pos: 2, n: 3, to: 6 }, &[5, 2, 3, 4, 0, 1, 6, 7, 8])] +// MOV: [(0, 1, 2), 3, 4, 5, |6, 7, 8] => [{3},4, 5, (0, 1, {2}),*6,|7, 8] => [4, 5, 0, 1, 6, (2, 3), 7, 8] +// MOV: [0, 1, (2, 3), 4, 5, 6, |7, 8] => [{0, 1}, 4, 5,|6, (2, 3),*7, 8] => [4, 5, 0, 1, 6, 2, 3, 7, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 6 }, Operation::Mov { pos: 2, n: 2, to: 7 }, &[4, 5, 0, 1, 6, 2, 3, 7, 8])] +// MOV: [(0, 1, 2), 3, 4, 5, |6, 7, 8] => [3, 4, 5, (0, 1, 2), 6, 7, 8] +// MOV: [0, (1, 2, 3), 4, |5, 6, 7, 8] => [{0}, 4, (1, 2, 3), 5,|6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 6 }, Operation::Mov { pos: 1, n: 3, to: 5 }, &[4, 1, 2, 3, 5, 0, 6, 7, 8])] +// MOV: [(0, 1, 2), 3, 4, 5, |6, 7, 8] => [3, 4, 5, (0, 1, 2), 6, 7, 8] +// MOV: [|0, (1, 2, 3, 4), 5, 6, 7, 8] => [(1, 2, 3, 4), 0, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 6 }, Operation::Mov { pos: 1, n: 4, to: 0 }, &[1, 2, 3, 4, 5, 0, 6, 7, 8])] +// same destination +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2),|0, {3, 4}, 5, 6, 7, 8] +// MOV: [|0, 1, (2, 3, 4), 5, 6, 7, 8] => [|(2, 3, 4), 0, {1}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Mov { pos: 2, n: 3, to: 0 }, &[1, 2, 3, 4, 0, 5, 6, 7, 8])] +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1, 2),|0, {3}, 4, 5, 6, 7, 8] +// MOV: [|0, 1, (2, 3), 4, 5, 6, 7, 8] => [|(2, 3), 0, {1}, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Mov { pos: 2, n: 2, to: 0 }, &[1, 2, 3, 0, 4, 5, 6, 7, 8])] +// MOV: [(0, 1, 2), 3, 4, 5, |6, 7, 8] => [3, 4, 5, (0, 1, 2), 6, 7, 8] +// MOV: [0, (1, 2, 3), 4, 5, |6, 7, 8] => [{0}, 4, 5, (1, 2, 3),|6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 6 }, Operation::Mov { pos: 1, n: 3, to: 6 }, &[4, 5, 0, 1, 2, 3, 6, 7, 8])] +fn transform_mov_mov_ol_less( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform(&a, &b, &input, expect); +} + +#[rstest] +// info: 10 test items +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8, 9] => [{2, 3, (0, 1), 4}, 5,|6, 7, 8, 9] => [5,|2, 3, 0, {1}, 4, 6, 7, 8, 9] +// MOV: [0, (1, 2, 3, 4), 5, |6, 7, 8, 9] => [{0}, 5, (1, 2, 3,|4), 6, 7, 8, 9] => [5, 1, 2, 3, (0), 4, 6, 7, 8, 9] +#[case(Operation::Mov { pos: 0, n: 2, to: 4 }, Operation::Mov { pos: 1, n: 4, to: 6 }, &[5, 1, 2, 3, 0, 4, 6, 7, 8, 9])] +fn transform_mov_mov_tmp( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + test_transform(&a, &b, &input, expect); +} + +#[rstest] +// a_split_by_b, to right +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [2, (0,|1), {3, 4}, 5, 6, 7, 8] +// MOV: [0, |1, 2, (3, 4), 5, 6, 7, 8] => [{0, (3, 4), 1}, 2,|5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 3, n: 2, to: 1 }, &[2, 0, 3, 4, 1, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [2, (0,|1), {3, 4, 5}, 6, 7, 8] +// MOV: [0, |1, 2, (3, 4, 5), 6, 7, 8] => [{0, (3, 4, 5), 1}, 2,|6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 3, n: 3, to: 1 }, &[2, 0, 3, 4, 5, 1, 6, 7, 8])] +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [{2, 3}, (0,|1), 4, 5, 6, 7, 8] +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [{0, (2, 3), 1}, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 4 }, Operation::Mov { pos: 2, n: 2, to: 1 }, &[0, 2, 3, 1, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [2, 3, (0,|1), {4, 5}, 6, 7, 8] end >= m2 +// MOV: [0, |1, 2, 3, (4, 5), 6, 7, 8] => [{0, (4, 5), 1}, 2, 3,|6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 4 }, Operation::Mov { pos: 4, n: 2, to: 1 }, &[2, 3, 0, 4, 5, 1, 6, 7, 8])] +// MOV: [(0, 1), 2, 3, 4, |5, 6, 7, 8] => [{2, 3}, 4, (0,|1), 5, 6, 7, 8] +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [{0, (2, 3), 1}, 4,|5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 5 }, Operation::Mov { pos: 2, n: 2, to: 1 }, &[4, 0, 2, 3, 1, 5, 6, 7, 8])] +// a_split_by_b, to left +// MOV: [|0, (1, 2), 3, 4, 5, 6, 7, 8] => [(1,|2), 0, {3, 4}, 5, 6, 7, 8] +// MOV: [0, 1, |2, (3, 4), 5, 6, 7, 8] => [|0, {1, (3, 4), 2}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 0 }, Operation::Mov { pos: 3, n: 2, to: 2 }, &[1, 3, 4, 2, 0, 5, 6, 7, 8])] +// b_split_by_a, m2_to_right +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [{2, (0, 1), 3}, 4,|5, 6, 7, 8] +// MOV: [0, 1, (2, 3), 4, |5, 6, 7, 8] => [{0, 1}, 4, (2,|3), 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 2, n: 2, to: 5 }, &[4, 2, 0, 1, 3, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [2, {3, (0, 1), 4}, 5,|6, 7, 8] +// MOV: [0, 1, 2, (3, 4), 5, |6, 7, 8] => [{0, 1}, 2, 5, (3,|4), 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 4 }, Operation::Mov { pos: 3, n: 2, to: 6 }, &[2, 5, 3, 0, 1, 4, 6, 7, 8])] +// b_split_by_a, m2_to_left +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [2, (0, 1), 3, 4, 5, 6, 7, 8] +// MOV: [|0, 1, (2, 3), 4, 5, 6, 7, 8] => [(2, 3), 0, 1, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 2, n: 2, to: 0 }, &[2, 0, 1, 3, 4, 5, 6, 7, 8])] +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [|2, {3, (0, 1), 4}, 5, 6, 7, 8] +// MOV: [0, 1, |2, (3, 4), 5, 6, 7, 8] => [{0, 1}, (3,|4), 2, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 4 }, Operation::Mov { pos: 3, n: 2, to: 2 }, &[3, 0, 1, 4, 2, 5, 6, 7, 8])] +// both split +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [{2}, (0,|1), {3}, 4, 5, 6, 7, 8] +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [0, (2, 3), 1, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 0, n: 2, to: 3 }, Operation::Mov { pos: 2, n: 2, to: 1 }, &[0, 2, 3, 1, 4, 5, 6, 7, 8])] +// MOV: [(0, 1, 2, 3), 4, |5, 6, 7, 8] => [{4}, (0,|1, 2, 3), {5, 6, 7}, 8] +// MOV: [0, |1, 2, 3, (4, 5, 6, 7), 8] => [0, (4, 5, 6, 7), 1, 2, 3, 8] +#[case(Operation::Mov { pos: 0, n: 4, to: 5 }, Operation::Mov { pos: 4, n: 4, to: 1 }, &[0, 4, 5, 6, 7, 1, 2, 3, 8])] +// MOV: [(0, 1, 2), 3, 4, 5, |6, 7, 8] => [3, {4, 5}, (0,|1, 2), {6, 7}, 8] +// MOV: [0, |1, 2, 3, (4, 5, 6, 7), 8] => [|0, (4, 5, 6, 7), 1, 2, {3}, 8] +#[case(Operation::Mov { pos: 0, n: 3, to: 6 }, Operation::Mov { pos: 4, n: 4, to: 1 }, &[3, 0, 4, 5, 6, 7, 1, 2, 8])] +fn transform_mov_mov_split_less( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform(&a, &b, &input, expect); +} + +#[rstest] +// a_split_by_b, to right +// MOV: [0, 1, 2, 3, (4, 5), 6, |7, 8] => [{0, 1, 2, 3}, 6, (4,|5), 7, 8] +// MOV: [(0, 1, 2, 3), 4, |5, 6, 7, 8] => [{4, (0, 1, 2, 3), 5}, 6,|7, 8] +#[case(Operation::Mov { pos: 4, n: 2, to: 7 }, Operation::Mov { pos: 0, n: 4, to: 5 }, &[6, 4, 0, 1, 2, 3, 5, 7, 8])] +// MOV: [0, 1, 2, 3, (4, 5), 6, |7, 8] => [{0, 1, 2}, 3, 6, (4,|5), 7, 8] +// MOV: [(0, 1, 2), 3, 4, |5, 6, 7, 8] => [3, {4, (0, 1, 2),*5}, 6,|7, 8] +#[case(Operation::Mov { pos: 4, n: 2, to: 7 }, Operation::Mov { pos: 0, n: 3, to: 5 }, &[3, 6, 4, 0, 1, 2, 5, 7, 8])] +// MOV: [0, 1, (2, 3, 4), 5, |6, 7, 8] => [{0}, 1, 5, (2,|3, 4), 6, 7, 8] +// MOV: [(0),1, 2,|3, 4, 5, 6, 7, 8] => [1, {2, (0),*3, 4}, 5,|6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 3, to: 6 }, Operation::Mov { pos: 0, n: 1, to: 3 }, &[1, 5, 2, 0, 3, 4, 6, 7, 8])] +// a_split_by_b, to left +// MOV: [0, 1, |2, (3, 4), 5, 6, 7, 8] => [{0, 1}, (3,|4), 2, 5, 6, 7, 8] m_to -m2_n +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [|2, {3, (0, 1), 4}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 3, n: 2, to: 2 }, Operation::Mov { pos: 0, n: 2, to: 4 }, &[3, 0, 1, 4, 2, 5, 6, 7, 8])] +// MOV: [|0, 1, 2, 3, (4, 5, 6, 7), 8] => [(4,|5, 6, 7), {0, 1, 2, 3}, 8] +// MOV: [(0, 1, 2, 3), 4, |5, 6, 7, 8] => [{4, (0, 1, 2, 3), 5, 6, 7}, 8] +#[case(Operation::Mov { pos: 4, n: 4, to: 0 }, Operation::Mov { pos: 0, n: 4, to: 5 }, &[4, 0, 1, 2, 3, 5, 6, 7, 8])] +// MOV: [|0, 1, 2, (3, 4), 5, 6, 7, 8] => [(3,|4), {0, 1}, 2, 5, 6, 7, 8] +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [|2, {3, (0, 1), 4}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 3, n: 2, to: 0 }, Operation::Mov { pos: 0, n: 2, to: 4 }, &[3, 0, 1, 4, 2, 5, 6, 7, 8])] +// MOV: [0, 1, 2, |3, (4, 5), 6, 7, 8] => [{0, 1}, 2, (4,|5), 3, 6, 7, 8] +// MOV: [(0, 1), 2, 3, 4, |5, 6, 7, 8] => [2,|3, {4, (0, 1), 5}, 6, 7, 8] +#[case(Operation::Mov { pos: 4, n: 2, to: 3 }, Operation::Mov { pos: 0, n: 2, to: 5 }, &[2, 4, 0, 1, 5, 3, 6, 7, 8])] +// b_split_by_a, to right +// MOV: [0, |1, 2, (3, 4), 5, 6, 7, 8] => [{0, (3, 4), 1}, 2,|5, 6, 7, 8] +// MOV: [(0, 1), 2, |3, 4, 5, 6, 7, 8] => [2, (0,|1), {3, 4}, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 3, n: 2, to: 1 }, Operation::Mov { pos: 0, n: 2, to: 3 }, &[2, 0, 3, 4, 1, 5, 6, 7, 8])] +// MOV: [0, |1, (2, 3), 4, 5, 6, 7, 8] => [{0, (2, 3), 1}, 4, 5, 6, 7, 8] +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [{2, 3}, (0,|1), 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 2, n: 2, to: 1 }, Operation::Mov { pos: 0, n: 2, to: 4 }, &[0, 2, 3, 1, 4, 5, 6, 7, 8])] +// both split +// MOV: [0, |1, 2, (3, 4), 5, 6, 7, 8] => [{0}, (3,|4), {1}, 2, 5, 6, 7, 8] +// MOV: [(0, 1), 2, 3, |4, 5, 6, 7, 8] => [{2}, 3, (0, 1), 4,|5, 6, 7, 8] +#[case(Operation::Mov { pos: 3, n: 2, to: 1 }, Operation::Mov { pos: 0, n: 2, to: 4 }, &[3, 0, 1, 4, 2, 5, 6, 7, 8])] +fn transform_mov_mov_split_greater( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + test_transform(&a, &b, &input, expect); +} diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 60e8858..7cf85ae 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,29 +1,40 @@ #![allow(dead_code)] -use std::{fmt::Debug, sync::atomic::AtomicUsize}; +use std::{ + fmt::{Debug, Display}, + sync::atomic::AtomicUsize, +}; use otvec::{transform, Operation}; -pub fn test_transform( +pub fn test_transform( a: &Operation, b: &Operation, input: &[T], expect: &[T], ) { + // Print operations + println!("A/B:"); + a.print_on(input); + b.print_on(input); + let (mut a2, mut b2) = (a.clone(), b.clone()); transform(&mut a2, &mut b2); dbg!(&a2, &b2); - check_op(&a2, a); - check_op(&b2, b); + check_op(&a2, a, b); + check_op(&b2, a, b); let mut data = input.to_vec(); a.clone().apply(&mut data); b2.apply(&mut data); - assert_eq!(data, expect, "a, b2"); - let mut data = input.to_vec(); - b.clone().apply(&mut data); - a2.apply(&mut data); - assert_eq!(data, expect, "b, a2"); + let mut data2 = input.to_vec(); + b.clone().apply(&mut data2); + a2.apply(&mut data2); + + assert_eq!(data, data2, "OT condition unfulfilled"); + + assert_eq!(data, expect, "a, b2"); + assert_eq!(data2, expect, "b, a2"); } #[derive(Default)] @@ -32,14 +43,20 @@ pub struct CondCatchCtr { ok: AtomicUsize, } -pub fn test_transform_cond_catch( +pub fn test_transform_cond_catch< + T: PartialEq + Clone + Debug + Display + std::panic::RefUnwindSafe, +>( a: &Operation, b: &Operation, input: &[T], ctr: Option<&CondCatchCtr>, ) { - if let Some(ctr) = ctr { - ctr.total.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let do_count = !a.is_empty() && !b.is_empty(); + + if do_count { + if let Some(ctr) = ctr { + ctr.total.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + } } let res = std::panic::catch_unwind(|| { @@ -49,8 +66,8 @@ pub fn test_transform_cond_catch( +pub fn test_transform_sym( a: &Operation, b: &Operation, input: &[T], @@ -95,8 +131,8 @@ pub fn test_transform_cond( let (mut a2, mut b2) = (a.clone(), b.clone()); transform(&mut a2, &mut b2); // dbg!(&a2, &b2); - check_op(&a2, a); - check_op(&b2, b); + check_op(&a2, a, b); + check_op(&b2, a, b); let mut data = input.to_vec(); a.clone().apply(&mut data); @@ -108,14 +144,16 @@ pub fn test_transform_cond( assert_eq!(data, data2, "ops:\nA: {a:?}\nB: {b:?}"); } -fn check_op(op: &Operation, a: &Operation) { +fn check_op(op: &Operation, a: &Operation, b: &Operation) { match op { Operation::Nop => {} - Operation::Ins { val, .. } => assert!(val.len() > 0, "empty op: {op:?}\nfrom: {a:?}"), - Operation::Del { n, .. } => assert!(*n > 0, "empty op: {op:?}\nfrom: {a:?}"), - Operation::Mov { pos, n, to } => { - assert!(pos != to && *n > 0, "empty op: {op:?}\nfrom: {a:?}") + Operation::Ins { val, .. } => { + assert!(val.len() > 0, "empty op: {op:?}\nfrom: {a:?}; {b:?}") } - Operation::Seq { ops } => ops.iter().for_each(|op| check_op(op, a)), + Operation::Del { n, .. } => assert!(*n > 0, "empty op: {op:?}\nfrom: {a:?}; {b:?}"), + Operation::Mov { pos, n, to } => { + assert!(pos != to && *n > 0, "empty op: {op:?}\nfrom: {a:?}; {b:?}") + } + Operation::Seq { ops } => ops.iter().for_each(|op| check_op(op, a, b)), } }