use std::time::Instant;
pub mod aggregate;
pub mod g;
mod item;
pub mod space_saving;
pub use item::Item;
use crate::g::Function;
#[derive(Copy, Clone, Debug)]
pub struct ForwardDecay<G> {
landmark: Instant,
g: G,
}
impl<G> ForwardDecay<G>
where
G: Function,
{
pub fn new(landmark: Instant, g: G) -> Self {
Self {
landmark,
g,
}
}
pub fn g(&self) -> &G {
&self.g
}
pub fn landmark(&self) -> Instant {
self.landmark
}
pub fn set_landmark(&mut self, landmark: Instant) -> f64 {
let age = landmark.age(self.landmark);
self.landmark = landmark;
age
}
pub fn weight<I>(&self, item: I, timestamp: Instant) -> f64
where
I: Item,
{
self.g.invoke(item.age(self.landmark)) / self.g.invoke(timestamp.age(self.landmark))
}
pub fn weighted_value<I>(&self, item: I, timestamp: Instant) -> f64
where
I: Item,
{
self.weight(&item, timestamp) * item.value()
}
pub fn static_weight<I>(&self, item: I) -> f64
where
I: Item,
{
self.g.invoke(item.age(self.landmark))
}
pub fn static_weighted_value<I>(&self, item: I) -> f64
where
I: Item,
{
self.g.invoke(item.age(self.landmark)) * item.value()
}
pub fn normalizing_factor(&self, timestamp: Instant) -> f64
{
self.g.invoke(timestamp.age(self.landmark))
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::*;
#[test]
fn example() {
let landmark = Instant::now();
let stream = vec![5, 7, 3, 8, 4];
let fd = ForwardDecay::new(landmark, g::Polynomial::new(2));
let now = landmark + Duration::from_secs(10);
let result: Vec<f64> = stream.into_iter()
.map(|i| landmark + Duration::from_secs(i))
.map(|i| fd.weight(i, now))
.collect();
let weights = vec![0.25, 0.49, 0.09, 0.64, 0.16];
assert_eq!(result, weights);
}
#[test]
fn scaled_exponential() {
let landmark = Instant::now();
let tick = Duration::from_secs(1);
let new_landmark = landmark + tick;
let stream = vec![5, 7, 3, 8, 4];
let alpha = 1.0;
let mut fd = ForwardDecay::new(landmark, g::Exponential::new(alpha));
let previous_weights: Vec<f64> = stream.iter()
.map(|i| landmark + Duration::from_secs(*i))
.map(|i| fd.static_weight(i))
.collect();
let age = fd.set_landmark(new_landmark);
let factor = fd.g().invoke(-age);
let new_weights: Vec<f64> = stream.iter()
.map(|i| landmark + Duration::from_secs(*i))
.map(|i| fd.static_weight(i))
.collect();
let factors: Vec<f64> = new_weights.iter().zip(previous_weights).map(|(a, b)| ((a / b) - factor).abs()).collect();
let epsilon = 0.001;
assert_eq!(age, tick.as_secs_f64());
assert!(factors.iter().all(|d| *d < epsilon));
}
#[test]
fn age() {
let landmark = Instant::now();
assert_eq!((landmark - Duration::from_secs(1)).age(landmark), -1.0);
assert_eq!(landmark.age(landmark), 0.0);
assert_eq!((landmark + Duration::from_secs(5)).age(landmark), 5.0);
assert_eq!((landmark + Duration::from_secs(10)).age(landmark), 10.0);
}
}