use std::time::Duration;
pub trait Function {
fn invoke(&self, age: f64) -> f64;
}
impl Function for () {
fn invoke(&self, _: f64) -> f64 {
1.0
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Exponential(f64);
impl Exponential {
pub fn new(alpha: f64) -> Self {
if !(alpha > 0.0) {
panic!("alpha must be greater than 0, given {alpha}");
}
Self(alpha)
}
pub fn rate(target: f64, duration: Duration) -> Self {
if !(target > 0.0 && target < 1.0) {
panic!("target must in the range (0, 1), given {target}");
}
Self(-target.ln() / duration.as_secs_f64())
}
}
impl Function for Exponential {
fn invoke(&self, age: f64) -> f64 {
(self.0 * age).exp()
}
}
#[derive(Copy, Clone)]
pub struct Polynomial(i32);
impl Polynomial {
pub fn new(beta: i32) -> Self {
if !(beta > 0) {
panic!("beta must be greater than 0, given {beta}");
}
Self(beta)
}
}
impl Function for Polynomial {
fn invoke(&self, age: f64) -> f64 {
age.powi(self.0)
}
}
#[derive(Copy, Clone)]
pub struct LandmarkWindow;
impl Function for LandmarkWindow {
fn invoke(&self, age: f64) -> f64 {
if age > 0.0 {
1.0
} else {
0.0
}
}
}
#[derive(Copy, Clone)]
pub struct Custom<F>(F);
impl<F> From<F> for Custom<F> where F: Fn(f64) -> f64 {
fn from(f: F) -> Self {
Self(f)
}
}
impl<F> Custom<F> where F: Fn(f64) -> f64 {
pub fn new(f: F) -> Self {
Self(f)
}
}
impl<F> Function for Custom<F> where F: Fn(f64) -> f64 {
fn invoke(&self, age: f64) -> f64 {
self.0(age)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_decay() {
assert_eq!(().invoke(1.0), 1.0);
assert_eq!(().invoke(0.0), 1.0);
assert_eq!(().invoke(-1.0), 1.0);
}
#[test]
fn exponential() {
assert_eq!(Exponential::new(1.0).invoke(1.0), 1.0_f64.exp());
assert_eq!(Exponential::rate(0.0001, Duration::from_secs(60)), Exponential::new(0.1535056728662697));
}
#[test]
#[should_panic]
fn negative_exponential() {
Exponential::new(-1.0);
}
#[test]
#[should_panic]
fn zero_exponential() {
Exponential::new(0.0);
}
#[test]
fn polynomial() {
assert_eq!(Polynomial::new(3).invoke(2.0), 8.0);
}
#[test]
#[should_panic]
fn negative_polynomial() {
Polynomial::new(-3);
}
#[test]
#[should_panic]
fn zero_polynomial() {
Polynomial::new(0);
}
#[test]
fn landmark() {
assert_eq!(LandmarkWindow.invoke(1.0), 1.0);
assert_eq!(LandmarkWindow.invoke(0.0), 0.0);
assert_eq!(LandmarkWindow.invoke(-1.0), 0.0);
}
#[test]
fn custom() {
assert_eq!(Custom::from(|n| n * 0.2).invoke(1.0), 0.2);
assert_eq!(Custom::from(|n| n * 0.2).invoke(0.0), 0.0);
assert_eq!(Custom::from(|n| n * 0.2).invoke(-1.0), -0.2);
}
}