use super::{
build_names, BinBuildEnvironment, BinDescription, Calculator,
FetchItem, GetCalibration, Item, Iteration, Result, Scope, SinkBin,
SinkNames, SourceBin, SourceId, SourceNames, SourceSinkBinDescription,
WriteDotSimple, SINK_INPUT,
};
use crate::{error, R64};
use indexmap::IndexSet;
use std::collections::VecDeque;
static BIN_TYPE: &str = "fifo";
static SINK_MIN_ITEMS: &str = "min_items";
static SINK_MAX_ITEMS: &str = "max_items";
static SOURCE_DELTA: &str = "delta";
static SOURCE_MIN: &str = "min";
static SOURCE_MAX: &str = "max";
#[derive(Debug)]
pub struct Bin {
scope: Scope,
source_input: Box<dyn FetchItem>,
source_min_items: Box<dyn FetchItem>,
source_max_items: Box<dyn FetchItem>,
fifo: VecDeque<Item>,
min: Option<R64>,
max: Option<R64>,
result_min: Item,
result_max: Item,
result_delta: Item,
}
impl SinkBin for Bin {}
impl SourceBin for Bin {
fn get_source_data(&self, source: &SourceId) -> Result<Item> {
if source.id == SOURCE_MIN {
Ok(self.result_min.clone())
} else if source.id == SOURCE_MAX {
Ok(self.result_max.clone())
} else if source.id == SOURCE_DELTA {
Ok(self.result_delta.clone())
} else {
error::MissingSourceName {
scope: self.scope.clone(),
name: source.id.to_string(),
bin_type: BIN_TYPE.to_string(),
}
.fail()
}
}
}
impl Calculator for Bin {
fn calculate(&mut self, _iteration: &Iteration) -> Result<()> {
let input = self.source_input.fetch_item(&self.scope)?;
let min_items = self.source_min_items.fetch_item(&self.scope)?;
let max_items = self.source_max_items.fetch_item(&self.scope)?;
let min_items = min_items.to_usize().unwrap_or(0usize);
let max_items = max_items.to_usize().unwrap_or(usize::max_value());
self.fifo.push_back(input.clone());
let dissed = {
let mut dissed = None;
while self.fifo.len() > max_items {
dissed = self.fifo.pop_front()
}
dissed
};
if let Some(d) = dissed {
if d.to_float().is_ok() {
self.min = None;
self.max = None;
for v in &self.fifo {
match (v.to_float(), self.min) {
(Ok(f), None) => self.min = Some(f),
(Ok(f), Some(m)) if f < m => self.min = Some(f),
_ => {}
}
match (v.to_float(), self.max) {
(Ok(f), None) => self.max = Some(f),
(Ok(f), Some(m)) if f > m => self.max = Some(f),
_ => {}
}
}
}
}
if self.fifo.len() >= min_items {
match (input.to_float(), self.min) {
(Ok(f), None) => self.min = Some(f),
(Ok(f), Some(m)) if f < m => self.min = Some(f),
_ => {}
}
match (input.to_float(), self.max) {
(Ok(f), None) => self.max = Some(f),
(Ok(f), Some(m)) if f > m => self.max = Some(f),
_ => {}
}
} else {
self.min = None;
self.max = None;
}
let (result_min, result_max, result_delta) =
match (self.min, self.max) {
(Some(min), Some(max)) => {
(Item::F64(min), Item::F64(max), Item::F64(max - min))
}
_ => (Item::Nothing, Item::Nothing, Item::Nothing),
};
self.result_min = result_min;
self.result_max = result_max;
self.result_delta = result_delta;
Ok(())
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Description;
impl BinDescription for Description {
type Bin = Bin;
fn check_validity(
&self,
_scope: &Scope,
_get_calibration: &mut dyn GetCalibration,
) -> Result<()> {
Ok(())
}
fn bin_type(&self) -> &'static str {
BIN_TYPE
}
}
impl SinkNames for Description {
fn sink_names(&self) -> IndexSet<String> {
build_names(&[SINK_INPUT, SINK_MIN_ITEMS, SINK_MAX_ITEMS])
}
}
impl SourceNames for Description {
fn source_names(&self) -> Result<IndexSet<String>> {
Ok(build_names(&[SOURCE_MIN, SOURCE_MAX, SOURCE_DELTA]))
}
}
impl SourceSinkBinDescription for Description {
fn build_bin(
&self,
scope: &Scope,
env: &mut dyn BinBuildEnvironment,
) -> Result<Self::Bin> {
Ok(Bin {
scope: scope.clone(),
source_input: env.resolve(SINK_INPUT)?,
source_min_items: env.resolve(SINK_MIN_ITEMS)?,
source_max_items: env.resolve(SINK_MAX_ITEMS)?,
fifo: VecDeque::new(),
min: None,
max: None,
result_min: Item::Nothing,
result_max: Item::Nothing,
result_delta: Item::Nothing,
})
}
}
impl WriteDotSimple for Description {}
#[cfg(test)]
mod tests {
use super::Description;
use crate::bins::{directsource, verificationsink};
use crate::Item as I;
use crate::{run_bin, Result};
use indexmap::indexset;
#[test]
fn simulate() -> Result<()> {
use crate::Item::*;
let input = directsource::Description {
columns: indexset![
"input".to_string(),
"min_items".to_string(),
"max_items".to_string(),
],
rows: vec![
vec![Nothing, Nothing, Nothing],
vec![Nothing, I::from(5f64), I::from(5f64)],
vec![Nothing, I::from(5f64), I::from(5f64)],
vec![I::from(1f64), I::from(5f64), I::from(5f64)],
vec![I::from(1f64), I::from(5f64), I::from(5f64)],
vec![I::from(0f64), I::from(5f64), I::from(5f64)],
vec![I::from(0f64), I::from(5f64), I::from(5f64)],
vec![I::from(0f64), I::from(5f64), I::from(5f64)],
vec![I::from(0f64), I::from(5f64), I::from(5f64)],
vec![I::from(0f64), I::from(5f64), I::from(5f64)],
vec![I::from(0f64), I::from(5f64), I::from(5f64)],
vec![I::from(1f64), I::from(5f64), I::from(5f64)],
vec![I::from(-1f64), I::from(5f64), I::from(5f64)],
vec![I::from(-1f64), I::from(5f64), I::from(5f64)],
vec![I::from(-1f64), I::from(5f64), I::from(5f64)],
vec![I::from(-1f64), I::from(5f64), I::from(5f64)],
vec![I::from(-1f64), I::from(5f64), I::from(5f64)],
vec![I::from(-1f64), I::from(5f64), I::from(5f64)],
vec![I::from(-1f64), I::from(5f64), I::from(5f64)],
vec![I::from(10f64), I::from(5f64), I::from(5f64)],
vec![I::from(-9f64), I::from(5f64), I::from(5f64)],
]
.into(),
};
let verification = verificationsink::Description {
columns: indexset![
"min".to_string(),
"max".to_string(),
"delta".to_string(),
],
expected: vec![
vec![Nothing, Nothing, Nothing],
vec![Nothing, Nothing, Nothing],
vec![Nothing, Nothing, Nothing],
vec![Nothing, Nothing, Nothing],
vec![I::from(1f64), I::from(1f64), I::from(0f64)],
vec![I::from(0f64), I::from(1f64), I::from(1f64)],
vec![I::from(0f64), I::from(1f64), I::from(1f64)],
vec![I::from(0f64), I::from(1f64), I::from(1f64)],
vec![I::from(0f64), I::from(1f64), I::from(1f64)],
vec![I::from(0f64), I::from(0f64), I::from(0f64)],
vec![I::from(0f64), I::from(0f64), I::from(0f64)],
vec![I::from(0f64), I::from(1f64), I::from(1f64)],
vec![I::from(-1f64), I::from(1f64), I::from(2f64)],
vec![I::from(-1f64), I::from(1f64), I::from(2f64)],
vec![I::from(-1f64), I::from(1f64), I::from(2f64)],
vec![I::from(-1f64), I::from(1f64), I::from(2f64)],
vec![I::from(-1f64), I::from(-1f64), I::from(0f64)],
vec![I::from(-1f64), I::from(-1f64), I::from(0f64)],
vec![I::from(-1f64), I::from(-1f64), I::from(0f64)],
vec![I::from(-1f64), I::from(10f64), I::from(11f64)],
vec![I::from(-9f64), I::from(10f64), I::from(19f64)],
]
.into(),
};
run_bin(&input, &Description {}, &verification)
}
}