#![allow(dead_code)]
use bigdecimal::BigDecimal;
use chrono::{DateTime, TimeDelta, Utc};
use splimes::{Point, Spline};
pub struct Rng(u64);
impl Rng {
pub fn new(seed: u64) -> Self {
Self(seed.wrapping_mul(0x9E37_79B9_7F4A_7C15) | 1)
}
pub fn next_u64(&mut self) -> u64 {
self.0 ^= self.0 >> 12;
self.0 ^= self.0 << 25;
self.0 ^= self.0 >> 27;
self.0.wrapping_mul(0x2545_F491_4F6C_DD1D)
}
pub fn unit(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 / (1u64 << 53) as f64
}
pub fn below(&mut self, n: u64) -> u64 {
self.next_u64() % n
}
}
pub fn epoch() -> DateTime<Utc> {
DateTime::<Utc>::UNIX_EPOCH + TimeDelta::days(20_000)
}
pub fn posix(t: DateTime<Utc>) -> i128 {
i128::from(t.timestamp()) * 1_000_000_000 + i128::from(t.timestamp_subsec_nanos())
}
pub fn nanos_between(a: DateTime<Utc>, b: DateTime<Utc>) -> i128 {
posix(a) - posix(b)
}
pub fn exact(points: &[Point], spline: Spline, t: DateTime<Utc>) -> BigDecimal {
exact_with_lebesgue(points, spline, t).0
}
pub fn exact_with_lebesgue(points: &[Point], spline: Spline, t: DateTime<Utc>) -> (BigDecimal, f64) {
let knots: Vec<Point> = distinct(points).into_iter().map(|p| Point::new(p.timestamp, BigDecimal::try_from(to_f64(&p.value)).expect("finite"))).collect();
let n = knots.len();
let spline = spline.fallback_for(n);
let x: Vec<BigDecimal> = knots.iter().map(|p| BigDecimal::from(nanos_between(p.timestamp, knots[0].timestamp))).collect();
let target = BigDecimal::from(nanos_between(t, knots[0].timestamp));
let below = target < x[0];
let above = target > x[n - 1];
if matches!(spline, Spline::Cubic) && (below || above) {
return (if below { knots[0].value.clone() } else { knots[n - 1].value.clone() }, 1.0);
}
let m = spline.min_points().min(n);
let p = x.iter().filter(|&xi| *xi <= target).count();
let start = (p as isize - (m / 2) as isize).clamp(0, (n - m) as isize) as usize;
let mut total = BigDecimal::from(0);
let mut lebesgue = BigDecimal::from(0);
for j in start..start + m {
let mut basis = BigDecimal::from(1);
for k in start..start + m {
if k != j {
basis = (basis * (&target - &x[k]) / (&x[j] - &x[k])).with_prec(60);
}
}
total += (&knots[j].value * &basis).with_prec(60);
lebesgue += basis.abs();
}
if let (Some(b), true) = (spline.bounds_factor(), below || above) {
let (min, max) = value_range(&knots);
let range = &max - &min;
let b: BigDecimal = b.to_string().parse().expect("finite bounds factor");
let lo = &min - &range * &b;
let hi = &max + &range * &b;
total = total.max(lo).min(hi);
}
(total, to_f64(&lebesgue).max(1.0))
}
pub fn distinct(points: &[Point]) -> Vec<Point> {
let mut sorted = points.to_vec();
sorted.sort_by_key(|p| posix(p.timestamp));
let mut out: Vec<Point> = Vec::with_capacity(sorted.len());
for p in sorted {
match out.last_mut() {
Some(last) if posix(last.timestamp) == posix(p.timestamp) => *last = p,
_ => out.push(p),
}
}
out
}
pub fn value_range(points: &[Point]) -> (BigDecimal, BigDecimal) {
let min = points.iter().map(|p| &p.value).min().expect("non-empty").clone();
let max = points.iter().map(|p| &p.value).max().expect("non-empty").clone();
(min, max)
}
pub fn to_f64(d: &BigDecimal) -> f64 {
d.to_string().parse().expect("decimal parses as f64")
}
pub fn gpu_or_skip(test: &str) -> Option<splimes::GpuInfo> {
match splimes::prewarm_gpu() {
Ok(info) => {
assert!(info.supports_f64 || std::env::var_os("SPLIMES_REQUIRE_GPU_F64").is_none(), "{test}: SPLIMES_REQUIRE_GPU_F64 is set but {} has no f64 support", info.name);
Some(info)
}
Err(e) if std::env::var_os("SPLIMES_REQUIRE_GPU").is_some() || std::env::var_os("SPLIMES_REQUIRE_GPU_F64").is_some() => panic!("{test}: SPLIMES_REQUIRE_GPU is set but the GPU is unavailable: {e}"),
Err(e) => {
eprintln!("{test}: skipped, {e}; set SPLIMES_REQUIRE_GPU=1 to make this a failure");
None
}
}
}
pub fn block_on<F: std::future::Future>(f: F) -> F::Output {
use std::{
sync::Arc, task::{Context, Poll, Wake}, thread::{self, Thread}
};
struct Unpark(Thread);
impl Wake for Unpark {
fn wake(self: Arc<Self>) {
self.0.unpark();
}
}
let waker = Arc::new(Unpark(thread::current())).into();
let mut cx = Context::from_waker(&waker);
let mut f = std::pin::pin!(f);
loop {
if let Poll::Ready(v) = f.as_mut().poll(&mut cx) {
return v;
}
thread::park();
}
}