use std::borrow::BorrowMut;
use crate::value::Value;
mod noise;
pub use noise::{ApplyNoise, NoiseTransform, Strength};
mod scale;
pub use scale::{ApplyScale, ScaleTransform};
mod normalization;
pub use normalization::{ApplyNormalization, NormalizationTransform};
pub use normalization::{ApplySmoothing, SmoothingTransform};
pub use rand;
pub use rand_distr;
pub trait Transform<T: Value> {
fn apply<'a>(&self, data: impl Iterator<Item = &'a mut T>);
fn apply_to(&self, point: &mut T) {
self.apply(std::iter::once(point));
}
}
impl<T: Value, R: ?Sized + Transform<T>> Transform<T> for &R {
fn apply<'a>(&self, data: impl Iterator<Item = &'a mut T>) {
<R as Transform<T>>::apply(*self, data);
}
}
pub trait Transformable<T: Value> {
fn transform<R: Transform<T>>(&mut self, transform: R);
#[must_use]
fn transformed<R: Transform<T>>(&self, transform: R) -> Self::Owned
where
Self: ToOwned,
Self::Owned: BorrowMut<Self>,
{
let mut new_data = self.to_owned();
new_data.borrow_mut().transform(transform);
new_data
}
}
impl<T: Value> Transformable<T> for [(T, T)] {
fn transform<R: Transform<T>>(&mut self, transform: R) {
transform.apply(self.iter_mut().map(|(_, y)| y));
}
}
thread_local! {
static SEED_VAULT: std::sync::Mutex<Vec<u64>> = const { std::sync::Mutex::new(Vec::new()) };
}
#[derive(Debug)]
pub struct SeedSource {
replay: Vec<u64>,
rng: rand::rngs::ThreadRng,
}
impl Default for SeedSource {
fn default() -> Self {
Self::new()
}
}
impl SeedSource {
#[must_use]
pub fn new() -> Self {
Self {
replay: Vec::new(),
rng: rand::rng(),
}
}
#[must_use]
pub fn from_seeds(seeds: impl Into<Vec<u64>>) -> Self {
Self {
replay: seeds.into(),
rng: rand::rng(),
}
}
pub fn reset() {
SEED_VAULT.with(|vault| {
let mut vault = vault.lock().expect("Failed to lock SEED_VAULT");
vault.clear();
});
}
#[must_use]
pub fn all_seeds() -> Vec<u64> {
SEED_VAULT.with(|vault| {
let vault = vault.lock().expect("Failed to lock SEED_VAULT");
vault.clone()
})
}
pub fn seed(&mut self) -> u64 {
let seed: u64 = if self.replay.is_empty() {
rand::Rng::random(&mut self.rng)
} else {
self.replay.remove(0)
};
SEED_VAULT.with(|vault| {
let mut vault = vault.lock().expect("Failed to lock SEED_VAULT");
vault.push(seed);
});
seed
}
#[must_use]
#[rustfmt::skip]
pub fn print_seeds() -> Option<String> {
use std::fmt::Write;
let mut out = String::new();
let seeds = Self::all_seeds();
if seeds.is_empty() {
return None;
}
let seeds = seeds
.iter()
.map(|s| format!("0x{s:x}"))
.collect::<Vec<_>>()
.join(", ");
writeln!(out, "Seeds used in this test thread: [{seeds}]").ok()?;
writeln!(out, "You can replay this test with:").ok()?;
writeln!(out, " let mut src = polyfit::transforms::SeedSource::from_seeds([{seeds}]);").ok()?;
writeln!(out, " data.apply_poisson_noise(Strength::Absolute(0.1), Some(src.seed()); // Poisson used for example").ok()?;
Some(out)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_transformed_slice_gives_vec() {
let points = [(-0.5, -4.0), (1.0, 3.0), (5.0, 4.0)];
let new_points: Vec<_> = points[1..].transformed(ScaleTransform::Quadratic(0.5));
assert_eq!(new_points, [(1.0, 4.5), (5.0, 8.0)]);
}
}