use crate::util::{Line, Point, Segment};
#[derive(PartialEq)]
enum GreedyState {
Need2,
Need1,
Ready,
}
pub struct GreedyPLR {
state: GreedyState,
gamma: f64,
s0: Option<Point>,
s1: Option<Point>,
sint: Option<Point>,
s_last: Option<Point>,
rho_lower: Option<Line>,
rho_upper: Option<Line>,
}
impl GreedyPLR {
pub fn new(gamma: f64) -> GreedyPLR {
return GreedyPLR {
state: GreedyState::Need2,
gamma,
s0: None,
s1: None,
sint: None,
s_last: None,
rho_lower: None,
rho_upper: 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)));
self.sint = Some(Line::intersection(
self.rho_lower.as_ref().unwrap(),
self.rho_upper.as_ref().unwrap(),
));
assert!(self.sint.is_some());
}
fn current_segment(&self, end: f64) -> Segment {
assert!(self.state == GreedyState::Ready);
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) = self.sint.as_ref().unwrap().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 == GreedyState::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 = GreedyState::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()) {
self.rho_upper = Some(self.sint.as_ref().unwrap().line_to(&s_upper));
}
if s_lower.above(self.rho_lower.as_ref().unwrap()) {
self.rho_lower = Some(self.sint.as_ref().unwrap().line_to(&s_lower));
}
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 {
GreedyState::Need2 => {
self.s0 = Some(pt);
GreedyState::Need1
}
GreedyState::Need1 => {
self.s1 = Some(pt);
self.setup();
GreedyState::Ready
}
GreedyState::Ready => {
returned_segment = self.process_pt(pt);
if returned_segment.is_some() {
GreedyState::Need1
} else {
GreedyState::Ready
}
}
};
self.state = new_state;
return returned_segment;
}
pub fn finish(self) -> Option<Segment> {
return match self.state {
GreedyState::Need2 => None,
GreedyState::Need1 => {
let s0 = self.s0.unwrap().as_tuple();
Some(Segment {
start: s0.0,
stop: std::f64::MAX,
slope: 0.0,
intercept: s0.1,
})
}
GreedyState::Ready => Some(self.current_segment(std::f64::MAX)),
};
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test_util::*;
#[test]
fn test_sin() {
let mut plr = GreedyPLR::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(), 77);
verify_gamma(0.0005, &data, &segments);
}
#[test]
fn test_linear() {
let mut plr = GreedyPLR::new(0.00005);
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.00005, &data, &segments);
}
#[test]
fn test_precision() {
let mut plr = GreedyPLR::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);
}
#[test]
fn test_osm() {
let mut plr = GreedyPLR::new(64.0);
let data = osm_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);
}
verify_gamma(64.0, &data, &segments);
}
#[test]
fn test_fb() {
let mut plr = GreedyPLR::new(64.0);
let data = fb_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);
}
verify_gamma(64.0, &data, &segments);
}
}