use ff_structure::NAIDX;
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub enum Move {
Add {
i: NAIDX,
j: NAIDX,
},
Del {
i: NAIDX,
j: NAIDX,
},
ShiftIK {
i: NAIDX,
j: NAIDX,
k: NAIDX,
},
ShiftJK {
i: NAIDX,
j: NAIDX,
k: NAIDX,
},
ShiftILJK {
i: NAIDX,
j: NAIDX,
k: NAIDX,
l: NAIDX,
},
ShiftIKLJ {
i: NAIDX,
j: NAIDX,
k: NAIDX,
l: NAIDX,
},
}
impl Move {
pub fn inverse(self) -> Self {
match self {
Move::Add { i, j } => Move::Del { i, j },
Move::Del { i, j } => Move::Add { i, j },
Move::ShiftIK { i, j, k } => {
if i < k {
Move::ShiftIK { i, j: k, k: j }
} else {
Move::ShiftJK { i: k, j: i, k: j }
}
}
Move::ShiftJK { i, j, k } => {
if k < j {
Move::ShiftJK { i: k, j, k: i }
} else {
Move::ShiftIK { i: j, j: k, k: i }
}
}
Move::ShiftILJK { i, j, k, l } => {
Move::ShiftIKLJ { i, j: l, k: j, l: k }
}
Move::ShiftIKLJ { i, j, k, l } => {
Move::ShiftILJK { i, j: k, k: l, l:j }
}
}
}
pub fn added_pair(&self) -> (NAIDX, NAIDX) {
match &self {
Move::Add { i, j } => (*i, *j),
Move::ShiftIK { i, k, .. } => if i < k { (*i, *k) } else { (*k, *i) },
Move::ShiftJK { j, k, .. } => if j < k { (*j, *k) } else { (*k, *j) },
_ => unreachable!("Move should add a single pair!"),
}
}
pub fn added_pairs(&self) -> ((NAIDX, NAIDX), (NAIDX, NAIDX)) {
match &self {
Move::ShiftIKLJ { i, j, k, l } => ((*i, *k), (*l, *j)),
Move::ShiftILJK { i, j, k, l } => ((*i, *l), (*j, *k)),
_ => unreachable!("Move should add exactly two pairs!"),
}
}
pub fn deleted_pair(&self) -> (NAIDX, NAIDX) {
match &self {
Move::Del { i, j } => (*i, *j),
Move::ShiftIK { i, j, .. } => (*i, *j),
Move::ShiftJK { i, j, .. } => (*i, *j),
_ => unreachable!("Move should delete a single pair!"),
}
}
pub fn deleted_pairs(&self) -> ((NAIDX, NAIDX), (NAIDX, NAIDX)) {
match &self {
Move::ShiftIKLJ { i, j, k, l } => ((*i, *j), (*k, *l)),
Move::ShiftILJK { i, j, k, l } => ((*i, *j), (*k, *l)),
_ => unreachable!("Move should delete exactly two pairs!"),
}
}
pub fn conflicts(&self, pair: (NAIDX, NAIDX)) -> bool {
#[inline]
fn overlaps(a: (NAIDX, NAIDX), b: (NAIDX, NAIDX)) -> bool {
let (p, q) = a;
let (m, n) = b;
!(q < m || n < p || (p < m && n < q) || (m < p && q < n))
}
match &self {
Move::Add { .. } => overlaps(self.added_pair(), pair),
Move::ShiftIK { .. } => overlaps(self.added_pair(), pair),
Move::ShiftJK { .. } => overlaps(self.added_pair(), pair),
Move::ShiftILJK { .. } => {
let (p1, p2) = self.added_pairs();
overlaps(p1, pair) || overlaps(p2, pair)
}
Move::ShiftIKLJ { .. } => {
let (p1, p2) = self.added_pairs();
overlaps(p1, pair) || overlaps(p2, pair)
}
_ => unreachable!("conflicts() is only valid for moves that add pairs"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn pair(a: NAIDX, b: NAIDX) -> (NAIDX, NAIDX) {
if a < b { (a, b) } else { (b, a) }
}
#[test]
fn test_inverse() {
let moves = [
Move::Add { i: 2, j: 10 },
Move::Del { i: 3, j: 7 },
Move::ShiftIK { i: 2, j: 8, k: 5 },
Move::ShiftJK { i: 2, j: 8, k: 5 },
Move::ShiftIKLJ { i: 1, j: 10, k: 4, l: 7 },
Move::ShiftILJK { i: 1, j: 10, k: 4, l: 7 },
];
for mv in moves {
assert_eq!(mv.inverse().inverse(), mv);
}
}
#[test]
fn add_and_del_pairs() {
let add = Move::Add { i: 3, j: 9 };
assert_eq!(add.added_pair(), pair(3, 9));
let del = Move::Del { i: 3, j: 9 };
assert_eq!(del.deleted_pair(), (3, 9));
}
#[test]
fn three_way_shift_pairs() {
let mv = Move::ShiftIK { i: 2, j: 8, k: 5 };
assert_eq!(mv.deleted_pair(), (2, 8));
assert_eq!(mv.added_pair(), pair(2, 5));
let mv = Move::ShiftJK { i: 2, j: 8, k: 5 };
assert_eq!(mv.deleted_pair(), (2, 8));
assert_eq!(mv.added_pair(), pair(8, 5));
}
#[test]
fn four_way_shift_iklj_pairs() {
let mv = Move::ShiftIKLJ { i: 1, j: 10, k: 4, l: 7 };
let (d1, d2) = mv.deleted_pairs();
assert_eq!(pair(d1.0, d1.1), pair(1, 10));
assert_eq!(pair(d2.0, d2.1), pair(4, 7));
let (a1, a2) = mv.added_pairs();
assert_eq!(pair(a1.0, a1.1), pair(1, 4));
assert_eq!(pair(a2.0, a2.1), pair(7, 10));
}
#[test]
fn four_way_shift_iljk_pairs() {
let mv = Move::ShiftILJK { i: 1, j: 10, k: 4, l: 7 };
let (d1, d2) = mv.deleted_pairs();
assert_eq!(pair(d1.0, d1.1), pair(1, 10));
assert_eq!(pair(d2.0, d2.1), pair(4, 7));
let (a1, a2) = mv.added_pairs();
assert_eq!(pair(a1.0, a1.1), pair(1, 7));
assert_eq!(pair(a2.0, a2.1), pair(4, 10));
}
}