Skip to content

Instantly share code, notes, and snippets.

@kindlychung
Created September 18, 2016 16:07
Show Gist options
  • Save kindlychung/07a20a2441ef35e1cd1f2a55d6087836 to your computer and use it in GitHub Desktop.
Save kindlychung/07a20a2441ef35e1cd1f2a55d6087836 to your computer and use it in GitHub Desktop.
Compiling algorithms_in_a_nutshell v0.1.0 (file:///Users/kaiyin/Documents/workspace-eclipse-neon/algorithms_in_a_nutshell)
error: `i` does not live long enough
--> src/convex_hull.rs:231:15
|
229 | (0..5).into_iter().map(|j| {
| --- capture occurs here
230 | let j = j as f64;
231 | Point::new(i, j)
| ^ does not live long enough
232 | })
233 | }).collect::<BTreeSet<Point>>();
| - - borrowed value needs to live until here
| |
| borrowed value only lives until here
error: aborting due to previous error
Build failed, waiting for other jobs to finish...
error: Could not compile `algorithms_in_a_nutshell`.
To learn more, run the command again with --verbose.
use std::cmp::Ordering::Equal;
use std::cmp::Ordering;
use std::ops::{Add, Sub, Mul};
use std::collections::BTreeSet;
#[derive(Debug, Copy, Clone)]
struct Point {
x: f64,
y: f64,
}
impl PartialEq for Point {
fn eq(&self, other: &Point) -> bool {
if self.x.is_nan() & other.x.is_nan() & self.y.is_nan() & other.y.is_nan() {
return true;
}
if self.x.is_nan() & other.x.is_nan() {
return self.y == other.y;
}
if self.y.is_nan() & other.y.is_nan() {
return self.x == other.x;
}
(self.x == other.x) & (self.y == other.y)
}
}
impl Eq for Point {}
impl PartialOrd for Point {
fn partial_cmp(&self, other: &Point) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Point {
fn cmp(&self, other: &Point) -> Ordering {
let c = self.x.partial_cmp(&other.x).unwrap();
if c == Equal {
self.y.partial_cmp(&other.y).unwrap()
} else {
c
}
}
}
impl Point {
fn new(x: f64, y: f64) -> Point {
if x.is_nan() | y.is_nan() {
panic!("Coordinates cannot be NaN!");
}
Point {
x: x,
y: y,
}
}
// Euclidean distance
fn distance(&self, other: &Point) -> f64 {
((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()
}
// Draw a horizontal line through this point, connect this point with the other, and measure the angle between these two lines.
fn angle(&self, other: &Point) -> f64 {
(other.y - self.y).atan2(other.x - self.x)
}
}
impl Add for Point {
type Output = Point;
fn add(self, rhs: Point) -> Point {
Point::new(self.x + rhs.x, self.y + rhs.y)
}
}
impl Sub for Point {
type Output = Point;
fn sub(self, rhs: Point) -> Point {
Point::new(self.x - rhs.x, self.y - rhs.y)
}
}
// dot product
impl Mul for Point {
type Output = f64;
fn mul(self, rhs: Point) -> f64 {
self.x * rhs.x + self.y * rhs.y
}
}
#[test]
fn test_point() {
use std::f64::consts::PI;
let p1 = Point::new(0.0, 0.0);
let p2 = Point::new(0.0, 1.0);
assert_eq!(p1.angle(&p2), PI/2.0);
assert_eq!(p1.distance(&p2), 1.0);
let p1 = Point::new(0.0, 0.0);
let p2 = Point::new(1.0, 1.0);
assert_eq!(p1.angle(&p2), PI/4.0);
assert_eq!(p1.distance(&p2), 2.0f64.sqrt());
let p1 = Point::new(0.0, 0.0);
let p2 = Point::new(1.0, -1.0);
assert_eq!(p1.angle(&p2), -PI/4.0);
assert_eq!(p1.distance(&p2), 2.0f64.sqrt());
}
#[derive(PartialEq,Eq,Debug)]
struct Triangle {
p0: Point,
p1: Point,
p2: Point,
}
impl Triangle {
fn new(p0: Point, p1: Point, p2: Point) -> Triangle {
// Sort by x-coordinate to make sure the first point is the leftmost and lowest.
let mut v: Vec<Point> = vec![p0, p1, p2];
v.sort();
Triangle {
p0: v[0],
p1: v[1],
p2: v[2],
}
}
fn range_x(&self) -> (f64, f64) {
(self.p0.x, self.p2.x)
}
fn range_y(&self) -> (f64, f64) {
let mut v = vec![self.p0, self.p1, self.p2];
v.sort_by(|a, b| {
a.y.partial_cmp(&(b.y)).unwrap_or(Equal)
});
(v[0].y, v[2].y)
}
// Barycentric Technique, check whether point is in triangle, see http://blackpawn.com/texts/pointinpoly/
fn contains(&self, p: Point) -> bool {
let v0 = self.p2 - self.p0;
let v1 = self.p1 - self.p0;
let v2 = p - self.p0;
let dot00 = v0 * v0;
let dot01 = v0 * v1;
let dot02 = v0 * v2;
let dot11 = v1 * v1;
let dot12 = v1 * v2;
let inv_denom = 1.0f64 / (dot00 * dot11 - dot01 * dot01);
let u = (dot11 * dot02 - dot01 * dot12) * inv_denom;
let v = (dot00 * dot12 - dot01 * dot02) * inv_denom;
(u >= 0.0) & (v >= 0.0) & (u + v <= 1.0)
}
}
#[macro_export]
macro_rules! btreeset {
($($x: expr),*) => {{
let mut set = ::std::collections::BTreeSet::new();
$( set.insert($x); )*
set
}}
}
fn convex_hull_naive(points: &BTreeSet<Point>) -> Vec<Point> {
// you must have at least 3 points, otherwise there is no hull
assert!(points.len() > 2);
// a fn for removing just one point from the set
let minus_one = |p: &Point| {
points.difference(&(btreeset!(p.clone()))).cloned().collect::<BTreeSet<Point>>()
};
// set of points that are marked as internal
let mut p_internal_set: BTreeSet<Point> = BTreeSet::new();
// check permutations of 4 points, check if the fourth point is contained in the triangle
for p_i in points {
let minus_i = minus_one(&p_i);
for p_j in &minus_i {
let minus_j = minus_one(&p_j);
for p_k in &minus_j {
let minus_k = minus_one(&p_k);
for p_m in &minus_k {
if Triangle::new(p_i.clone(), p_j.clone(), p_k.clone()).contains(p_m.clone()) {
p_internal_set.insert(p_m.clone());
}
}
}
}
}
// set of points that are not internal
let mut hull = points.difference(&p_internal_set).cloned().collect::<Vec<Point>>();
// sort by coordinates so that the first point is the leftmost
hull.sort();
let head = hull[0].clone();
let angle_to_head = |p: &Point| {
if p == &head {
0.0
} else {
head.angle(p)
}
};
// sort by the angle with the first point
// when that is equal, sort by x coord
// what that is equal, sort by y coord
hull.sort_by(|a, b| {
let angle_a = angle_to_head(&a);
let angle_b = angle_to_head(&b);
let res1 = angle_a.partial_cmp(&angle_b).unwrap();
if res1 == Equal {
let res2 = a.x.partial_cmp(&b.x).unwrap();
if res2 == Equal {
let res3 = b.y.partial_cmp(&a.y).unwrap();
res3
} else {
res2
}
} else {
res1
}
});
hull
}
#[test]
fn test_convex_hull_naive() {
let points = (0..5).into_iter().flat_map(|i| {
let i = i as f64;
(0..5).into_iter().map(|j| {
let j = j as f64;
Point::new(i, j)
})
}).collect::<BTreeSet<Point>>();
let hull = convex_hull_naive(&points);
let hull_should_be = vec![
Point::new(0.0, 0.0),
Point::new(1.0, 0.0),
Point::new(2.0, 0.0),
Point::new(3.0, 0.0),
Point::new(3.0, 1.0),
Point::new(3.0, 2.0),
Point::new(3.0, 3.0),
Point::new(2.0, 3.0),
Point::new(1.0, 3.0),
Point::new(0.0, 3.0),
Point::new(0.0, 2.0),
Point::new(0.0, 1.0),
];
assert_eq!(hull, hull_should_be);
}
#[test]
fn test_triangle() {
let p2 = Point::new(0.0, 0.0);
let p1 = Point::new(0.0, 1.0);
let p0 = Point::new(1.0, 1.0);
let t = Triangle::new(p0, p1, p2);
assert_eq!(t.range_x(), (0.0, 1.0));
assert_eq!(t.range_y(), (0.0, 1.0));
assert_eq!(t.p0, p2);
assert_eq!(t.p1, p1);
assert_eq!(t.p2, p0);
// triangle should contain its vertices
assert!(t.contains(p0));
assert!(t.contains(p1));
assert!(t.contains(p2));
// triangle should contain points on any side
assert!(t.contains(Point::new(0.5, 0.5)));
assert!(t.contains(Point::new(0.3, 0.3)));
assert!(t.contains(Point::new(0.2, 0.2)));
assert!(t.contains(Point::new(0.1, 0.1)));
assert!(t.contains(Point::new(0.0, 0.1)));
assert!(t.contains(Point::new(0.0, 0.2)));
assert!(t.contains(Point::new(0.1, 1.0)));
assert!(t.contains(Point::new(0.2, 1.0)));
assert!(!t.contains(Point::new(0.2, 1.1)));
// strictly inside the triangle
assert!(t.contains(Point::new(0.5, 0.51)));
assert!(t.contains(Point::new(0.51, 0.51)));
assert!(t.contains(Point::new(0.51, 0.52)));
let p2 = Point::new(0.0, 0.0);
let p1 = Point::new(0.5, 1.0);
let p0 = Point::new(1.0, 0.5);
let t = Triangle::new(p0, p1, p2);
assert_eq!(t.range_x(), (0.0, 1.0));
assert_eq!(t.range_y(), (0.0, 1.0));
assert_eq!(t.p0, p2);
assert_eq!(t.p1, p1);
assert_eq!(t.p2, p0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment