use std::io::Write;
use anyhow::{Context, Result, bail};
use bigdecimal::FromPrimitive;
use chrono::{DateTime, Utc};
use rayon::prelude::*;
use crate::{
Error, POINT_SIZE, Point, Resolution, Spline, generate_target_times, helpers::{InterpolationState, batch}, splines::{cubic, cubic_simd, linear, linear_simd, polynomial, polynomial_simd, quadratic, quadratic_simd}
};
mod fast_path;
pub use fast_path::apply_fast_path;
pub async fn cpu_interpolate(points: &mut [Point], start: DateTime<Utc>, end: DateTime<Utc>, resolution: Resolution, spline: Spline) -> Result<Vec<Point>> {
let _input_count = points.len();
let _target_times = generate_target_times(start, end, resolution);
let _output_count = _target_times.len();
match spline {
Spline::Linear => linear(points, &start, &end, &resolution).await,
Spline::Quadratic => quadratic(points, &start, &end, &resolution).await,
Spline::Cubic => cubic(points, &start, &end, &resolution).await,
Spline::Polynomial(_, _) => polynomial(points, &start, &end, &resolution, &spline).await,
}
}
pub async fn parallel_interpolate(points: &mut [Point], start: &DateTime<Utc>, end: &DateTime<Utc>, spline: Spline, resolution: Resolution) -> Result<Vec<Point>> {
spline.pre_check(points, start, end)?;
batch(points, start, end, &spline, &resolution, |state| Box::pin(p_interpolate(state))).await
}
pub async fn p_interpolate(state: &mut InterpolationState) -> Result<()> {
let Some(input_points) = &state.input_points else {
bail!("No input points provided for interpolation");
};
let Some(batch_times) = &state.batch_times else {
bail!("No batch times provided for interpolation");
};
state.system.refresh_memory();
let available_memory = usize::from_u64(state.system.available_memory()).context("Failed to get available memory")?;
let batch_size = if available_memory < state.memory_threshold {
let max_points = (available_memory / POINT_SIZE).max(50);
batch_times.len().min(max_points).max(1)
} else {
batch_times.len()
};
let input_points_ref = input_points;
let batch_times_ref = batch_times;
let spline = state.spline;
let resolution = state.resolution;
let chunks: Vec<Vec<DateTime<Utc>>> = batch_times_ref.chunks(batch_size).map(<[chrono::DateTime<chrono::Utc>]>::to_vec).collect();
let chunk_results: Result<Vec<Vec<Point>>> = chunks
.par_iter()
.map(|times| match spline {
Spline::Linear => linear_simd(input_points_ref, times, resolution),
Spline::Quadratic => quadratic_simd(input_points_ref, times, resolution),
Spline::Cubic => cubic_simd(input_points_ref, times, resolution),
Spline::Polynomial(_, _) => polynomial_simd(input_points_ref, times, resolution, &spline),
})
.collect();
let chunk_results = chunk_results?;
let all_points: Vec<Point> = chunk_results.into_iter().flatten().collect();
assert_eq!(all_points.len(), batch_times_ref.len(), "Parallel processing lost points: expected {}, got {}", batch_times_ref.len(), all_points.len());
if state.temp_file.is_some() {
if let Some(writer) = state.temp_file.as_mut() {
let mut writer = writer.lock().await;
for point in all_points {
writeln!(writer, "{},{}", point.timestamp.to_rfc3339(), point.value)?;
}
writer.flush().map_err(|e| Error::IOError(e.to_string()))?;
}
} else {
state.result = Some(all_points);
}
Ok(())
}