2020-06-09 00:05:14 +08:00
|
|
|
use std::cmp;
|
2020-06-11 03:35:01 +08:00
|
|
|
use std::time::Instant;
|
|
|
|
|
2020-06-10 05:06:59 +08:00
|
|
|
use pathfinding::directed::dijkstra::dijkstra;
|
2020-06-09 00:05:14 +08:00
|
|
|
|
2020-06-11 17:55:03 +08:00
|
|
|
use smallvec::smallvec; // the macro
|
|
|
|
use crate::SmallVec16;
|
|
|
|
|
2020-06-09 00:05:14 +08:00
|
|
|
const ONE_ATTRIBUTE: u32 = 1000;
|
|
|
|
const MAX_DISTANCE: u32 = 8;
|
|
|
|
|
2020-06-09 23:32:25 +08:00
|
|
|
fn index_proximity(lhs: u32, rhs: u32) -> u32 {
|
2020-06-10 22:27:02 +08:00
|
|
|
if lhs <= rhs {
|
2020-06-09 23:32:25 +08:00
|
|
|
cmp::min(rhs - lhs, MAX_DISTANCE)
|
|
|
|
} else {
|
|
|
|
cmp::min(lhs - rhs, MAX_DISTANCE) + 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn positions_proximity(lhs: u32, rhs: u32) -> u32 {
|
|
|
|
let (lhs_attr, lhs_index) = extract_position(lhs);
|
|
|
|
let (rhs_attr, rhs_index) = extract_position(rhs);
|
|
|
|
if lhs_attr != rhs_attr { MAX_DISTANCE }
|
|
|
|
else { index_proximity(lhs_index, rhs_index) }
|
|
|
|
}
|
|
|
|
|
2020-06-09 00:05:14 +08:00
|
|
|
// Returns the attribute and index parts.
|
|
|
|
fn extract_position(position: u32) -> (u32, u32) {
|
|
|
|
(position / ONE_ATTRIBUTE, position % ONE_ATTRIBUTE)
|
|
|
|
}
|
|
|
|
|
2020-06-10 05:06:59 +08:00
|
|
|
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
2020-06-11 17:55:03 +08:00
|
|
|
struct Path(SmallVec16<u32>);
|
2020-06-09 00:05:14 +08:00
|
|
|
|
2020-06-10 05:06:59 +08:00
|
|
|
impl Path {
|
|
|
|
fn new(positions: &[Vec<u32>]) -> Option<Path> {
|
|
|
|
let position = positions.first()?.first()?;
|
2020-06-11 17:55:03 +08:00
|
|
|
Some(Path(smallvec![*position]))
|
2020-06-10 05:06:59 +08:00
|
|
|
}
|
2020-06-09 00:05:14 +08:00
|
|
|
|
2020-06-10 20:20:35 +08:00
|
|
|
// TODO we must skip the successors that have already been sent
|
2020-06-11 17:55:03 +08:00
|
|
|
fn successors(&self, positions: &[Vec<u32>]) -> SmallVec16<(Path, u32)> {
|
|
|
|
let mut successors = SmallVec16::new();
|
2020-06-10 20:20:35 +08:00
|
|
|
|
|
|
|
// If we can grow or shift the path
|
|
|
|
if self.0.len() < positions.len() {
|
2020-06-10 22:27:02 +08:00
|
|
|
for next_pos in &positions[self.0.len()] {
|
|
|
|
let mut grown_path = self.0.clone();
|
|
|
|
grown_path.push(*next_pos);
|
|
|
|
let path = Path(grown_path);
|
|
|
|
let proximity = path.proximity();
|
|
|
|
successors.push((path, proximity));
|
|
|
|
}
|
2020-06-10 20:20:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// We retrieve the tail of the current path and try to find
|
|
|
|
// the successor of this tail.
|
2020-06-10 22:28:33 +08:00
|
|
|
let next_path_tail = self.0.last().unwrap() + 1;
|
2020-06-10 20:20:35 +08:00
|
|
|
// To do so we add 1 to the tail and check that something exists.
|
2020-06-10 22:28:33 +08:00
|
|
|
let path_tail_index = positions[self.0.len() - 1].binary_search(&next_path_tail).unwrap_or_else(|p| p);
|
2020-06-10 20:20:35 +08:00
|
|
|
// If we found something it means that we can shift the path.
|
|
|
|
if let Some(pos) = positions[self.0.len() - 1].get(path_tail_index) {
|
|
|
|
let mut shifted_path = self.0.clone();
|
|
|
|
*shifted_path.last_mut().unwrap() = *pos;
|
|
|
|
let path = Path(shifted_path);
|
|
|
|
let proximity = path.proximity();
|
|
|
|
successors.push((path, proximity));
|
|
|
|
}
|
|
|
|
|
|
|
|
successors
|
2020-06-10 05:06:59 +08:00
|
|
|
}
|
2020-06-09 00:05:14 +08:00
|
|
|
|
2020-06-10 05:06:59 +08:00
|
|
|
fn proximity(&self) -> u32 {
|
|
|
|
self.0.windows(2).map(|ps| positions_proximity(ps[0], ps[1])).sum::<u32>()
|
|
|
|
}
|
2020-06-09 00:05:14 +08:00
|
|
|
|
2020-06-10 05:06:59 +08:00
|
|
|
fn is_complete(&self, positions: &[Vec<u32>]) -> bool {
|
|
|
|
positions.len() == self.0.len()
|
2020-06-09 00:05:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct BestProximity {
|
|
|
|
positions: Vec<Vec<u32>>,
|
2020-06-10 05:06:59 +08:00
|
|
|
best_proximity: u32,
|
2020-06-09 00:05:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BestProximity {
|
|
|
|
pub fn new(positions: Vec<Vec<u32>>) -> BestProximity {
|
2020-06-10 05:06:59 +08:00
|
|
|
BestProximity { positions, best_proximity: 0 }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_path_successful(&self, path: &Path) -> bool {
|
|
|
|
path.is_complete(&self.positions) && path.proximity() >= self.best_proximity
|
2020-06-09 00:05:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Iterator for BestProximity {
|
2020-06-09 23:32:25 +08:00
|
|
|
type Item = (u32, Vec<Vec<u32>>);
|
2020-06-09 00:05:14 +08:00
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
2020-06-10 05:06:59 +08:00
|
|
|
let mut output: Option<(u32, Vec<Vec<u32>>)> = None;
|
|
|
|
|
2020-06-11 03:35:01 +08:00
|
|
|
let before = Instant::now();
|
|
|
|
|
2020-06-10 05:06:59 +08:00
|
|
|
loop {
|
|
|
|
let result = dijkstra(
|
2020-06-10 20:20:35 +08:00
|
|
|
&Path::new(&self.positions)?,
|
2020-06-10 05:06:59 +08:00
|
|
|
|p| p.successors(&self.positions),
|
2020-06-11 17:55:03 +08:00
|
|
|
|p| {
|
|
|
|
self.is_path_successful(p) &&
|
|
|
|
output.as_ref().map_or(true, |(_, paths)| {
|
|
|
|
!paths.iter().position(|q| q.as_slice() == p.0.as_slice()).is_some()
|
|
|
|
})
|
|
|
|
},
|
2020-06-10 05:06:59 +08:00
|
|
|
);
|
|
|
|
|
2020-06-10 22:28:33 +08:00
|
|
|
match result {
|
2020-06-10 20:20:35 +08:00
|
|
|
Some((mut paths, _)) => {
|
2020-06-10 05:06:59 +08:00
|
|
|
let positions = paths.pop().unwrap();
|
2020-06-10 20:20:35 +08:00
|
|
|
let proximity = positions.proximity();
|
2020-06-10 05:06:59 +08:00
|
|
|
|
|
|
|
// If the current output is
|
2020-06-10 22:28:33 +08:00
|
|
|
match &mut output {
|
2020-06-10 05:06:59 +08:00
|
|
|
Some((best_proximity, paths)) => {
|
|
|
|
// If the shortest path we found is bigger than the one requested
|
|
|
|
// it means that we found all the paths with the same proximity and can
|
|
|
|
// return those to the user.
|
|
|
|
if proximity > *best_proximity {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We add the new path to the output list as this path is known
|
|
|
|
// to be the requested distance.
|
2020-06-11 17:55:03 +08:00
|
|
|
paths.push(positions.0.to_vec());
|
2020-06-09 23:32:25 +08:00
|
|
|
},
|
2020-06-11 17:55:03 +08:00
|
|
|
None => output = Some((positions.proximity(), vec![positions.0.to_vec()])),
|
2020-06-09 23:32:25 +08:00
|
|
|
}
|
2020-06-10 05:06:59 +08:00
|
|
|
},
|
|
|
|
None => break,
|
2020-06-09 23:32:25 +08:00
|
|
|
}
|
|
|
|
}
|
2020-06-10 05:06:59 +08:00
|
|
|
|
2020-06-11 03:35:01 +08:00
|
|
|
eprintln!("BestProximity::next() took {:.02?}", before.elapsed());
|
|
|
|
|
2020-06-10 20:20:35 +08:00
|
|
|
if let Some((proximity, _)) = output.as_ref() {
|
|
|
|
self.best_proximity = proximity + 1;
|
|
|
|
}
|
|
|
|
|
2020-06-10 05:06:59 +08:00
|
|
|
output
|
2020-06-09 00:05:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn same_attribute() {
|
|
|
|
let positions = vec![
|
|
|
|
vec![0, 2, 3, 4 ],
|
|
|
|
vec![ 1, ],
|
|
|
|
vec![ 3, 6],
|
|
|
|
];
|
|
|
|
let mut iter = BestProximity::new(positions);
|
|
|
|
|
2020-06-10 20:20:35 +08:00
|
|
|
assert_eq!(iter.next(), Some((1+2, vec![vec![0, 1, 3]]))); // 3
|
|
|
|
assert_eq!(iter.next(), Some((2+2, vec![vec![2, 1, 3]]))); // 4
|
|
|
|
assert_eq!(iter.next(), Some((3+2, vec![vec![3, 1, 3]]))); // 5
|
|
|
|
assert_eq!(iter.next(), Some((1+5, vec![vec![0, 1, 6], vec![4, 1, 3]]))); // 6
|
|
|
|
assert_eq!(iter.next(), Some((2+5, vec![vec![2, 1, 6]]))); // 7
|
|
|
|
assert_eq!(iter.next(), Some((3+5, vec![vec![3, 1, 6]]))); // 8
|
|
|
|
assert_eq!(iter.next(), Some((4+5, vec![vec![4, 1, 6]]))); // 9
|
|
|
|
assert_eq!(iter.next(), None);
|
2020-06-09 00:05:14 +08:00
|
|
|
}
|
2020-06-10 22:27:02 +08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn different_attributes() {
|
|
|
|
let positions = vec![
|
|
|
|
vec![0, 2, 1000, 1001, 2000 ],
|
|
|
|
vec![ 1, 1000, 2001 ],
|
|
|
|
vec![ 3, 6, 2002, 3000],
|
|
|
|
];
|
|
|
|
let mut iter = BestProximity::new(positions);
|
|
|
|
|
|
|
|
assert_eq!(iter.next(), Some((1+1, vec![vec![2000, 2001, 2002]]))); // 2
|
|
|
|
assert_eq!(iter.next(), Some((1+2, vec![vec![0, 1, 3]]))); // 3
|
|
|
|
assert_eq!(iter.next(), Some((2+2, vec![vec![2, 1, 3]]))); // 4
|
|
|
|
assert_eq!(iter.next(), Some((1+5, vec![vec![0, 1, 6]]))); // 6
|
|
|
|
// We ignore others here...
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn easy_proximities() {
|
|
|
|
fn slice_proximity(positions: &[u32]) -> u32 {
|
|
|
|
positions.windows(2).map(|ps| positions_proximity(ps[0], ps[1])).sum::<u32>()
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(slice_proximity(&[1000, 1000, 2002]), 8);
|
|
|
|
}
|
2020-06-09 00:05:14 +08:00
|
|
|
}
|