use crate::util::{Line, Point, Segment};
use std::collections::VecDeque;
struct Hull {
upper: bool,
data: VecDeque<Point>,
}
impl Hull {
fn new_upper() -> Hull {
return Hull {
upper: true,
data: VecDeque::new(),
};
}
fn new_lower() -> Hull {
return Hull {
upper: false,
data: VecDeque::new(),
};
}
fn remove_front(&mut self, count: usize) {
for _ in 0..count {
let res = self.data.pop_front();
debug_assert!(res.is_some());
}
}
fn push(&mut self, pt: Point) {
self.data.push_back(pt);
while self.data.len() > 2 {
let pt1 = &self.data[self.data.len() - 1];
let pt2 = &self.data[self.data.len() - 2];
let pt3 = &self.data[self.data.len() - 3];
let line = pt1.line_to(pt3);
if (self.upper && pt2.above(&line)) || (!self.upper && pt2.below(&line)) {
let res = self.data.remove(self.data.len() - 2);
debug_assert!(res.is_some());
} else {
break;
}
}
}
fn items(&self) -> &VecDeque<Point> {
return &self.data;
}
}
#[derive(PartialEq)]
enum OptimalState {
Need2,
Need1,
Ready,
}
pub struct OptimalPLR {
state: OptimalState,
gamma: f64,
s0: Option<Point>,
s1: Option<Point>,
s_last: Option<Point>,
rho_lower: Option<Line>,
rho_upper: Option<Line>,
upper_hull: Option<Hull>,
lower_hull: Option<Hull>,
}
impl OptimalPLR {
pub fn new(gamma: f64) -> OptimalPLR {
return OptimalPLR {
state: OptimalState::Need2,
gamma,
s0: None,
s1: None,
s_last: None,
rho_lower: None,
rho_upper: None,
upper_hull: None,
lower_hull: None,
};
}
fn setup(&mut self) {
let gamma = self.gamma;
let s0 = self.s0.as_ref().unwrap();
let s1 = self.s1.as_ref().unwrap();
self.rho_lower = Some(s0.upper_bound(gamma).line_to(&s1.lower_bound(gamma)));
self.rho_upper = Some(s0.lower_bound(gamma).line_to(&s1.upper_bound(gamma)));
let mut upper_hull = Hull::new_upper();
let mut lower_hull = Hull::new_lower();
upper_hull.push(s0.upper_bound(gamma));
upper_hull.push(s1.upper_bound(gamma));
lower_hull.push(s0.lower_bound(gamma));
lower_hull.push(s1.lower_bound(gamma));
self.upper_hull = Some(upper_hull);
self.lower_hull = Some(lower_hull);
}
fn current_segment(&self, end: f64) -> Segment {
assert!(self.state == OptimalState::Ready);
let sint = Line::intersection(
self.rho_lower.as_ref().unwrap(),
self.rho_upper.as_ref().unwrap(),
);
let segment_start = self.s0.as_ref().unwrap().as_tuple().0;
let segment_stop = end;
let avg_slope = Line::average_slope(
self.rho_lower.as_ref().unwrap(),
self.rho_upper.as_ref().unwrap(),
);
let (sint_x, sint_y) = sint.as_tuple();
let intercept = -avg_slope * sint_x + sint_y;
return Segment {
start: segment_start,
stop: segment_stop,
slope: avg_slope,
intercept,
};
}
fn process_pt(&mut self, pt: Point) -> Option<Segment> {
assert!(self.state == OptimalState::Ready);
if !(pt.above(self.rho_lower.as_ref().unwrap())
&& pt.below(self.rho_upper.as_ref().unwrap()))
{
let current_segment = self.current_segment(pt.as_tuple().0);
self.s0 = Some(pt);
self.state = OptimalState::Need1;
return Some(current_segment);
}
let s_upper = pt.upper_bound(self.gamma);
let s_lower = pt.lower_bound(self.gamma);
if s_upper.below(self.rho_upper.as_ref().unwrap()) {
let lower_hull: &mut Hull = self.lower_hull.as_mut().unwrap();
let it = lower_hull
.items()
.iter()
.enumerate()
.map(|(idx, pt)| (idx, pt.line_to(&s_upper)));
let mut curr_best_val = std::f64::INFINITY;
let mut curr_best_idx = 0;
for (idx, line) in it {
if line.slope() < curr_best_val {
curr_best_idx = idx;
curr_best_val = line.slope();
}
}
self.rho_upper = Some(s_upper.line_to(&lower_hull.items()[curr_best_idx]));
lower_hull.remove_front(curr_best_idx);
lower_hull.push(s_lower);
}
if s_lower.above(self.rho_lower.as_ref().unwrap()) {
let upper_hull: &mut Hull = self.upper_hull.as_mut().unwrap();
let it = upper_hull
.items()
.iter()
.enumerate()
.map(|(idx, pt)| (idx, pt.line_to(&s_lower)));
let mut curr_best_val = -std::f64::INFINITY;
let mut curr_best_idx = 0;
for (idx, line) in it {
if line.slope() > curr_best_val {
curr_best_idx = idx;
curr_best_val = line.slope();
}
}
self.rho_lower = Some(s_lower.line_to(&upper_hull.items()[curr_best_idx]));
upper_hull.remove_front(curr_best_idx);
upper_hull.push(s_upper);
}
return None;
}
pub fn process(&mut self, x: f64, y: f64) -> Option<Segment> {
let pt = Point::new(x, y);
self.s_last = Some(pt);
let mut returned_segment: Option<Segment> = None;
let new_state = match self.state {
OptimalState::Need2 => {
self.s0 = Some(pt);
OptimalState::Need1
}
OptimalState::Need1 => {
self.s1 = Some(pt);
self.setup();
OptimalState::Ready
}
OptimalState::Ready => {
returned_segment = self.process_pt(pt);
if returned_segment.is_some() {
OptimalState::Need1
} else {
OptimalState::Ready
}
}
};
self.state = new_state;
return returned_segment;
}
pub fn finish(self) -> Option<Segment> {
return match self.state {
OptimalState::Need2 => None,
OptimalState::Need1 => {
let s0 = self.s0.unwrap().as_tuple();
Some(Segment {
start: s0.0,
stop: std::f64::MAX,
slope: 0.0,
intercept: s0.1,
})
}
OptimalState::Ready => Some(self.current_segment(std::f64::MAX)),
};
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test_util::*;
use approx::*;
#[test]
fn test_upper_hull() {
let mut hull = Hull::new_upper();
hull.push(Point::new(1.0, 1.0));
hull.push(Point::new(2.0, 1.0));
hull.push(Point::new(3.0, 3.0));
hull.push(Point::new(4.0, 3.0));
assert_eq!(hull.items().len(), 3);
let items = hull.items();
assert_relative_eq!(items[0].as_tuple().0, 1.0);
assert_relative_eq!(items[0].as_tuple().1, 1.0);
assert_relative_eq!(items[1].as_tuple().0, 2.0);
assert_relative_eq!(items[1].as_tuple().1, 1.0);
assert_relative_eq!(items[2].as_tuple().0, 4.0);
assert_relative_eq!(items[2].as_tuple().1, 3.0);
}
#[test]
fn test_lower_hull() {
let mut hull = Hull::new_lower();
hull.push(Point::new(1.0, 1.0));
hull.push(Point::new(2.0, 1.0));
hull.push(Point::new(3.0, 3.0));
hull.push(Point::new(4.0, 3.0));
assert_eq!(hull.items().len(), 3);
let items = hull.items();
assert_relative_eq!(items[0].as_tuple().0, 1.0);
assert_relative_eq!(items[0].as_tuple().1, 1.0);
assert_relative_eq!(items[1].as_tuple().0, 3.0);
assert_relative_eq!(items[1].as_tuple().1, 3.0);
assert_relative_eq!(items[2].as_tuple().0, 4.0);
assert_relative_eq!(items[2].as_tuple().1, 3.0);
}
#[test]
fn test_sin() {
let mut plr = OptimalPLR::new(0.0005);
let data = sin_data();
let mut segments = Vec::new();
for &(x, y) in data.iter() {
if let Some(segment) = plr.process(x, y) {
segments.push(segment);
}
}
if let Some(segment) = plr.finish() {
segments.push(segment);
}
assert_eq!(segments.len(), 66);
verify_gamma(0.0005, &data, &segments);
}
#[test]
fn test_linear() {
let mut plr = OptimalPLR::new(0.0005);
let data = linear_data(10.0, 25.0);
let mut segments = Vec::new();
for &(x, y) in data.iter() {
if let Some(segment) = plr.process(x, y) {
segments.push(segment);
}
}
if let Some(segment) = plr.finish() {
segments.push(segment);
}
assert_eq!(segments.len(), 1);
verify_gamma(0.0005, &data, &segments);
}
#[test]
fn test_precision() {
let mut plr = OptimalPLR::new(0.00005);
let data = precision_data();
let mut segments = Vec::new();
for &(x, y) in data.iter() {
if let Some(segment) = plr.process(x, y) {
segments.push(segment);
}
}
if let Some(segment) = plr.finish() {
segments.push(segment);
}
assert_eq!(segments.len(), 1);
verify_gamma(0.00005, &data, &segments);
}
}