Initial push to remote

This commit is contained in:
Olek 2016-12-23 11:11:08 +01:00
parent 662c916c88
commit 1942e4b0a0
6 changed files with 369 additions and 0 deletions

2
.gitignore vendored
View File

@ -1,6 +1,8 @@
# Generated by Cargo
# will have compiled files and executables
/target/
/.idea/
*.iml
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "kdtree-rust"
version = "0.1.0"
authors = ["Aleksander Fular <ntszar@gmail.com>"]
[dependencies]
rand = "*"
[dev-dependencies]
quickcheck = "0.3"

79
src/kdtree/mod.rs Normal file
View File

@ -0,0 +1,79 @@
mod test_common;
mod partition;
pub trait KdtreePointTrait {
fn dims(&self) -> &[f64];
}
pub struct Kdtree<T> {
nodes: Vec<KdtreeNode<T>>,
}
impl<T: KdtreePointTrait> Kdtree<T> {
pub fn new(points: Vec<T>) -> Kdtree<T> {
if points.len() == 0 {
panic!("empty vector point not allowed");
}
Kdtree {
nodes: vec![],
}
}
fn add_node(&mut self, p: T) {
let node = KdtreeNode::new(p);
self.nodes.push(node);
}
fn add_left_node(&mut self, for_node: usize, ) {
{
let len = self.nodes.len();
let node = self.nodes.get_mut(for_node).unwrap();
node.left_node = Some(len);
}
//self.nodes.push(KdtreeNode::new());
}
}
pub struct KdtreeNode<T> {
left_node: Option<usize>,
right_node: Option<usize>,
point: T,
}
impl<T: KdtreePointTrait> KdtreeNode<T> {
fn new(p: T) -> KdtreeNode<T> {
KdtreeNode {
left_node: None,
right_node: None,
point: p,
}
}
}
#[cfg(test)]
mod tests3 {
use ::kdtree::test_common::tests_utils::Point2WithId;
use super::*;
#[test]
#[should_panic(expected = "empty vector point not allowed")]
fn should_panic_given_empty_vector() {
let empty_vec: Vec<Point2WithId> = vec![];
let tree = Kdtree::new(empty_vec);
}
#[test]
fn test2() {
let p1 = Point2WithId::new(1, 1., 2.);
let p2 = Point2WithId::new(1, 1., 2.);
let vec = vec![p1, p2];
let tree = Kdtree::new(vec);
}
}

226
src/kdtree/partition.rs Normal file
View File

