1#![feature(is_sorted)]
2#![feature(let_chains)]
3use chrono::{DateTime, Duration, Utc};
4use plotters::prelude::*;
5use serde::{Deserialize, Serialize};
6use std::{error::Error, ops::{Mul, Div, Add, Sub}};
7
8#[derive(Debug, Clone, Serialize, Deserialize, Default)]
27pub struct TimeFunc(pub Vec<(DateTime<Utc>, f64)>);
28
29impl From<Vec<(DateTime<Utc>, f64)>> for TimeFunc {
30 fn from(input: Vec<(DateTime<Utc>, f64)>) -> Self { TimeFunc(input) }
31}
32
33fn intersection(lhs: TimeFunc, rhs: TimeFunc, function: fn(f64, f64) -> f64 ) -> TimeFunc {
34 let lhs_domain = lhs.get_domain();
35 let rhs_domain = rhs.get_domain();
36
37 let output_start_time = DateTime::max(lhs_domain.start, rhs_domain.start);
38
39 let lhs_start_index = lhs.get_index_safe(&output_start_time);
40 let rhs_start_index = rhs.get_index_safe(&output_start_time);
41
42 let mut lhs_index = lhs_start_index;
43 let mut rhs_index = rhs_start_index;
44
45 let mut output = TimeFunc::new();
46
47 loop {
48 let lhs_tuple = lhs.0.get(lhs_index);
49 let rhs_tuple = rhs.0.get(rhs_index);
50
51 if let Some(lhs_tuple) = lhs_tuple
52 && let Some(rhs_tuple) = rhs_tuple {
53 if lhs_tuple.0 < rhs_tuple.0 {
54 let val = function(lhs_tuple.1, rhs.get_value_interpolated(&lhs_tuple.0));
55 output.push((lhs_tuple.0, val)).unwrap();
56 lhs_index += 1;
57 } else if lhs_tuple.0 > rhs_tuple.0 {
58 let val = function(lhs.get_value_interpolated(&rhs_tuple.0), rhs_tuple.1);
59 output.push((rhs_tuple.0, val)).unwrap();
60 rhs_index += 1;
61 } else { let val = function(lhs_tuple.1, rhs_tuple.1);
63 output.push((lhs_tuple.0, val)).unwrap();
64 lhs_index += 1;
65 rhs_index += 1;
66 }
67 } else { return output; }
68 }
69}
70
71impl Mul for TimeFunc {
72 type Output = Self;
73
74 fn mul(self, rhs: Self) -> Self::Output {
75 intersection(self, rhs, |x, y| {x*y})
76 }
77}
78
79impl Div for TimeFunc {
80 type Output = Self;
81
82 fn div(self, rhs: Self) -> Self::Output {
83 intersection(self, rhs, |x, y| {x/y})
84 }
85}
86
87impl Add for TimeFunc {
88 type Output = Self;
89
90 fn add(self, rhs: Self) -> Self::Output {
91 intersection(self, rhs, |x, y| {x+y})
92 }
93}
94
95impl Sub for TimeFunc {
96 type Output = Self;
97
98 fn sub(self, rhs: Self) -> Self::Output {
99 intersection(self, rhs, |x, y| {x-y})
100 }
101}
102
103impl TimeFunc {
104 pub fn new() -> Self { TimeFunc(vec![]) }
106
107 pub fn get_moving_average(&self, time: DateTime<Utc>, duration: Duration) -> f64 {
110 let start_index = self.get_index_safe(&(time - duration));
111 let end_index = self.get_index_safe(&time);
113 if start_index == end_index {
114 return self.0[end_index].1;
115 }
116 if end_index == 0 {
117 return self.0[0].1;
118 }
119 let mut sum = 0.0;
120 let mut total_duration = Duration::seconds(0);
121 for i in start_index..end_index {
122 let this = self.0[i];
123 let next = self.0[i + 1];
124 let duration = next.0 - this.0;
125 total_duration = total_duration + duration;
126 sum += this.1 * duration.num_seconds() as f64;
127 }
128 sum / total_duration.num_seconds() as f64
129 }
130
131 pub fn get_integral_interpolated(&self, time: DateTime<Utc>, duration: Duration) -> f64 {
134 let start_time = time - duration;
135 let start_point = (start_time, self.get_value_interpolated(&start_time));
136 let end_point = (time, self.get_value_interpolated(&time));
137 let start_index = self.get_index_above(&start_time);
138 let end_index = self.get_index_below(&time);
139 let mut current_index = start_index;
140 let mut integral = 0.0;
141 integral += get_integral(start_point, self.0[start_index]);
142 while current_index < end_index {
143 integral += get_integral(self.0[current_index], self.0[current_index + 1]);
144 current_index += 1;
145 }
146 integral += get_integral(self.0[end_index], end_point);
147 integral
148 }
149
150 pub fn get_average_interpolated(&self, time: DateTime<Utc>, duration: Duration) -> f64 {
152 let integral = self.get_integral_interpolated(time, duration);
153 integral / duration.num_seconds() as f64
154 }
155
156 pub fn get_rms(&self, duration: Duration, time: DateTime<Utc>) -> f64 {
159 let average = self.get_moving_average(time, duration);
160 let end_index = self.get_index_safe(&time);
161 let mut start_index = self.get_index_safe(&(time - duration));
162 if end_index < 2 {
163 return 0.0;
164 }
165 if end_index == start_index {
166 start_index = end_index - 1;
167 }
168 let mut total_duration = Duration::seconds(0);
169 let mut sum = 0.0;
170 for i in start_index..end_index {
171 let this = self.0[i];
172 let next = self.0[i + 1];
173 let duration = next.0 - this.0;
174 let relative_square = ((this.1 - average) / average).powf(2.0);
175 sum += relative_square * duration.num_seconds() as f64;
176 total_duration = total_duration + duration;
177 }
178 (sum / total_duration.num_seconds() as f64).sqrt()
179 }
180
181 pub fn get_rms_timefunc(&self, duration: Duration) -> Self {
182 let mut timefunc = TimeFunc::new();
183 for i in 1..self.0.len() {
184 let time = self.0[i].0;
185 timefunc.push((time, self.get_rms(duration, time))).unwrap();
186 }
187 timefunc
188 }
189
190 pub fn get_inflation(&self, duration: Duration, time: DateTime<Utc>) -> f64 {
191 let end_index = self.get_index_safe(&time);
192 if end_index == 0 {
193 return 0.0;
194 }
195 let mut start_index = self.get_index_safe(&(time - duration));
196 if end_index == start_index {
197 start_index = end_index - 1
198 }
199 let end = self.0[end_index];
200 let start = self.0[start_index];
201 let duration = end.0 - start.0;
202 ((start.1 / end.1) - 1.0) * (365 * 24 * 60 * 60) as f64 / duration.num_seconds() as f64
203 }
204
205 pub fn get_inflation_interpolated(&self, duration: Duration, time: DateTime<Utc>) -> f64 {
206 let start = self.get_value_interpolated(&(time - duration));
207 let end = self.get_value_interpolated(&time);
208 ((start / end) - 1.0) * (365 * 24 * 60 * 60) as f64 / duration.num_seconds() as f64
209 }
210
211 pub fn get_inflation_timefunc(&self, duration: Duration) -> Self {
212 let mut timefunc = TimeFunc::new();
213 for i in 1..self.0.len() {
214 let time = self.0[i].0;
215 timefunc
216 .push((time, self.get_inflation(duration, time)))
217 .unwrap();
218 }
219 timefunc
220 }
221
222 pub fn get_inflation_interpolated_timefunc(&self, duration: Duration) -> Self {
223 let mut timefunc = TimeFunc::new();
224 for i in 1..self.0.len() {
225 let time = self.0[i].0;
226 timefunc
227 .push((time, self.get_inflation_interpolated(duration, time)))
228 .unwrap();
229 }
230 timefunc
231 }
232
233 pub fn get_index(&self, time: &DateTime<Utc>) -> Result<usize, usize> {
234 self.0.binary_search_by(|probe| probe.0.cmp(time))
235 }
236
237 pub fn get_index_safe(&self, time: &DateTime<Utc>) -> usize {
238 match self.get_index(time) {
239 Ok(index) => index,
240 Err(index) => {
241 let len = self.0.len();
242 if index == len {
243 len - 1
244 } else {
245 index
246 }
247 }
248 }
249 }
250
251 pub fn get_index_above(&self, time: &DateTime<Utc>) -> usize {
252 match self.get_index(time) {
253 Ok(index) => index + 1,
254 Err(index) => index,
255 }
256 }
257
258 pub fn get_index_below(&self, time: &DateTime<Utc>) -> usize {
259 match self.get_index(time) {
260 Ok(index) => index - 1,
261 Err(index) => index - 1,
262 }
263 }
264
265 pub fn get_fractional_index(&self, time: &DateTime<Utc>) -> Result<f64, Box<dyn Error>> {
267 match self.get_index(time) {
268 Ok(index) => Ok(index as f64),
269 Err(index) => {
270 let len = self.0.len();
271 if index == len || index == 0 {
272 return Err("Index is out of bounds".into())
274 } else {
275 let lower_time = self.0[index - 1].0;
276 let upper_time = self.0[index].0;
277 let time_step = upper_time - lower_time;
278 let difference = *time - lower_time;
279 let fractional =
280 difference.num_seconds() as f64 / time_step.num_seconds() as f64;
281 let fractional_index = (index - 1) as f64 + fractional;
282 Ok(fractional_index)
283 }
284 }
285 }
286 }
287
288 pub fn get_value(&self, time: DateTime<Utc>) -> f64 {
289 let result = self.get_index(&time);
290 match result {
291 Ok(index) => self.0[index].1,
292 Err(index) => {
293 let len = self.0.len();
294 if index == len {
295 self.0.last().unwrap().1
296 } else {
297 self.0[index].1
298 }
299 }
300 }
301 }
302
303 pub fn get_value_interpolated(&self, time: &DateTime<Utc>) -> f64 {
304 let result = self.get_index(&time);
305 match result {
306 Ok(index) => self.0[index].1,
307 Err(index) => {
309 if index == 0 {
310 return self.0[0].1;
311 }
312 let len = self.0.len();
313 if index == len {
314 return self.0.last().unwrap().1;
315 }
316 let upper_index = index;
317 let lower_index = index - 1;
318 let upper_tuple = self.0[upper_index];
319 let lower_tuple = self.0[lower_index];
320 let slope = (upper_tuple.1 - lower_tuple.1)
321 / (upper_tuple.0 - lower_tuple.0).num_seconds() as f64;
322 slope * (time.to_owned() - lower_tuple.0).num_seconds() as f64 + lower_tuple.1
323 }
324 }
325 }
326
327 fn get_range(&self) -> std::ops::Range<f64> {
328 let mut max = self.0[0].1;
329 let mut min = self.0[0].1;
330 for i in 1..self.0.len() {
331 let val = self.0[i].1;
332 if val > max {
333 max = val
334 } else if val < min {
335 min = val
336 }
337 }
338 min..max
339 }
340
341 fn get_domain(&self) -> std::ops::Range<DateTime<Utc>> {
342 let first = self.0[0].0;
343 let last = self.0.last().unwrap().0;
344 first..last
345 }
346
347 pub fn push(&mut self, point: (DateTime<Utc>, f64)) -> Result<(), Box<dyn Error>> {
348 match self.0.last() {
349 Some(last) => {
350 if last.0 >= point.0 {
351 return Err("Attempting to add point to TimeFunc that is out of order.".into());
352 }
353 }
354 None => {}
355 }
356 self.0.push(point);
357 Ok(())
358 }
359
360 pub fn verify(&self) -> Result<(), Box<dyn Error>> {
363 if self.0.is_sorted_by(|a, b| Some(a.0.cmp(&b.0))) && self.is_deduped() {
364 Ok(())
365 } else {
366 Err("TimeFunc is invalid.".into())
367 }
368 }
369
370 pub fn dedup(&mut self) { self.0.dedup_by(|a, b| a.0.eq(&b.0)); }
373
374 pub fn is_deduped(&self) -> bool {
377 for i in 1..self.0.len() {
378 if self.0[i].0 == self.0[i - 1].0 {
379 return false;
380 }
381 }
382 true
383 }
384
385 pub fn repair(&mut self) {
387 self.0.sort_unstable_by(|a, b| a.0.cmp(&b.0));
388 self.dedup();
389 }
390
391 pub fn draw(&self, title: String) -> Result<(), Box<dyn std::error::Error>> {
392 let filename = format!("images/{}.png", title);
393 let style = ("sans-serif", 25).into_font();
394 let root = BitMapBackend::new(&filename, (1200, 800)).into_drawing_area();
395 root.fill(&WHITE)?;
396
397 let mut chart = ChartBuilder::on(&root)
398 .caption(title, ("sans-serif", 50).into_font())
399 .margin(10)
400 .x_label_area_size(60)
401 .y_label_area_size(80)
402 .right_y_label_area_size(80)
403 .build_cartesian_2d(self.get_domain().yearly(), self.get_range())?;
404
405 chart
406 .configure_mesh()
407 .x_label_style(style.clone())
408 .y_label_style(style)
409 .draw()?;
410
411 chart.draw_series(LineSeries::new(self.0.clone(), &BLUE))?;
412
413 Ok(())
414 }
415}
416
417fn get_integral(a: (DateTime<Utc>, f64), b: (DateTime<Utc>, f64)) -> f64 {
420 let duration = b.0 - a.0;
421 let average = get_average(b.1, a.1);
422 duration.num_seconds() as f64 * average
423}
424
425fn get_average(a: f64, b: f64) -> f64 { (a + b) / 2.0 }
427
428#[cfg(test)]
429mod tests {
430
431 use super::*;
432
433 #[test]
434 fn test_get_moving_average() {
435 let mut time_func = TimeFunc::new();
436 let start_time = Utc::now();
437 let time_step = Duration::seconds(60);
438 time_func.push((start_time, 1.0)).unwrap();
439 time_func.push((start_time + time_step, 1.0)).unwrap();
440 time_func.push((start_time + time_step * 2, 1.0)).unwrap();
441
442 let av = time_func.get_moving_average(start_time + time_step * 2, time_step * 2);
443 assert_eq!(av, 1.0)
444 }
445
446 #[test]
447 fn test_get_fractional_index() {
448 let mut time_func = TimeFunc::new();
449 let start_time = Utc::now();
450 let time_step = Duration::seconds(60);
451 time_func.push((start_time, 1.0)).unwrap();
452 time_func.push((start_time + time_step, 1.0)).unwrap();
453 time_func.push((start_time + time_step * 2, 1.0)).unwrap();
454
455 let fractional_index = time_func
456 .get_fractional_index(&(start_time + time_step / 2))
457 .unwrap();
458 assert_eq!(fractional_index, 0.5);
459 let fractional_index = time_func.get_fractional_index(&(start_time - time_step / 2));
460 assert!(fractional_index.is_err());
461 }
462
463 #[test]
464 fn test_get_value_interpolated() {
465 let mut time_func = TimeFunc::new();
466 let start_time = Utc::now();
467 let time_step = Duration::seconds(60);
468 time_func.push((start_time, 1.0)).unwrap();
469 time_func.push((start_time + time_step, 2.0)).unwrap();
470 time_func.push((start_time + time_step * 2, 1.0)).unwrap();
471
472 let value = time_func.get_value_interpolated(&(start_time + time_step / 2));
473 assert_eq!(value, 1.5);
474 }
475
476 #[test]
477 fn test_get_integral_interpolated() {
478 let mut time_func = TimeFunc::new();
479 let start_time = Utc::now();
480 let time_step = Duration::seconds(60);
481 time_func.push((start_time, 1.0)).unwrap();
482 time_func.push((start_time + time_step, 2.0)).unwrap();
483 time_func.push((start_time + time_step * 2, 1.0)).unwrap();
484
485 let integral =
486 time_func.get_integral_interpolated(start_time + time_step * 2, time_step * 2);
487 assert_eq!(integral, 1.5 * 60.0 + 1.5 * 60.0);
488 }
489}