From ce071e363c686cca55913bbaf355537c8eef697f Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Wed, 4 Sep 2024 01:13:05 +0200 Subject: [PATCH 01/13] feat: MOV-DEL working --- src/lib.rs | 248 ++++++++++++++++++++++++++++++++++++++------- tests/proptest.rs | 113 +++++++++++---------- tests/tests_mov.rs | 163 ++++++++++++++++++++++++++--- tests/util/mod.rs | 37 ++++--- 4 files changed, 443 insertions(+), 118 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a60538a..12c98e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -284,6 +284,20 @@ impl Operation { } fn sanitize(&mut self) { + 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 self.is_empty() { *self = Operation::Nop; return; @@ -292,11 +306,11 @@ impl Operation { // 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. /// @@ -447,6 +461,7 @@ impl Operation { }; } } + */ } impl Debug for Operation { @@ -669,12 +684,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 +777,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 +817,125 @@ 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 => {} } + a.sanitize(); } + #[allow(unused_variables)] Operation::Mov { pos: b_pos, n: b_n, to: b_to, } => { - unimplemented!("mov-mov"); + todo!("MOV-MOV"); } Operation::Seq { ops } => transform_seq(ops, a, false), Operation::Nop => {} @@ -773,6 +943,15 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } } +fn splice_or_append(v1: &mut Vec, pos: usize, mut v2: Vec) { + if pos < v1.len() { + v1.splice(pos..pos, v2); + } else { + v1.append(&mut v2); + } +} + +/* fn mov2seq(pos: usize, n: usize, to: usize) -> Operation { Operation::Seq { ops: if to < pos { @@ -789,15 +968,9 @@ fn mov2seq(pos: usize, n: usize, to: usize) -> Operation { }, } } +*/ -fn splice_or_append(v1: &mut Vec, pos: usize, mut v2: Vec) { - if pos < v1.len() { - v1.splice(pos..pos, v2); - } else { - v1.append(&mut v2); - } -} - +/* #[cfg(test)] mod tests { use crate::mov2seq; @@ -817,3 +990,4 @@ mod tests { assert_eq!(ib, &[0, 3, 4, 5, 1, 2, 6]); } } +*/ diff --git a/tests/proptest.rs b/tests/proptest.rs index 00af9c0..a4bd576 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -4,9 +4,11 @@ use proptest::prelude::*; use rayon::prelude::*; use otvec::Operation; -use util::{print_cond_catch_ctr, test_transform_cond, test_transform_cond_catch, CondCatchCtr}; +use util::test_transform_cond; const ALL_SIZE: usize = 20; +const MIN_RANGE_LEN: usize = 2; + const VEC_SIZE: usize = 100; fn testvec(len: usize) -> Vec { @@ -83,15 +85,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,8 +96,8 @@ fn t_mov_del( pos: b_pos, n: b_len, }; - test_transform_cond_catch(&a, &b, &input, ctr); - test_transform_cond_catch(&b, &a, &input, ctr); + test_transform_cond(&a, &b, &input); + test_transform_cond(&b, &a, &input); } fn t_mov_mov( @@ -133,92 +127,109 @@ 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)); + (MIN_RANGE_LEN..(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); + (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_iter().for_each(|a_pos| { + (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) + .into_iter() + .for_each(|a_len| { + (0..ALL_SIZE).into_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(|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)); + (0..ALL_SIZE).into_iter().for_each(|b_pos| { + (MIN_RANGE_LEN..(ALL_SIZE - b_pos)) + .into_iter() + .for_each(|b_len| { + t_mov_del(ALL_SIZE, a_pos, a_len, a_to, b_pos, b_len); + }); }); }); }); - }); }); - 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| { + (MIN_RANGE_LEN..(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| { + (MIN_RANGE_LEN..(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)); @@ -264,7 +275,6 @@ proptest! { t_mov_ins(VEC_SIZE, a_pos, a_len, a_to, b_pos, b_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 @@ -272,9 +282,8 @@ proptest! { 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) + t_mov_del(VEC_SIZE, a_pos, a_len, a_to, b_pos, b_len) } - */ /* #[test] diff --git a/tests/tests_mov.rs b/tests/tests_mov.rs index c1817d5..3ba5887 100644 --- a/tests/tests_mov.rs +++ b/tests/tests_mov.rs @@ -9,34 +9,171 @@ use rstest::rstest; // 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); +} diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 60e8858..a0ddd43 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,9 +1,12 @@ #![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], @@ -12,8 +15,8 @@ pub fn test_transform( 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); @@ -49,8 +52,8 @@ pub fn test_transform_cond_catch( +pub fn test_transform_sym( a: &Operation, b: &Operation, input: &[T], @@ -95,8 +98,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 +111,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)), } } From ab43aa2dd6de0b8c137203fd68e47ba3e8242486 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sun, 8 Sep 2024 01:56:05 +0200 Subject: [PATCH 02/13] WIP --- src/lib.rs | 523 +++++++++++++++++++++++++++++++++++---------- tests/proptest.rs | 74 ++++--- tests/tests.rs | 12 ++ tests/tests_mov.rs | 389 ++++++++++++++++++++++++++++++++- tests/util/mod.rs | 61 ++++-- 5 files changed, 902 insertions(+), 157 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 12c98e9..3017d26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ use std::{ - fmt::{Debug, Display, Write}, + fmt::{Debug, Display}, ops::Range, }; @@ -22,9 +22,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 +37,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 +51,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 +65,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 +85,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 +151,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,138 +180,75 @@ 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 let Operation::Seq { ops } = self { ops.iter_mut().for_each(Operation::sanitize); @@ -300,13 +266,14 @@ impl Operation { } if self.is_empty() { *self = Operation::Nop; - return; } - if let Operation::Mov { pos, n, to } = self { - // Invalid move operation - if *to >= *pos && *to <= *pos + *n { - *self = Operation::Nop; - } + } + + fn into_vec(self) -> Vec { + match self { + Operation::Nop => vec![], + Operation::Seq { ops } => ops, + _ => vec![self], } } @@ -931,11 +898,345 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } #[allow(unused_variables)] Operation::Mov { - pos: b_pos, - n: b_n, - to: b_to, + pos: m2_pos, + n: m2_n, + to: m2_to, } => { - todo!("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); + + 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 mop1_to = if *m_pos > m_to_start { + *m_pos + *m_n + } else { + *m_pos + }; + let ops = vec![ + Operation::Mov { + pos: m_to_start, + n: *m_n, + to: mop1_to, + }, + 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 *m2_to > ra.start && *m2_to < ra.end { + // 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; + } + + let ia = Operation::<()>::Ins { + pos: *m_to, + val: vec![(); *m_n], + }; + let da = Operation::<()>::Del { + pos: if m_to_right { *m_pos } else { *m_pos + *m_n }, + n: *m_n, + }; + let ib = Operation::<()>::Ins { + pos: *m2_to, + val: vec![(); *m2_n], + }; + let db = Operation::<()>::Del { + pos: if m2_to_right { + *m2_pos + } else { + *m2_pos + *m2_n + }, + n: *m2_n, + }; + + let mut opa = Operation::Seq { ops: vec![ia, da] }; + let mut opb = Operation::Seq { ops: vec![ib, db] }; + // dbg!(&opa, &opb); + transform_internal(&mut opa, &mut opb); + let opa = opa.into_vec(); + let opb = opb.into_vec(); + + if opa.len() != 2 || opb.len() != 2 { + panic!("unexpected op len"); + } + + // dbg!(&opa, &opb); + + let (ia2, da2) = (&opa[0], &opa[1]); + let (ib2, db2) = (&opb[0], &opb[1]); + + match (ia2, da2, ib2, db2) { + ( + Operation::Ins { pos: ia_pos, .. }, + Operation::Del { + pos: da_pos, + n: da_n, + }, + Operation::Ins { pos: ib_pos, .. }, + Operation::Del { + pos: db_pos, + n: db_n, + }, + ) => { + if da_n != m_n || db_n != m2_n { + todo!("truncated del, {im2:?}"); + /* + dbg!(&opa, &opb, &im2); + if m_to_right { + let mop1_n = *m2_n - *db_n; + let mop1_pos = rtarget.end - mop1_n; + + if mop1_pos == *db_pos { + todo!("mop1_pos == *db_pos") + } else if matches!(im2, IntersectionExt::LessOverlap) { + let a_ops = vec![ + Operation::Mov { + pos: mop1_pos, + n: mop1_n, + to: *m2_to, + }, + Operation::Mov { + pos: *db_pos, + n: *db_n, + to: *m2_to, + }, + ]; + *a = Operation::Nop; + *b = Operation::Seq { ops: a_ops }; + } else if matches!(im2, IntersectionExt::GreaterOverlap) { + todo!("greaterOverlap"); + } + } else { + todo!("min1 to left") + } + return; + */ + } + + *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::Ins { pos: ia_pos, .. }, + Operation::Del { + pos: da_pos, + n: da_n, + }, + Operation::Ins { pos: ib_pos, .. }, + Operation::Seq { ops: b_ops }, + ) => { + // dbg!(&opa, &opb, &im2); + // Range B split by A insertion + todo!( + "mov: invalid comb A:[{}/{}] B:[{}/{}]; {:?}", + ia2.opname(), + da2.opname(), + ib2.opname(), + db2.opname(), + im2 + ) + } + // A within B; B has priority, so A should be undone + ( + Operation::Ins { pos: ia_pos, .. }, + Operation::Nop, + Operation::Ins { pos: ib_pos, .. }, + Operation::Del { + pos: db_pos, + n: db_n, + }, + ) => unreachable!(), + // A over B + ( + Operation::Ins { .. }, + Operation::Del { .. }, + Operation::Ins { .. }, + Operation::Nop, + ) => unreachable!(), + // Same + ( + Operation::Ins { .. }, + Operation::Nop, + Operation::Ins { .. }, + Operation::Nop, + ) => unreachable!(), + _ => { + if matches!(im2, IntersectionExt::Over) { + assert!( + *m2_to > ra.start && *m2_to < ra.end, + "testassert; {m2_to}; {ra:?}" + ); + } + + todo!( + "mov: invalid comb A:[{}/{}] B:[{}/{}]; {:?}", + ia2.opname(), + da2.opname(), + ib2.opname(), + db2.opname(), + im2 + ) + } + } + + /* + 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 (im1t2, im2t1) = (ra.intersect_ext(&rtarget2), rm2.intersect_ext(&rtarget)); + + match im2 { + IntersectionExt::Less => { + if *m_to > rm2.start && *m_to <= rm2.end { + todo!("Less RM2 split by M1 ins") + } else if *m2_to > ra.start && *m2_to <= ra.end { + todo!("Less RM1 split by M2 ins") + } else { + /* + if !m2_to_right && *m2_to < ra.end { + m_pos_n += *m2_n; + }*/ + + let mut m_pos_n = *m_pos; + let mut m_to_n = *m_to; + let mut m2_to_n = *m2_to; + + if m_to_right && *m_to > rm2.start { + // in this case: m2_pos -= m_n + // todo!("Less moved beyond RM2, right") + *m2_pos -= *m_n; + } + if !m_to_right && *m_to < rm2.end && m_to < m2_to && m2_to <= m_pos { + // only for test case 3, 5 + m2_to_n += *m_n; + } + + if m_to_right && *m2_to > ra.start { + todo!("Less moved beyond RM1, right") + } + if !m2_to_right && *m2_to < ra.end { + m_pos_n += *m2_n; + if m_to_right || m2_to <= m_to { + m_to_n += *m2_n; + } + } + + *m_pos = m_pos_n; + *m_to = m_to_n; + *m2_to = m2_to_n; + } + } + IntersectionExt::Greater => todo!("Greater"), + IntersectionExt::LessOverlap => todo!("LessOverlap"), + IntersectionExt::GreaterOverlap => todo!("GreaterOverlap"), + IntersectionExt::Within => todo!("Within"), + IntersectionExt::Over => todo!("Over"), + IntersectionExt::Same => { + // 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; + } + IntersectionExt::Empty => {} + } + // a.sanitize(); + // b.sanitize(); + */ } Operation::Seq { ops } => transform_seq(ops, a, false), Operation::Nop => {} diff --git a/tests/proptest.rs b/tests/proptest.rs index a4bd576..49eea01 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -4,9 +4,9 @@ use proptest::prelude::*; use rayon::prelude::*; use otvec::Operation; -use util::test_transform_cond; +use util::{print_cond_catch_ctr, test_transform_cond, test_transform_cond_catch, CondCatchCtr}; -const ALL_SIZE: usize = 20; +const ALL_SIZE: usize = 9; const MIN_RANGE_LEN: usize = 2; const VEC_SIZE: usize = 100; @@ -108,6 +108,7 @@ fn t_mov_mov( b_pos: usize, b_len: usize, b_to: usize, + ctr: Option<&CondCatchCtr>, ) { let input = testvec(size); let a = Operation::Mov { @@ -120,8 +121,8 @@ fn t_mov_mov( n: b_len, to: b_to, }; - test_transform_cond(&a, &b, &input); - test_transform_cond(&b, &a, &input); + test_transform_cond_catch(&a, &b, &input, ctr); + test_transform_cond_catch(&b, &a, &input, ctr); } #[test] @@ -199,6 +200,29 @@ fn all_mov_ins() { #[test] fn all_mov_del() { + (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { + (MIN_RANGE_LEN..(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| { + (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); + }); + }); + }); + }); + }); +} + +#[test] +fn all_mov_mov() { + let ctr = CondCatchCtr::default(); (0..ALL_SIZE).into_iter().for_each(|a_pos| { (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) .into_iter() @@ -211,39 +235,29 @@ fn all_mov_del() { (MIN_RANGE_LEN..(ALL_SIZE - b_pos)) .into_iter() .for_each(|b_len| { - t_mov_del(ALL_SIZE, a_pos, a_len, a_to, b_pos, b_len); + (0..ALL_SIZE).into_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, + Some(&ctr), + ); + }); }); }); }); }); }); + print_cond_catch_ctr(&ctr); } -/* -#[test] -fn all_mov_mov() { - (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (MIN_RANGE_LEN..(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| { - (MIN_RANGE_LEN..(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); - }); - }); - }); - }); - }); - }); -} -*/ - 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) { diff --git a/tests/tests.rs b/tests/tests.rs index 0f1912f..195b556 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 { diff --git a/tests/tests_mov.rs b/tests/tests_mov.rs index 3ba5887..6c1b684 100644 --- a/tests/tests_mov.rs +++ b/tests/tests_mov.rs @@ -1,7 +1,7 @@ mod util; use otvec::Operation; -use util::test_transform_sym; +use util::{test_transform, test_transform_sym}; use rstest::rstest; @@ -177,3 +177,390 @@ fn transform_mov_del_over( 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); + // } + 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 }, &[0, 1, 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] => [{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 }, &[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] +// 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 }, &[])] +// 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, 3, 1, 2, 4, 5, 6, 7, 8] +#[case(Operation::Mov { pos: 1, n: 2, to: 4 }, Operation::Mov { pos: 0, n: 2, to: 3 }, &[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] +// 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_x( + #[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] +// 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])] +fn transform_mov_mov_split( + #[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); +} + +#[test] +fn tmp() { + // 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] + + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let expect = [3, 4, 2, 0, 1, 5, 6, 7, 8]; + + let a = Operation::::Mov { + pos: 0, + n: 2, + to: 3, + }; + let b = Operation::::Mov { + pos: 3, + n: 2, + to: 0, + }; + + let a2 = Operation::::Seq { + ops: vec![ + Operation::Del { pos: 0, n: 2 }, + Operation::Ins { + pos: 1, + val: vec![0, 1], + }, + ], + }; + let b2 = Operation::::Seq { + ops: vec![ + Operation::Del { pos: 3, n: 2 }, + Operation::Ins { + pos: 0, + val: vec![3, 4], + }, + ], + }; + /* + [tests/util/mod.rs:22:5] &a2 = Seq { + ops: [ + Del { + pos: 2, + n: 2, + }, + Ins { + pos: 3, + val: 2, + }, + ], + } + [tests/util/mod.rs:22:5] &b2 = Seq { + ops: [ + Del { + pos: 3, + n: 2, + }, + Ins { + pos: 0, + val: 2, + }, + ], + } + */ + + test_transform(&a, &b, &input, &expect); + test_transform(&a2, &b2, &input, &expect); +} + +#[test] +fn tmp2() { + // MOV: [(0, 1), 2, 3, 4, |5, 6, 7, 8] => [2, 3, 4, (0, 1), 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] => [(2, 3), 4, 0, 1, 5, 6, 7, 8-] + + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let expect = [2, 3, 4, 0, 1, 5, 6, 7, 8]; + + let a = Operation::::Mov { + pos: 0, + n: 2, + to: 5, + }; + let b = Operation::::Mov { + pos: 2, + n: 2, + to: 0, + }; + + let a2 = Operation::::Seq { + ops: vec![ + Operation::Del { pos: 0, n: 2 }, + Operation::Ins { + pos: 3, + val: vec![0, 1], + }, + ], + }; + let b2 = Operation::::Seq { + ops: vec![ + Operation::Del { pos: 2, n: 2 }, + Operation::Ins { + pos: 0, + val: vec![2, 3], + }, + ], + }; + /* + [tests/util/mod.rs:22:5] &a2 = Seq { + ops: [ + Del { + pos: 2, + n: 2, + }, + Ins { + pos: 3, + val: 2, + }, + ], + } + [tests/util/mod.rs:22:5] &b2 = Seq { + ops: [ + Del { + pos: 0, + n: 2, + }, + Ins { + pos: 0, + val: 2, + }, + ], + } + */ + + test_transform(&a, &b, &input, &expect); + test_transform(&a2, &b2, &input, &expect); +} + +#[test] +fn tmp3() { + // 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] => [{0}, 3, 4,|{1, 2}, 5, 6, 7, 8] + + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let expect = [3, 4, 0, 1, 2, 5, 6, 7, 8]; + // let expect = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + + let a = Operation::::Mov { + pos: 0, + n: 3, + to: 5, + }; + let b = Operation::::Mov { + pos: 3, + n: 2, + to: 1, + }; + + // let a2 = Operation::::Seq { + // ops: vec![ + // Operation::Del { pos: 0, n: 3 }, + // Operation::Ins { pos: 2, val: vec![0, 1, 2] }, + // ], + // }; + // let b2 = Operation::::Seq { + // ops: vec![ + // Operation::Del { pos: 3, n: 2 }, + // Operation::Ins { pos: 1, val: vec![3, 4] }, + // ], + // }; + let a2 = Operation::::Seq { + ops: vec![ + Operation::Ins { + pos: 5, + val: vec![0, 1, 2], + }, + Operation::Del { pos: 0, n: 3 }, + ], + }; + let b2 = Operation::::Seq { + ops: vec![ + Operation::Ins { + pos: 1, + val: vec![3, 4], + }, + Operation::Del { pos: 5, n: 2 }, + ], + }; + /* + [tests/util/mod.rs:22:5] &a2 = Seq { + ops: [ + Ins { + pos: 5, + val: 3, + }, + Seq { + ops: [ + Del { + pos: 3, + n: 2, + }, + Del { + pos: 0, + n: 1, + }, + ], + }, + ], + } + [tests/util/mod.rs:22:5] &b2 = Seq { + ops: [ + Ins { + pos: 0, + val: 2, + }, + Del { + pos: 2, + n: 2, + }, + ], + } + */ + + // test_transform(&a, &b, &input, &expect); + test_transform(&a2, &b2, &input, &expect); +} diff --git a/tests/util/mod.rs b/tests/util/mod.rs index a0ddd43..868733f 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -12,21 +12,29 @@ pub fn test_transform( 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, b); - check_op(&b2, a, 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)] @@ -35,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(|| { @@ -52,8 +66,8 @@ pub fn test_transform_cond_catch Date: Thu, 12 Sep 2024 04:02:41 +0200 Subject: [PATCH 03/13] WIP: mov-mov 83% done --- src/lib.rs | 378 ++++++++++++++++++++++++++++----------------- tests/proptest.rs | 2 + tests/tests_mov.rs | 293 ++++++++--------------------------- 3 files changed, 310 insertions(+), 363 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3017d26..83e128f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -896,7 +896,6 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } a.sanitize(); } - #[allow(unused_variables)] Operation::Mov { pos: m2_pos, n: m2_n, @@ -909,6 +908,9 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo 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; @@ -921,17 +923,8 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } if matches!(im2, IntersectionExt::Within) { // A within B; B has priority, so A should be undone - let mop1_to = if *m_pos > m_to_start { - *m_pos + *m_n - } else { - *m_pos - }; let ops = vec![ - Operation::Mov { - pos: m_to_start, - n: *m_n, - to: mop1_to, - }, + undo_mov(*m_pos, *m_n, m_to_start), Operation::Mov { pos: *m2_pos, n: *m2_n, @@ -948,7 +941,7 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo let a_before = *m_pos..*m2_pos; let a_after = rm2.end..ra.end; - if *m2_to > ra.start && *m2_to < ra.end { + if a_split_by_b { // Split *m2_pos = rtarget.start + m2_inside_offset; if m_to_right { @@ -1010,6 +1003,117 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo return; } + if matches!(im2, IntersectionExt::LessOverlap) { + match (a_split_by_b, b_split_by_a) { + (true, _) => { + // Range A is split by B insertion + // A: to_right, B: to_left + // A=>B2: move intersection of A/B to m_pos + let intersection = *m2_pos..ra.end; + let left_of_m2_to = *m_pos..*m2_to; + let a_delta = rtarget.start - *m_pos; + let b_delta = *m2_pos - rtarget2.start; + let nleft = *m2_pos - *m_pos; + *m2_pos += a_delta; + *m2_n = intersection.len(); + *m2_to = *m_pos; + + // 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); + 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 { + rtarget2.end + } else { + rtarget2.end - left_of_m2_to.len() + }, + n: nleft - left_of_m2_to.len(), + to: mto, + }, + ]; + *a = Operation::Seq { ops: a_ops }; + return; + } + (false, true) => { + // Range B is split by A insertion + // let m2_offset = *m2_pos - *m_pos; + let intersection = *m2_pos..ra.end; + + let mut m2_pos_n = *m2_pos; + let mut b_ops = None; + + // A=>B2 move intersection of A/B to m2_to + assert!(m_to_right); + let mdelta = rtarget.start - *m_pos; + m2_pos_n += 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: intersection.start + delta + 1, + 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) => {} + } + } + let ia = Operation::<()>::Ins { pos: *m_to, val: vec![(); *m_n], @@ -1061,38 +1165,44 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo }, ) => { if da_n != m_n || db_n != m2_n { - todo!("truncated del, {im2:?}"); - /* - dbg!(&opa, &opb, &im2); - if m_to_right { - let mop1_n = *m2_n - *db_n; - let mop1_pos = rtarget.end - mop1_n; - - if mop1_pos == *db_pos { - todo!("mop1_pos == *db_pos") - } else if matches!(im2, IntersectionExt::LessOverlap) { - let a_ops = vec![ - Operation::Mov { - pos: mop1_pos, - n: mop1_n, - to: *m2_to, - }, - Operation::Mov { - pos: *db_pos, - n: *db_n, - to: *m2_to, - }, - ]; - *a = Operation::Nop; - *b = Operation::Seq { ops: a_ops }; - } else if matches!(im2, IntersectionExt::GreaterOverlap) { - todo!("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 = if matches!(im2, IntersectionExt::LessOverlap) { + *m_pos } else { - todo!("min1 to left") + 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.saturating_sub(1)); + if *m_to > 0 { + m_to_n += 1; } + + // A=>B2: + // 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; - */ } *m_pos = if m_to_right { *da_pos } else { *da_pos - *m_n }; @@ -1105,6 +1215,7 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo }; *m2_to = *ib_pos; } + /* ( Operation::Ins { pos: ia_pos, .. }, Operation::Del { @@ -1116,6 +1227,10 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo ) => { // dbg!(&opa, &opb, &im2); // Range B split by A insertion + assert!( + *m_to > rm2.start && *m_to < rm2.end, + "testassert; {m_to}; {rm2:?}" + ); todo!( "mov: invalid comb A:[{}/{}] B:[{}/{}]; {:?}", ia2.opname(), @@ -1124,16 +1239,13 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo db2.opname(), im2 ) - } + }*/ // A within B; B has priority, so A should be undone ( - Operation::Ins { pos: ia_pos, .. }, + Operation::Ins { .. }, Operation::Nop, - Operation::Ins { pos: ib_pos, .. }, - Operation::Del { - pos: db_pos, - n: db_n, - }, + Operation::Ins { .. }, + Operation::Del { .. }, ) => unreachable!(), // A over B ( @@ -1150,13 +1262,6 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo Operation::Nop, ) => unreachable!(), _ => { - if matches!(im2, IntersectionExt::Over) { - assert!( - *m2_to > ra.start && *m2_to < ra.end, - "testassert; {m2_to}; {ra:?}" - ); - } - todo!( "mov: invalid comb A:[{}/{}] B:[{}/{}]; {:?}", ia2.opname(), @@ -1167,76 +1272,6 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo ) } } - - /* - 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 (im1t2, im2t1) = (ra.intersect_ext(&rtarget2), rm2.intersect_ext(&rtarget)); - - match im2 { - IntersectionExt::Less => { - if *m_to > rm2.start && *m_to <= rm2.end { - todo!("Less RM2 split by M1 ins") - } else if *m2_to > ra.start && *m2_to <= ra.end { - todo!("Less RM1 split by M2 ins") - } else { - /* - if !m2_to_right && *m2_to < ra.end { - m_pos_n += *m2_n; - }*/ - - let mut m_pos_n = *m_pos; - let mut m_to_n = *m_to; - let mut m2_to_n = *m2_to; - - if m_to_right && *m_to > rm2.start { - // in this case: m2_pos -= m_n - // todo!("Less moved beyond RM2, right") - *m2_pos -= *m_n; - } - if !m_to_right && *m_to < rm2.end && m_to < m2_to && m2_to <= m_pos { - // only for test case 3, 5 - m2_to_n += *m_n; - } - - if m_to_right && *m2_to > ra.start { - todo!("Less moved beyond RM1, right") - } - if !m2_to_right && *m2_to < ra.end { - m_pos_n += *m2_n; - if m_to_right || m2_to <= m_to { - m_to_n += *m2_n; - } - } - - *m_pos = m_pos_n; - *m_to = m_to_n; - *m2_to = m2_to_n; - } - } - IntersectionExt::Greater => todo!("Greater"), - IntersectionExt::LessOverlap => todo!("LessOverlap"), - IntersectionExt::GreaterOverlap => todo!("GreaterOverlap"), - IntersectionExt::Within => todo!("Within"), - IntersectionExt::Over => todo!("Over"), - IntersectionExt::Same => { - // 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; - } - IntersectionExt::Empty => {} - } - // a.sanitize(); - // b.sanitize(); - */ } Operation::Seq { ops } => transform_seq(ops, a, false), Operation::Nop => {} @@ -1252,6 +1287,35 @@ fn splice_or_append(v1: &mut Vec, pos: usize, mut v2: Vec) { } } +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 + } +} + /* fn mov2seq(pos: usize, n: usize, to: usize) -> Operation { Operation::Seq { @@ -1271,24 +1335,62 @@ fn mov2seq(pos: usize, n: usize, to: usize) -> Operation { } */ -/* #[cfg(test)] mod tests { - use crate::mov2seq; + 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_mov2seq() { - let input = [0, 1, 2, 3, 4, 5, 6]; - let a = mov2seq::(1, 3, 6); - let b = mov2seq::(3, 3, 1); + fn t_project_mov_point() { + let size = 9; + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - let mut ia = input.to_vec(); - a.apply(&mut ia); - assert_eq!(ia, &[0, 4, 5, 1, 2, 3, 6]); + (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 mut ib = input.to_vec(); - b.apply(&mut ib); - assert_eq!(ib, &[0, 3, 4, 5, 1, 2, 6]); + 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 49eea01..966b94b 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -226,6 +226,7 @@ fn all_mov_mov() { (0..ALL_SIZE).into_iter().for_each(|a_pos| { (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) .into_iter() + .rev() .for_each(|a_len| { (0..ALL_SIZE).into_iter().for_each(|a_to| { let a_to = filter_to(ALL_SIZE, a_pos, a_to); @@ -234,6 +235,7 @@ fn all_mov_mov() { (0..ALL_SIZE).into_iter().for_each(|b_pos| { (MIN_RANGE_LEN..(ALL_SIZE - b_pos)) .into_iter() + .rev() .for_each(|b_len| { (0..ALL_SIZE).into_iter().for_each(|b_to| { let b_to = filter_to(ALL_SIZE, b_pos, b_to); diff --git a/tests/tests_mov.rs b/tests/tests_mov.rs index 6c1b684..bfbb969 100644 --- a/tests/tests_mov.rs +++ b/tests/tests_mov.rs @@ -256,20 +256,20 @@ fn transform_mov_mov_less( #[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 }, &[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 }, &[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 }, &[])] +#[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, 3, 1, 2, 4, 5, 6, 7, 8] -#[case(Operation::Mov { pos: 1, n: 2, to: 4 }, Operation::Mov { pos: 0, n: 2, to: 3 }, &[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_x( +fn transform_mov_mov_truncated( #[case] a: Operation, #[case] b: Operation, #[case] expect: &[i32], @@ -328,9 +328,36 @@ fn transform_mov_mov_over( } #[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])] +// 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( #[case] a: Operation, #[case] b: Operation, @@ -340,227 +367,43 @@ fn transform_mov_mov_split( test_transform(&a, &b, &input, expect); } -#[test] -fn tmp() { - // 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] - +#[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, 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])] +// 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])] +// 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])] +fn transform_mov_mov_truncdel( + #[case] a: Operation, + #[case] b: Operation, + #[case] expect: &[i32], +) { let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - let expect = [3, 4, 2, 0, 1, 5, 6, 7, 8]; - - let a = Operation::::Mov { - pos: 0, - n: 2, - to: 3, - }; - let b = Operation::::Mov { - pos: 3, - n: 2, - to: 0, - }; - - let a2 = Operation::::Seq { - ops: vec![ - Operation::Del { pos: 0, n: 2 }, - Operation::Ins { - pos: 1, - val: vec![0, 1], - }, - ], - }; - let b2 = Operation::::Seq { - ops: vec![ - Operation::Del { pos: 3, n: 2 }, - Operation::Ins { - pos: 0, - val: vec![3, 4], - }, - ], - }; - /* - [tests/util/mod.rs:22:5] &a2 = Seq { - ops: [ - Del { - pos: 2, - n: 2, - }, - Ins { - pos: 3, - val: 2, - }, - ], - } - [tests/util/mod.rs:22:5] &b2 = Seq { - ops: [ - Del { - pos: 3, - n: 2, - }, - Ins { - pos: 0, - val: 2, - }, - ], - } - */ - - test_transform(&a, &b, &input, &expect); - test_transform(&a2, &b2, &input, &expect); + test_transform(&a, &b, &input, expect); } -#[test] -fn tmp2() { - // MOV: [(0, 1), 2, 3, 4, |5, 6, 7, 8] => [2, 3, 4, (0, 1), 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] => [(2, 3), 4, 0, 1, 5, 6, 7, 8-] - +#[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])] +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]; - let expect = [2, 3, 4, 0, 1, 5, 6, 7, 8]; - - let a = Operation::::Mov { - pos: 0, - n: 2, - to: 5, - }; - let b = Operation::::Mov { - pos: 2, - n: 2, - to: 0, - }; - - let a2 = Operation::::Seq { - ops: vec![ - Operation::Del { pos: 0, n: 2 }, - Operation::Ins { - pos: 3, - val: vec![0, 1], - }, - ], - }; - let b2 = Operation::::Seq { - ops: vec![ - Operation::Del { pos: 2, n: 2 }, - Operation::Ins { - pos: 0, - val: vec![2, 3], - }, - ], - }; - /* - [tests/util/mod.rs:22:5] &a2 = Seq { - ops: [ - Del { - pos: 2, - n: 2, - }, - Ins { - pos: 3, - val: 2, - }, - ], - } - [tests/util/mod.rs:22:5] &b2 = Seq { - ops: [ - Del { - pos: 0, - n: 2, - }, - Ins { - pos: 0, - val: 2, - }, - ], - } - */ - - test_transform(&a, &b, &input, &expect); - test_transform(&a2, &b2, &input, &expect); -} - -#[test] -fn tmp3() { - // 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] => [{0}, 3, 4,|{1, 2}, 5, 6, 7, 8] - - let input = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - let expect = [3, 4, 0, 1, 2, 5, 6, 7, 8]; - // let expect = [0, 1, 2, 3, 4, 5, 6, 7, 8]; - - let a = Operation::::Mov { - pos: 0, - n: 3, - to: 5, - }; - let b = Operation::::Mov { - pos: 3, - n: 2, - to: 1, - }; - - // let a2 = Operation::::Seq { - // ops: vec![ - // Operation::Del { pos: 0, n: 3 }, - // Operation::Ins { pos: 2, val: vec![0, 1, 2] }, - // ], - // }; - // let b2 = Operation::::Seq { - // ops: vec![ - // Operation::Del { pos: 3, n: 2 }, - // Operation::Ins { pos: 1, val: vec![3, 4] }, - // ], - // }; - let a2 = Operation::::Seq { - ops: vec![ - Operation::Ins { - pos: 5, - val: vec![0, 1, 2], - }, - Operation::Del { pos: 0, n: 3 }, - ], - }; - let b2 = Operation::::Seq { - ops: vec![ - Operation::Ins { - pos: 1, - val: vec![3, 4], - }, - Operation::Del { pos: 5, n: 2 }, - ], - }; - /* - [tests/util/mod.rs:22:5] &a2 = Seq { - ops: [ - Ins { - pos: 5, - val: 3, - }, - Seq { - ops: [ - Del { - pos: 3, - n: 2, - }, - Del { - pos: 0, - n: 1, - }, - ], - }, - ], - } - [tests/util/mod.rs:22:5] &b2 = Seq { - ops: [ - Ins { - pos: 0, - val: 2, - }, - Del { - pos: 2, - n: 2, - }, - ], - } - */ - - // test_transform(&a, &b, &input, &expect); - test_transform(&a2, &b2, &input, &expect); + test_transform(&a, &b, &input, expect); } From cc8c0f61f4fcda09998a8e9cfe9aa45ae5dd682b Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Thu, 12 Sep 2024 04:40:12 +0200 Subject: [PATCH 04/13] fix: mov-mov split --- src/lib.rs | 7 ++++++- tests/tests_mov.rs | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 83e128f..6207b80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1083,7 +1083,12 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo to: *m2_to, }, Operation::Mov { - pos: intersection.start + delta + 1, + pos: project_mov_point( + *m_pos, + *m_n, + *m_to, + intersection.start, + ) + delta, n: intersection.len(), to: *m_pos + delta, }, diff --git a/tests/tests_mov.rs b/tests/tests_mov.rs index bfbb969..a9362ba 100644 --- a/tests/tests_mov.rs +++ b/tests/tests_mov.rs @@ -396,14 +396,14 @@ fn transform_mov_mov_truncdel( } #[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, 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]; + let input = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; test_transform(&a, &b, &input, expect); } From 4967b219c1a7fb85c1f33c8a3cba6897b298b8bb Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 13 Sep 2024 00:35:33 +0200 Subject: [PATCH 05/13] WIP: mov-mov 86% done (GreaterOverlap) --- src/lib.rs | 150 ++++++++++++++++++++++++++++++++++++++++----- tests/proptest.rs | 4 +- tests/tests_mov.rs | 45 +++++++++++++- 3 files changed, 181 insertions(+), 18 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6207b80..32124d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1004,24 +1004,27 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } 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 - // A: to_right, B: to_left + // B: to_left // A=>B2: move intersection of A/B to m_pos - let intersection = *m2_pos..ra.end; let left_of_m2_to = *m_pos..*m2_to; - let a_delta = rtarget.start - *m_pos; let b_delta = *m2_pos - rtarget2.start; let nleft = *m2_pos - *m_pos; - *m2_pos += a_delta; - *m2_n = intersection.len(); - *m2_to = *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); + let wi = rm2.contains(m_to); // m_to within m2 source let mut mto = *m_to; if wi { mto -= b_delta; @@ -1033,30 +1036,32 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo to: mto, }, Operation::Mov { - pos: if wi { + pos: if wi || !m_to_right { rtarget2.end } else { rtarget2.end - left_of_m2_to.len() }, n: nleft - left_of_m2_to.len(), - to: mto, + 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 m2_offset = *m2_pos - *m_pos; - let intersection = *m2_pos..ra.end; - - let mut m2_pos_n = *m2_pos; let mut b_ops = None; // A=>B2 move intersection of A/B to m2_to - assert!(m_to_right); let mdelta = rtarget.start - *m_pos; - m2_pos_n += mdelta; + let m2_pos_n = intersection.start + mdelta; if m2_to < m_pos { let r_before = @@ -1119,6 +1124,121 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } } + 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) => {} + } + } + let ia = Operation::<()>::Ins { pos: *m_to, val: vec![(); *m_n], diff --git a/tests/proptest.rs b/tests/proptest.rs index 966b94b..1b66251 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -226,7 +226,7 @@ fn all_mov_mov() { (0..ALL_SIZE).into_iter().for_each(|a_pos| { (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) .into_iter() - .rev() + // .rev() .for_each(|a_len| { (0..ALL_SIZE).into_iter().for_each(|a_to| { let a_to = filter_to(ALL_SIZE, a_pos, a_to); @@ -235,7 +235,7 @@ fn all_mov_mov() { (0..ALL_SIZE).into_iter().for_each(|b_pos| { (MIN_RANGE_LEN..(ALL_SIZE - b_pos)) .into_iter() - .rev() + // .rev() .for_each(|b_len| { (0..ALL_SIZE).into_iter().for_each(|b_to| { let b_to = filter_to(ALL_SIZE, b_pos, b_to); diff --git a/tests/tests_mov.rs b/tests/tests_mov.rs index a9362ba..b2cbdc5 100644 --- a/tests/tests_mov.rs +++ b/tests/tests_mov.rs @@ -354,11 +354,54 @@ fn transform_mov_mov_over( // 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( +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 +// 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_greater( #[case] a: Operation, #[case] b: Operation, #[case] expect: &[i32], From 1c87e590eae7b2a23be802940265e5a289aa779b Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 13 Sep 2024 03:59:19 +0200 Subject: [PATCH 06/13] WIP: mov-mov 93% (Less) --- src/lib.rs | 133 +++++++++++++++++++++++++++++---------------- tests/proptest.rs | 4 +- tests/tests_mov.rs | 59 +++++++++++++++++++- 3 files changed, 146 insertions(+), 50 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 32124d9..2cab582 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1239,6 +1239,93 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } } + 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) => {} + } + } + let ia = Operation::<()>::Ins { pos: *m_to, val: vec![(); *m_n], @@ -1340,52 +1427,6 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo }; *m2_to = *ib_pos; } - /* - ( - Operation::Ins { pos: ia_pos, .. }, - Operation::Del { - pos: da_pos, - n: da_n, - }, - Operation::Ins { pos: ib_pos, .. }, - Operation::Seq { ops: b_ops }, - ) => { - // dbg!(&opa, &opb, &im2); - // Range B split by A insertion - assert!( - *m_to > rm2.start && *m_to < rm2.end, - "testassert; {m_to}; {rm2:?}" - ); - todo!( - "mov: invalid comb A:[{}/{}] B:[{}/{}]; {:?}", - ia2.opname(), - da2.opname(), - ib2.opname(), - db2.opname(), - im2 - ) - }*/ - // A within B; B has priority, so A should be undone - ( - Operation::Ins { .. }, - Operation::Nop, - Operation::Ins { .. }, - Operation::Del { .. }, - ) => unreachable!(), - // A over B - ( - Operation::Ins { .. }, - Operation::Del { .. }, - Operation::Ins { .. }, - Operation::Nop, - ) => unreachable!(), - // Same - ( - Operation::Ins { .. }, - Operation::Nop, - Operation::Ins { .. }, - Operation::Nop, - ) => unreachable!(), _ => { todo!( "mov: invalid comb A:[{}/{}] B:[{}/{}]; {:?}", diff --git a/tests/proptest.rs b/tests/proptest.rs index 1b66251..966b94b 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -226,7 +226,7 @@ fn all_mov_mov() { (0..ALL_SIZE).into_iter().for_each(|a_pos| { (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) .into_iter() - // .rev() + .rev() .for_each(|a_len| { (0..ALL_SIZE).into_iter().for_each(|a_to| { let a_to = filter_to(ALL_SIZE, a_pos, a_to); @@ -235,7 +235,7 @@ fn all_mov_mov() { (0..ALL_SIZE).into_iter().for_each(|b_pos| { (MIN_RANGE_LEN..(ALL_SIZE - b_pos)) .into_iter() - // .rev() + .rev() .for_each(|b_len| { (0..ALL_SIZE).into_iter().for_each(|b_to| { let b_to = filter_to(ALL_SIZE, b_pos, b_to); diff --git a/tests/tests_mov.rs b/tests/tests_mov.rs index b2cbdc5..a25e812 100644 --- a/tests/tests_mov.rs +++ b/tests/tests_mov.rs @@ -361,7 +361,7 @@ fn transform_mov_mov_over( // 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_less( +fn transform_mov_mov_split_ol_less( #[case] a: Operation, #[case] b: Operation, #[case] expect: &[i32], @@ -401,7 +401,7 @@ fn transform_mov_mov_split_less( // 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_greater( +fn transform_mov_mov_split_ol_greater( #[case] a: Operation, #[case] b: Operation, #[case] expect: &[i32], @@ -439,6 +439,7 @@ fn transform_mov_mov_truncdel( } #[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])] @@ -450,3 +451,57 @@ fn transform_mov_mov_tmp( 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); +} From c58d547be13c7b72844a631be06784c20934d8b1 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 13 Sep 2024 05:28:48 +0200 Subject: [PATCH 07/13] feat: mov-mov completed --- src/lib.rs | 94 ++++++++++++++++++++++++++++++++++++++++++++++ tests/proptest.rs | 34 ++++++++++++++--- tests/tests.rs | 2 +- tests/tests_mov.rs | 44 ++++++++++++++++++++++ 4 files changed, 167 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2cab582..738e104 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1326,6 +1326,100 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } } + 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; + 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 = *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 ia = Operation::<()>::Ins { pos: *m_to, val: vec![(); *m_n], diff --git a/tests/proptest.rs b/tests/proptest.rs index 966b94b..c5b6ec0 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -100,7 +100,7 @@ fn t_mov_del(size: usize, a_pos: usize, a_len: usize, a_to: usize, b_pos: usize, test_transform_cond(&b, &a, &input); } -fn t_mov_mov( +fn t_mov_mov_catch( size: usize, a_pos: usize, a_len: usize, @@ -125,6 +125,30 @@ fn t_mov_mov( test_transform_cond_catch(&b, &a, &input, ctr); } +fn t_mov_mov( + size: usize, + a_pos: usize, + a_len: usize, + a_to: usize, + b_pos: usize, + b_len: usize, + b_to: usize, +) { + 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(&a, &b, &input); + test_transform_cond(&b, &a, &input); +} + #[test] fn all_ins_ins() { (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { @@ -226,7 +250,7 @@ fn all_mov_mov() { (0..ALL_SIZE).into_iter().for_each(|a_pos| { (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) .into_iter() - .rev() + // .rev() .for_each(|a_len| { (0..ALL_SIZE).into_iter().for_each(|a_to| { let a_to = filter_to(ALL_SIZE, a_pos, a_to); @@ -235,13 +259,13 @@ fn all_mov_mov() { (0..ALL_SIZE).into_iter().for_each(|b_pos| { (MIN_RANGE_LEN..(ALL_SIZE - b_pos)) .into_iter() - .rev() + // .rev() .for_each(|b_len| { (0..ALL_SIZE).into_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( + t_mov_mov_catch( ALL_SIZE, a_pos, a_len, @@ -301,7 +325,6 @@ proptest! { t_mov_del(VEC_SIZE, a_pos, a_len, a_to, b_pos, b_len) } - /* #[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 @@ -312,5 +335,4 @@ proptest! { t_mov_mov(VEC_SIZE, a_pos, a_len, a_to, b_pos, b_len, b_to) } - */ } diff --git a/tests/tests.rs b/tests/tests.rs index 195b556..e205ed0 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -393,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 a25e812..fd7ada3 100644 --- a/tests/tests_mov.rs +++ b/tests/tests_mov.rs @@ -505,3 +505,47 @@ fn transform_mov_mov_split_less( 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); +} From ce47bb84a3ddf36f80af52af1613056178bdfe21 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Fri, 13 Sep 2024 20:47:52 +0200 Subject: [PATCH 08/13] test: improve tests --- src/lib.rs | 189 +-------------------------------------------- tests/proptest.rs | 153 ++++++++++++++++++++++-------------- tests/tests_mov.rs | 11 ++- tests/util/mod.rs | 8 +- 4 files changed, 106 insertions(+), 255 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 738e104..6db92dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -276,159 +276,6 @@ impl Operation { _ => vec![self], } } - - /* - /// 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 } - }; - } - } - */ } impl Debug for Operation { @@ -468,8 +315,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) { @@ -595,14 +442,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); @@ -894,7 +733,6 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } IntersectionExt::Empty => {} } - a.sanitize(); } Operation::Mov { pos: m2_pos, @@ -1443,7 +1281,7 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo let mut opa = Operation::Seq { ops: vec![ia, da] }; let mut opb = Operation::Seq { ops: vec![ib, db] }; - // dbg!(&opa, &opb); + transform_internal(&mut opa, &mut opb); let opa = opa.into_vec(); let opb = opb.into_vec(); @@ -1452,8 +1290,6 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo panic!("unexpected op len"); } - // dbg!(&opa, &opb); - let (ia2, da2) = (&opa[0], &opa[1]); let (ib2, db2) = (&opb[0], &opb[1]); @@ -1576,25 +1412,6 @@ fn project_mov_point(m_pos: usize, m_n: usize, m_to: usize, point: usize) -> usi } } -/* -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() - }, - } -} -*/ - #[cfg(test)] mod tests { use super::*; diff --git a/tests/proptest.rs b/tests/proptest.rs index c5b6ec0..67c693d 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -6,8 +6,11 @@ use rayon::prelude::*; use otvec::Operation; use util::{print_cond_catch_ctr, test_transform_cond, test_transform_cond_catch, CondCatchCtr}; -const ALL_SIZE: usize = 9; -const MIN_RANGE_LEN: usize = 2; +const ALL_SIZE: usize = 20; +const MIN_RANGE_LEN: usize = 1; + +const TEST_SIZE: usize = 9; +const TEST_MIN_RANGE_LEN: usize = 2; const VEC_SIZE: usize = 100; @@ -152,11 +155,11 @@ fn t_mov_mov( #[test] fn all_ins_ins() { (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) + (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)) + (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); @@ -169,11 +172,11 @@ fn all_ins_ins() { #[test] fn all_del_del() { (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) + (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)) + (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); @@ -186,11 +189,11 @@ fn all_del_del() { #[test] fn all_ins_del() { (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) + (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)) + (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); @@ -203,21 +206,21 @@ fn all_ins_del() { #[test] fn all_mov_ins() { (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) + (MIN_RANGE_LEN..=(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| { - (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); - }); + (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); + }); + }); }); - }); }); }); } @@ -225,65 +228,95 @@ fn all_mov_ins() { #[test] fn all_mov_del() { (0..ALL_SIZE).into_par_iter().for_each(|a_pos| { - (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) + (MIN_RANGE_LEN..=(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| { - (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); - }); + (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 all_mov_mov() { +fn dev_all_mov_mov() { let ctr = CondCatchCtr::default(); - (0..ALL_SIZE).into_iter().for_each(|a_pos| { - (MIN_RANGE_LEN..(ALL_SIZE - a_pos)) + (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..ALL_SIZE).into_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_iter().for_each(|b_pos| { - (MIN_RANGE_LEN..(ALL_SIZE - b_pos)) - .into_iter() - // .rev() - .for_each(|b_len| { - (0..ALL_SIZE).into_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_catch( - ALL_SIZE, - a_pos, - a_len, - a_to, - b_pos, - b_len, - b_to, - Some(&ctr), - ); + (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| { + (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, + ); + }); + }); + }); + }); + }); + }); +} + 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) { diff --git a/tests/tests_mov.rs b/tests/tests_mov.rs index fd7ada3..d076d39 100644 --- a/tests/tests_mov.rs +++ b/tests/tests_mov.rs @@ -245,12 +245,11 @@ fn transform_mov_mov_less( #[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); - // } - test_transform(&a, &b, &input, expect); + if sym { + test_transform_sym(&a, &b, &input, expect); + } else { + test_transform(&a, &b, &input, expect); + } } #[rstest] diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 868733f..7cf85ae 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -20,8 +20,8 @@ pub fn test_transform( let (mut a2, mut b2) = (a.clone(), b.clone()); transform(&mut a2, &mut b2); dbg!(&a2, &b2); - // check_op(&a2, a, b); - // check_op(&b2, a, b); + check_op(&a2, a, b); + check_op(&b2, a, b); let mut data = input.to_vec(); a.clone().apply(&mut data); @@ -107,7 +107,9 @@ pub fn print_cond_catch_ctr(ctr: &CondCatchCtr) { let perc = (success as f64 / total as f64) * 100.0; if success < total { - panic!("✅ Cases passed: {success}/{total} ({perc:0.2}%)"); + panic!("❌ Cases passed: {success}/{total} ({perc:0.2}%)"); + } else { + println!("✅ Cases passed: {success}/{total} ({perc:0.2}%)"); } } From e8cd0eda71fef56b34e557604f327b98fc67399d Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 14 Sep 2024 00:01:48 +0200 Subject: [PATCH 09/13] test: improve proptests --- tests/proptest.rs | 81 +++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/tests/proptest.rs b/tests/proptest.rs index 67c693d..0ff676d 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -18,18 +18,6 @@ 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 { @@ -317,55 +305,58 @@ fn all_mov_mov() { }); } +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) + 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); } } From 836b0f62165f56d38bb90473b2dbf56433978dc0 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sat, 14 Sep 2024 17:23:19 +0200 Subject: [PATCH 10/13] small refactor --- src/lib.rs | 201 ++++++++++++++++++++++----------------------- tests/tests_mov.rs | 6 ++ 2 files changed, 106 insertions(+), 101 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6db92dc..1e92b55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -268,14 +268,6 @@ impl Operation { *self = Operation::Nop; } } - - fn into_vec(self) -> Vec { - match self { - Operation::Nop => vec![], - Operation::Seq { ops } => ops, - _ => vec![self], - } - } } impl Debug for Operation { @@ -958,7 +950,46 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } return; } - (false, false) => {} + (false, false) => { + let intersection = ra.start.max(rm2.start)..ra.end.min(rm2.end); + let m_n_n = *m_n - intersection.len(); + let m_pos_pre = if matches!(im2, IntersectionExt::LessOverlap) { + *m_pos + } else { + 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.saturating_sub(1)); + if *m_to > 0 { + m_to_n += 1; + } + + // A=>B2: + // 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; + } } } @@ -1073,7 +1104,46 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } return; } - (false, false) => {} + (false, false) => { + let intersection = ra.start.max(rm2.start)..ra.end.min(rm2.end); + let m_n_n = *m_n - intersection.len(); + let m_pos_pre = if matches!(im2, IntersectionExt::LessOverlap) { + *m_pos + } else { + 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.saturating_sub(1)); + if *m_to > 0 { + m_to_n += 1; + } + + // A=>B2: + // 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; + } } } @@ -1258,19 +1328,19 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } } - let ia = Operation::<()>::Ins { + let mut ia = Operation::<()>::Ins { pos: *m_to, val: vec![(); *m_n], }; - let da = Operation::<()>::Del { + let mut da = Operation::<()>::Del { pos: if m_to_right { *m_pos } else { *m_pos + *m_n }, n: *m_n, }; - let ib = Operation::<()>::Ins { + let mut ib = Operation::<()>::Ins { pos: *m2_to, val: vec![(); *m2_n], }; - let db = Operation::<()>::Del { + let mut db = Operation::<()>::Del { pos: if m2_to_right { *m2_pos } else { @@ -1279,95 +1349,24 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo n: *m2_n, }; - let mut opa = Operation::Seq { ops: vec![ia, da] }; - let mut opb = Operation::Seq { ops: vec![ib, db] }; + transform_internal(&mut ia, &mut ib); + transform_internal(&mut da, &mut ib); + transform_internal(&mut ia, &mut db); + transform_internal(&mut da, &mut db); - transform_internal(&mut opa, &mut opb); - let opa = opa.into_vec(); - let opb = opb.into_vec(); + *m_pos = if m_to_right { + da.pos() + } else { + da.pos() - *m_n + }; + *m_to = ia.pos(); - if opa.len() != 2 || opb.len() != 2 { - panic!("unexpected op len"); - } - - let (ia2, da2) = (&opa[0], &opa[1]); - let (ib2, db2) = (&opb[0], &opb[1]); - - match (ia2, da2, ib2, db2) { - ( - Operation::Ins { pos: ia_pos, .. }, - Operation::Del { - pos: da_pos, - n: da_n, - }, - Operation::Ins { pos: ib_pos, .. }, - Operation::Del { - pos: db_pos, - n: db_n, - }, - ) => { - if da_n != m_n || db_n != m2_n { - let intersection = ra.start.max(rm2.start)..ra.end.min(rm2.end); - let m_n_n = *m_n - intersection.len(); - let m_pos_pre = if matches!(im2, IntersectionExt::LessOverlap) { - *m_pos - } else { - 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.saturating_sub(1)); - if *m_to > 0 { - m_to_n += 1; - } - - // A=>B2: - // 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; - } - - *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; - } - _ => { - todo!( - "mov: invalid comb A:[{}/{}] B:[{}/{}]; {:?}", - ia2.opname(), - da2.opname(), - ib2.opname(), - db2.opname(), - im2 - ) - } - } + *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 => {} diff --git a/tests/tests_mov.rs b/tests/tests_mov.rs index d076d39..746ad00 100644 --- a/tests/tests_mov.rs +++ b/tests/tests_mov.rs @@ -428,6 +428,12 @@ fn transform_mov_mov_split_ol_greater( // 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] => [(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 }, &[2, 3, 1, 0, 4, 5, 6, 7, 8])] fn transform_mov_mov_truncdel( #[case] a: Operation, #[case] b: Operation, From 7054674a134fbe953355c1071ced27f46b468f74 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 16 Sep 2024 02:05:11 +0200 Subject: [PATCH 11/13] fix: simplify overlapping mov-mov with same target --- src/lib.rs | 108 +++++++++++++++++++++++++-------------------- tests/proptest.rs | 2 +- tests/tests_mov.rs | 65 ++++++++++++++++++++------- 3 files changed, 110 insertions(+), 65 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1e92b55..5bd0675 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -951,14 +951,10 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo 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_pre = if matches!(im2, IntersectionExt::LessOverlap) { - *m_pos - } else { - ra.end - m_n_n - }; - let m_pos_n = project_mov_point(*m2_pos, *m2_n, *m2_to, m_pos_pre); + 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)); @@ -967,27 +963,38 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo } // A=>B2: - // 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 }; + 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; } } @@ -1105,37 +1112,42 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo 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 = if matches!(im2, IntersectionExt::LessOverlap) { - *m_pos - } else { - ra.end - m_n_n - }; - let m_pos_n = project_mov_point(*m2_pos, *m2_n, *m2_to, m_pos_pre); + let m_pos_pre = ra.end - m_n_n; - 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; - } + 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: - // 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 }; + 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; diff --git a/tests/proptest.rs b/tests/proptest.rs index 0ff676d..5e7af8b 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -10,7 +10,7 @@ const ALL_SIZE: usize = 20; const MIN_RANGE_LEN: usize = 1; const TEST_SIZE: usize = 9; -const TEST_MIN_RANGE_LEN: usize = 2; +const TEST_MIN_RANGE_LEN: usize = 1; const VEC_SIZE: usize = 100; diff --git a/tests/tests_mov.rs b/tests/tests_mov.rs index 746ad00..76fe21e 100644 --- a/tests/tests_mov.rs +++ b/tests/tests_mov.rs @@ -5,9 +5,6 @@ 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] // 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] @@ -409,6 +406,44 @@ fn transform_mov_mov_split_ol_greater( 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] @@ -417,24 +452,22 @@ fn transform_mov_mov_split_ol_greater( // 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, 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])] -// 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])] -// 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] => [(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 }, &[2, 3, 1, 0, 4, 5, 6, 7, 8])] -fn transform_mov_mov_truncdel( +// 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], From dddc1132b45ac91dbe7ef15c95a98f4d317d9771 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 16 Sep 2024 02:17:37 +0200 Subject: [PATCH 12/13] fix: docs, remove unnecessary condition --- src/lib.rs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5bd0675..c8cc051 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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> }, @@ -1297,9 +1321,6 @@ fn transform_mov(a: &mut Operation, b: &mut Operation, first: bo *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; From 1e6f3ddbff436666f900094093f8b6c696139798 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Mon, 16 Sep 2024 02:27:04 +0200 Subject: [PATCH 13/13] test: add transform_seq testing --- tests/proptest.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/proptest.rs b/tests/proptest.rs index 5e7af8b..246d805 100644 --- a/tests/proptest.rs +++ b/tests/proptest.rs @@ -359,4 +359,31 @@ proptest! { 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); + } }