use std::cell::RefCell;
use crate::HananCoord;
use super::point::*;
use super::permutations;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct PositionSequence {
position_sequence: Vec<usize>
}
impl PositionSequence {
pub(crate) fn new(position_sequence: Vec<usize>) -> Self {
let s = Self { position_sequence };
assert!(s.is_valid());
s
}
pub fn len(&self) -> usize {
self.position_sequence.len()
}
pub fn sequence(&self) -> &[usize] {
&self.position_sequence
}
pub fn group_index(&self) -> usize {
permutations::group_index(self)
}
pub fn from_points<T>(points: &[Point<T>]) -> Self
where T: Ord + Copy {
let mut points_x_ascending = points.to_vec();
points_x_ascending.sort_by_key(|p| p.x);
let mut indices: Vec<_> = (0..points.len()).collect();
indices.sort_by_key(|&idx| points_x_ascending[idx].y);
Self { position_sequence: indices }
}
pub fn from_points_noalloc<T>(mut reuse: Self, points: &[Point<T>]) -> Self
where T: Ord + Copy {
reuse.position_sequence.clear();
reuse.position_sequence.extend(0..points.len());
position_sequence_no_alloc(&mut reuse.position_sequence, points);
reuse
}
pub fn to_points(&self) -> impl Iterator<Item=Point<HananCoord>> + '_ {
debug_assert!(self.is_valid());
self.position_sequence.iter()
.enumerate()
.map(|(y, &x)| Point::new(x as HananCoord, y as HananCoord))
}
pub(crate) fn is_valid(&self) -> bool {
let n = self.position_sequence.len();
(0..n).all(|i| self.position_sequence.contains(&i))
}
}
#[test]
fn test_position_sequence_to_points() {
let seq = PositionSequence::new(vec![0, 4, 3, 1, 5, 2, 6]);
let points: Vec<_> = seq.to_points().collect();
assert_eq!(PositionSequence::from_points(&points), seq);
}
fn position_sequence_no_alloc<T>(position_sequence: &mut [usize], points: &[Point<T>])
where T: Ord + Copy {
assert_eq!(position_sequence.len(), points.len(), "sizes of arrays must match");
thread_local! {
static SCRATCH: RefCell<Vec<usize>> = RefCell::new(vec![]);
}
SCRATCH.with(|scratch| {
let mut scratch = scratch.borrow_mut();
scratch.clear();
scratch.extend(0..points.len());
scratch.sort_by_key(|&idx| points[idx].x);
position_sequence.iter_mut()
.enumerate()
.for_each(|(i, v)| *v = i);
position_sequence.sort_by_key(|&idx| points[scratch[idx]].y);
scratch.clear();
});
}
#[test]
fn test_position_sequence() {
let points = vec![(0, 1).into(), (1, 3).into(), (2, 0).into(), (3, 2).into()];
assert_eq!(PositionSequence::from_points(&points).position_sequence, vec![2, 0, 3, 1]);
}