@ -0,0 +1,226 @@
use ::kdtree::*;
enum PointsWereOnSide {
Left,
Right,
Both,
}
struct PartitionPointHelper {
points_were_on_side: PointsWereOnSide,
midpoint_split_value: f64,
index_of_splitter: usize,
}
fn partition_sliding_midpoint_helper<T: KdtreePointTrait>(vec: &mut Vec<T>, midpoint_value: f64, partition_on_dimension: usize) -> PartitionPointHelper {
let mut closest_index = 0;
let mut closest_distance = (vec[0].dims()[partition_on_dimension] - midpoint_value).abs();
const HAS_POINTS_ON_LEFT_SIDE: i32 = 0b01;
const HAS_POINTS_ON_RIGHT_SIDE: i32 = 0b10;
const HAS_POINTS_ON_BOTH_SIDES: i32 = HAS_POINTS_ON_RIGHT_SIDE | HAS_POINTS_ON_LEFT_SIDE;
let mut has_points_on_sides = 0;
for i in 0..vec.len() {
let p = vec.get(i).unwrap();
if p.dims()[partition_on_dimension] <= midpoint_value {
has_points_on_sides |= HAS_POINTS_ON_LEFT_SIDE;
} else {
has_points_on_sides |= HAS_POINTS_ON_RIGHT_SIDE;
}
let dist = (p.dims()[partition_on_dimension] - midpoint_value).abs();
if dist < closest_distance {
closest_distance = dist;
closest_index = i;
}
}
if has_points_on_sides != HAS_POINTS_ON_BOTH_SIDES {
return PartitionPointHelper {
index_of_splitter: closest_index,
midpoint_split_value: vec.get(closest_index).unwrap().dims()[partition_on_dimension],
points_were_on_side: if has_points_on_sides == HAS_POINTS_ON_LEFT_SIDE { PointsWereOnSide::Left } else { PointsWereOnSide::Right }
}
}
return PartitionPointHelper {
index_of_splitter: closest_index,
midpoint_split_value: midpoint_value,
points_were_on_side: PointsWereOnSide::Both
}
}
pub fn partition_sliding_midpoint<T: KdtreePointTrait>(vec: &mut Vec<T>, midpoint_value: f64, partition_on_dimension: usize) -> (usize, f64) {
let vec_len = vec.len();
debug_assert!(vec[0].dims().len() > partition_on_dimension);
debug_assert!(vec.len() > 1);
let partition_point_data = partition_sliding_midpoint_helper(vec, midpoint_value, partition_on_dimension);
match partition_point_data.points_were_on_side {
PointsWereOnSide::Left => {
vec.swap(partition_point_data.index_of_splitter, vec_len - 1);
(vec_len - 1, partition_point_data.midpoint_split_value)
},
PointsWereOnSide::Right => {
vec.swap(partition_point_data.index_of_splitter, 0);
(0, partition_point_data.midpoint_split_value)
}
PointsWereOnSide::Both => {
let index_of_splitting_point = partition_kdtree(vec, partition_point_data.index_of_splitter, partition_on_dimension);
(index_of_splitting_point, partition_point_data.midpoint_split_value)
}
}
}
fn partition_kdtree<T: KdtreePointTrait>(vec: &mut Vec<T>, index_of_splitting_point: usize, partition_on_dimension: usize) -> usize {
let pivot = vec[index_of_splitting_point].dims()[partition_on_dimension];
let vec_len = vec.len();
vec.swap(index_of_splitting_point, vec_len - 1);
//using Lomuto variant of partition here, change it to hoare sometime?
let mut store_index = 0;
for left in 0..vec_len - 1 {
if vec[left].dims()[partition_on_dimension] <= pivot {
vec.swap(left, store_index);
store_index += 1;
}
}
vec.swap(store_index, vec_len - 1);
store_index
}
#[cfg(test)]
mod tests {
use ::kdtree::*;
use ::kdtree::test_common::tests_utils::*;
use ::rand::distributions::{IndependentSample, Range};
use ::rand::*;
use super::*;
use super::partition_kdtree;
#[test]
fn parition_kdtree_works() {
let p1 = Point2WithId::new(0, 1., 4.);
let p2 = Point2WithId::new(1, 2., 6.);
let p3 = Point2WithId::new(2, 3., 8.);
let p4 = Point2WithId::new(3, 0., 8.);
let p5 = Point2WithId::new(4, -1., 8.);
let p6 = Point2WithId::new(5, 3., 8.);
let p7 = Point2WithId::new(6, 4., 8.);
let mut vec = vec![p1, p2, p3, p4, p5, p6, p7];
assert_eq! (1, partition_kdtree(&mut vec.clone(), 3, 0));
assert_eq! (6, partition_kdtree(&mut vec.clone(), 6, 0));
assert_eq! (0, partition_kdtree(&mut vec.clone(), 4, 0));
assert_eq! (5, partition_kdtree(&mut vec.clone(), 2, 0));
}
quickcheck! {
fn partition_kdtree_qc(xs: Vec<f64>) -> bool {
let mut vec : Vec<Point1WithId> = vec![];
for i in 0 .. xs.len() {
let p = Point1WithId::new(i as i32, xs[i]);
vec.push(p);
}
if(xs.len() == 0 ) {
return true;
}
let between = Range::new(0, xs.len());
let mut rng = thread_rng();
for i in 0 .. 5 {
let random_splitting_index = between.ind_sample(&mut rng);
let mut vec = vec.clone();
let index_of_splitting_point = partition_kdtree(&mut vec, random_splitting_index, 0);
return assert_partition(&vec, index_of_splitting_point);
}
true
}
}
#[test]
#[ignore]
fn partition_given_midpoint_exactly_in_between_points_returns_smaller_index() {
let p1 = Point2WithId::new(1, 2., 4.);
let p2 = Point2WithId::new(1, 4., 6.);
let mut vec = vec![p1, p2];
assert_eq! ((0, 3.), partition_sliding_midpoint(&mut vec, 3., 0));
assert_eq! ((0, 5.), partition_sliding_midpoint(&mut vec, 5., 1));
}
#[test]
#[ignore]
fn partition_given_midpoint_which_has_all_points_on_one_side_slides_split_plane_and_returns_index_to_closest_element() {
let p1 = Point2WithId::new(1, 2., 4.);
let p2 = Point2WithId::new(2, 4., 6.);
let p3 = Point2WithId::new(3, 3., 7.);
let p4 = Point2WithId::new(4, 0., 8.);
let mut vec = vec![p1, p2, p3];
assert_eq! ((0, 2.), partition_sliding_midpoint(&mut vec, 1.9, 0));
assert_eq! (1, vec[0].id);
assert_eq! (2, vec[1].id);
assert_eq! (3, vec[2].id);
let mut vec = vec![p1, p2, p3];
assert_eq! ((0, 2.), partition_sliding_midpoint(&mut vec, -5000., 0));
assert_eq! (1, vec[0].id);
assert_eq! (2, vec[1].id);
assert_eq! (3, vec[2].id);
let mut vec = vec![p1, p2, p3];
assert_eq! ((2, 4.), partition_sliding_midpoint(&mut vec, 10., 0));
assert_eq! (1, vec[0].id);
assert_eq! (3, vec[1].id);
assert_eq! (2, vec[2].id);
let mut vec = vec![p1, p2, p3];
assert_eq! ((2, 7.), partition_sliding_midpoint(&mut vec, 10., 1));
assert_eq! (1, vec[0].id);
assert_eq! (2, vec[1].id);
assert_eq! (3, vec[2].id);
let mut vec = vec![p1, p2, p3, p4];
assert_eq! ((0, 0.), partition_sliding_midpoint(&mut vec, -5000., 0));
assert_eq! (4, vec[0].id);
assert_eq! (2, vec[1].id);
assert_eq! (3, vec[2].id);
assert_eq! (1, vec[3].id);
}
fn assert_partition(v: &Vec<Point1WithId>, index_of_splitting_point: usize) -> bool {
let pivot = v[index_of_splitting_point].dims()[0];
for i in 0 .. index_of_splitting_point {
if v[i].dims()[0] > pivot {
return false;
}
}
for i in index_of_splitting_point + 1 .. v.len() {
if v[i].dims()[0] < pivot {
return false;
}
}
true
}
}

45
src/kdtree/test_common.rs Normal file
View File

@ -0,0 +1,45 @@
#[cfg(test)]
pub mod tests_utils {
use ::kdtree::*;
#[derive(Copy, Clone, PartialEq)]
pub struct Point2WithId {
dims: [f64; 2],
pub id: i32,
}
impl Point2WithId {
pub fn new(id: i32, x: f64, y: f64) -> Point2WithId {
Point2WithId {
dims: [x, y],
id: id,
}
}
}
impl KdtreePointTrait for Point2WithId {
fn dims(&self) -> &[f64] {
return &self.dims;
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct Point1WithId {
dims: [f64; 1],
pub id: i32,
}
impl Point1WithId {
pub fn new(id: i32, x: f64) -> Point1WithId {
Point1WithId {
dims: [x],
id: id,
}
}
}
impl KdtreePointTrait for Point1WithId {
fn dims(&self) -> &[f64] {
return &self.dims;
}
}
}

7
src/lib.rs Normal file
View File

@ -0,0 +1,7 @@
#[cfg(test)]
#[macro_use]
extern crate quickcheck;
extern crate rand;
pub mod kdtree;