Linear interpolation between surrounding real values.
(
target_ts: i64,
preceding: &[(i64, AggResult)],
existing: &std::collections::HashMap<i64, &AggResult>,
interval_ms: i64,
end_ms: i64,
)
| 126 | |
| 127 | /// Linear interpolation between surrounding real values. |
| 128 | fn linear_fill( |
| 129 | target_ts: i64, |
| 130 | preceding: &[(i64, AggResult)], |
| 131 | existing: &std::collections::HashMap<i64, &AggResult>, |
| 132 | interval_ms: i64, |
| 133 | end_ms: i64, |
| 134 | ) -> AggResult { |
| 135 | // Find last real value before target. |
| 136 | let prev = preceding.iter().rev().find(|(_, a)| a.count > 0); |
| 137 | |
| 138 | // Find next real value after target. |
| 139 | let mut next_ts = target_ts + interval_ms; |
| 140 | let mut next: Option<(i64, &AggResult)> = None; |
| 141 | while next_ts < end_ms { |
| 142 | if let Some(agg) = existing.get(&next_ts) { |
| 143 | next = Some((next_ts, agg)); |
| 144 | break; |
| 145 | } |
| 146 | next_ts += interval_ms; |
| 147 | } |
| 148 | |
| 149 | match (prev, next) { |
| 150 | (Some((prev_ts, prev_agg)), Some((next_ts, next_agg))) => { |
| 151 | let t = (target_ts - prev_ts) as f64 / (next_ts - prev_ts) as f64; |
| 152 | let avg_prev = if prev_agg.count > 0 { |
| 153 | prev_agg.sum / prev_agg.count as f64 |
| 154 | } else { |
| 155 | 0.0 |
| 156 | }; |
| 157 | let avg_next = if next_agg.count > 0 { |
| 158 | next_agg.sum / next_agg.count as f64 |
| 159 | } else { |
| 160 | 0.0 |
| 161 | }; |
| 162 | let interpolated = avg_prev + t * (avg_next - avg_prev); |
| 163 | literal_agg(interpolated) |
| 164 | } |
| 165 | (Some((_, prev_agg)), None) => { |
| 166 | // No next value: flat extrapolation from prev. |
| 167 | prev_agg.clone() |
| 168 | } |
| 169 | (None, Some((_, next_agg))) => { |
| 170 | // No prev value: flat extrapolation from next. |
| 171 | (*next_agg).clone() |
| 172 | } |
| 173 | (None, None) => null_agg(), |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | #[cfg(test)] |
| 178 | mod tests { |
no test coverage detected