use crate::ExpirationDate;
use crate::error::SimulationError;
use crate::simulation::steps::{Xstep, Ystep};
use crate::utils::TimeFrame;
use num_traits::FromPrimitive;
use positive::Positive;
use rust_decimal::Decimal;
use serde::ser::SerializeStruct;
use serde::{Serialize, Serializer};
use std::convert::TryInto;
use std::fmt::{Display, Formatter};
use std::ops::AddAssign;
#[derive(Debug, Clone)]
pub struct Step<X, Y>
where
X: Copy + TryInto<Positive> + AddAssign + Display,
Y: TryInto<Positive> + Display + Clone,
{
pub x: Xstep<X>,
pub y: Ystep<Y>,
}
impl<X, Y> Step<X, Y>
where
X: Copy + TryInto<Positive> + AddAssign + Display,
Y: TryInto<Positive> + Display + Clone,
{
pub fn new(x_value: X, time_unit: TimeFrame, datetime: ExpirationDate, y_value: Y) -> Self {
Self {
x: Xstep::new(x_value, time_unit, datetime),
y: Ystep::new(0, y_value),
}
}
pub fn next(&self, new_y_value: Y) -> Result<Self, SimulationError> {
let next_x = match self.x.next() {
Ok(x_step) => x_step,
Err(e) => {
return Err(format!(
"Cannot generate next step. Expiration date is already reached: {e}"
)
.into());
}
};
Ok(Self {
x: next_x,
y: Ystep::new(self.y.index() + 1, new_y_value),
})
}
pub fn previous(&self, new_y_value: Y) -> Result<Self, SimulationError> {
let previous_x = match self.x.previous() {
Ok(x_step) => x_step,
Err(e) => {
return Err(format!(
"Cannot generate previous step. Expiration date is already reached: {e}"
)
.into());
}
};
Ok(Self {
x: previous_x,
y: Ystep::new(self.y.index() - 1, new_y_value),
})
}
pub fn get_graph_x_value(&self) -> Result<Decimal, SimulationError> {
match Decimal::from_i32(*self.x.index()) {
Some(x) => Ok(x),
None => Err("Cannot convert x-axis index to decimal".into()),
}
}
pub fn get_graph_x_in_days_left(&self) -> Positive {
self.x.days_left().unwrap()
}
pub fn get_graph_y_value(&self) -> Result<Positive, SimulationError> {
self.y.positive()
}
pub fn get_y_step(&self) -> &Ystep<Y> {
&self.y
}
pub fn get_x_step(&self) -> &Xstep<X> {
&self.x
}
pub fn get_value(&self) -> &Y {
self.y.value()
}
pub fn get_index(&self) -> &X {
self.x.step_size_in_time()
}
pub fn get_positive_value(&self) -> Result<Positive, SimulationError> {
self.y.positive()
}
}
impl<X, Y> Display for Step<X, Y>
where
X: Copy + TryInto<Positive> + AddAssign + Display,
Y: TryInto<Positive> + Display + Clone,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Step {{ x: {}, y: {} }}", self.x, self.y)
}
}
impl<X, Y> Serialize for Step<X, Y>
where
X: Copy + TryInto<Positive> + AddAssign + Display + Serialize,
Y: TryInto<Positive> + Display + Serialize + Clone,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("Step", 2)?;
state.serialize_field("x", &self.x)?;
state.serialize_field("y", &self.y)?;
state.end()
}
}
#[cfg(test)]
mod tests {
use super::*;
use positive::pos_or_panic;
#[derive(Debug, Copy, Clone, PartialEq)]
struct TestValue(u32);
impl From<TestValue> for Positive {
fn from(val: TestValue) -> Self {
Positive::new(val.0 as f64).unwrap()
}
}
impl AddAssign for TestValue {
fn add_assign(&mut self, other: Self) {
self.0 += other.0;
}
}
impl Display for TestValue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[test]
fn test_step_new() {
let value = TestValue(5);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let step = Xstep::new(value, time_unit, datetime);
assert_eq!(*step.index(), 0);
assert_eq!(step.step_size_in_time(), &TestValue(5));
assert_eq!(*step.time_unit(), TimeFrame::Day);
assert_eq!(*step.datetime(), ExpirationDate::Days(pos_or_panic!(30.0)));
}
#[test]
#[should_panic(expected = "ExpirationDate::DateTime is not supported")]
fn test_step_new_with_datetime_should_panic() {
use chrono::{Duration, Utc};
let value = TestValue(5);
let time_unit = TimeFrame::Day;
let dt = Utc::now() + Duration::days(30);
let datetime = ExpirationDate::DateTime(dt);
let _step = Xstep::new(value, time_unit, datetime);
}
#[test]
fn test_step_next() {
let value = TestValue(5);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let step = Xstep::new(value, time_unit, datetime);
let next_step = step.next().unwrap();
assert_eq!(*step.index(), 0);
assert_eq!(step.step_size_in_time(), &TestValue(5));
assert_eq!(*step.time_unit(), TimeFrame::Day);
assert_eq!(*step.datetime(), ExpirationDate::Days(pos_or_panic!(30.0)));
assert_eq!(*next_step.index(), 1);
assert_eq!(next_step.step_size_in_time(), &TestValue(5));
assert_eq!(*next_step.time_unit(), TimeFrame::Day);
assert_eq!(
*next_step.datetime(),
ExpirationDate::Days(pos_or_panic!(25.0))
);
}
#[test]
fn test_step_next_with_weeks() {
let value = TestValue(2);
let time_unit = TimeFrame::Week;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let step = Xstep::new(value, time_unit, datetime);
let next_step = step.next().unwrap();
assert_eq!(*step.index(), 0);
assert_eq!(step.step_size_in_time(), &TestValue(2));
assert_eq!(*step.time_unit(), TimeFrame::Week);
assert_eq!(*step.datetime(), ExpirationDate::Days(pos_or_panic!(30.0)));
assert_eq!(*next_step.index(), 1);
assert_eq!(next_step.step_size_in_time(), &TestValue(2));
assert_eq!(*next_step.time_unit(), TimeFrame::Week);
assert_eq!(
*next_step.datetime(),
ExpirationDate::Days(pos_or_panic!(16.0))
);
}
#[test]
fn test_step_previous() {
let value = TestValue(5);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let step = Xstep::new(value, time_unit, datetime);
let prev_step = step.previous().unwrap();
assert_eq!(*step.index(), 0);
assert_eq!(step.step_size_in_time(), &TestValue(5));
assert_eq!(*step.time_unit(), TimeFrame::Day);
assert_eq!(*step.datetime(), ExpirationDate::Days(pos_or_panic!(30.0)));
assert_eq!(*prev_step.index(), -1);
assert_eq!(prev_step.step_size_in_time(), &TestValue(5));
assert_eq!(*prev_step.time_unit(), TimeFrame::Day);
assert_eq!(
*prev_step.datetime(),
ExpirationDate::Days(pos_or_panic!(35.0))
);
}
#[test]
fn test_step_previouse_with_months() {
let value = TestValue(3);
let time_unit = TimeFrame::Month;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let step = Xstep::new(value, time_unit, datetime);
let prev_step = step.previous().unwrap();
assert_eq!(*step.index(), 0);
assert_eq!(step.step_size_in_time(), &TestValue(3));
assert_eq!(*step.time_unit(), TimeFrame::Month);
assert_eq!(*step.datetime(), ExpirationDate::Days(pos_or_panic!(30.0)));
assert_eq!(*prev_step.index(), -1);
assert_eq!(prev_step.step_size_in_time(), &TestValue(3));
assert_eq!(*prev_step.time_unit(), TimeFrame::Month);
assert_eq!(
*prev_step.datetime(),
ExpirationDate::Days(pos_or_panic!(121.25))
);
}
#[test]
fn test_multiple_steps() {
let value = TestValue(10);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(Positive::HUNDRED);
let step = Xstep::new(value, time_unit, datetime);
let step1 = step.next().unwrap();
let step2 = step1.next().unwrap();
let step3 = step2.next().unwrap();
assert_eq!(*step1.index(), 1);
assert_eq!(*step1.datetime(), ExpirationDate::Days(pos_or_panic!(90.0)));
assert_eq!(*step2.index(), 2);
assert_eq!(*step2.datetime(), ExpirationDate::Days(pos_or_panic!(80.0)));
assert_eq!(*step3.index(), 3);
assert_eq!(*step3.datetime(), ExpirationDate::Days(pos_or_panic!(70.0)));
}
#[test]
fn test_forward_and_backward() {
let value = TestValue(5);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(50.0));
let original = Xstep::new(value, time_unit, datetime);
let forward = original.next().unwrap();
let back_to_original = forward.previous().unwrap();
assert_eq!(*original.index(), 0);
assert_eq!(*forward.index(), 1);
assert_eq!(*back_to_original.index(), 0);
assert_eq!(
*original.datetime(),
ExpirationDate::Days(pos_or_panic!(50.0))
);
assert_eq!(
*forward.datetime(),
ExpirationDate::Days(pos_or_panic!(45.0))
);
assert_eq!(
*back_to_original.datetime(),
ExpirationDate::Days(pos_or_panic!(50.0))
);
}
}
#[cfg(test)]
mod tests_positive {
use super::*;
use positive::pos_or_panic;
#[test]
fn test_step_new() {
let value = pos_or_panic!(5.0);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let step = Xstep::new(value, time_unit, datetime);
assert_eq!(*step.index(), 0);
assert_eq!(*step.step_size_in_time(), pos_or_panic!(5.0));
assert_eq!(*step.time_unit(), TimeFrame::Day);
assert_eq!(*step.datetime(), ExpirationDate::Days(pos_or_panic!(30.0)));
}
#[test]
#[should_panic(expected = "ExpirationDate::DateTime is not supported")]
fn test_step_new_with_datetime_should_panic() {
use chrono::{Duration, Utc};
let value = pos_or_panic!(5.0);
let time_unit = TimeFrame::Day;
let dt = Utc::now() + Duration::days(30);
let datetime = ExpirationDate::DateTime(dt);
let _step = Xstep::new(value, time_unit, datetime);
}
#[test]
fn test_step_next() {
let value = pos_or_panic!(5.0);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let step = Xstep::new(value, time_unit, datetime);
let next_step = step.next().unwrap();
assert_eq!(*next_step.index(), 1);
assert_eq!(*next_step.step_size_in_time(), pos_or_panic!(5.0));
assert_eq!(*next_step.time_unit(), TimeFrame::Day);
assert_eq!(
*next_step.datetime(),
ExpirationDate::Days(pos_or_panic!(25.0))
);
}
#[test]
fn test_step_next_with_weeks() {
let value = Positive::TWO;
let time_unit = TimeFrame::Week;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let step = Xstep::new(value, time_unit, datetime);
let next_step = step.next().unwrap();
assert_eq!(*next_step.index(), 1);
assert_eq!(*next_step.step_size_in_time(), Positive::TWO);
assert_eq!(*next_step.time_unit(), TimeFrame::Week);
assert_eq!(
*next_step.datetime(),
ExpirationDate::Days(pos_or_panic!(16.0))
);
}
#[test]
fn test_step_previous() {
let value = pos_or_panic!(5.0);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let step = Xstep::new(value, time_unit, datetime);
let prev_step = step.previous().unwrap();
assert_eq!(*prev_step.index(), -1);
assert_eq!(*prev_step.step_size_in_time(), pos_or_panic!(5.0));
assert_eq!(*prev_step.time_unit(), TimeFrame::Day);
assert_eq!(
*prev_step.datetime(),
ExpirationDate::Days(pos_or_panic!(35.0))
);
}
#[test]
fn test_step_previouse_with_months() {
let value = pos_or_panic!(3.0);
let time_unit = TimeFrame::Month;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let step = Xstep::new(value, time_unit, datetime);
let prev_step = step.previous().unwrap();
assert_eq!(*prev_step.index(), -1);
assert_eq!(*prev_step.step_size_in_time(), pos_or_panic!(3.0));
assert_eq!(*prev_step.time_unit(), TimeFrame::Month);
assert_eq!(
*prev_step.datetime(),
ExpirationDate::Days(pos_or_panic!(121.25))
);
}
#[test]
fn test_multiple_steps() {
let value = Positive::TEN;
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(Positive::HUNDRED);
let step = Xstep::new(value, time_unit, datetime);
let step1 = step.next().unwrap();
let step2 = step1.next().unwrap();
let step3 = step2.next().unwrap();
assert_eq!(*step1.index(), 1);
assert_eq!(*step1.datetime(), ExpirationDate::Days(pos_or_panic!(90.0)));
assert_eq!(*step2.index(), 2);
assert_eq!(*step2.datetime(), ExpirationDate::Days(pos_or_panic!(80.0)));
assert_eq!(*step3.index(), 3);
assert_eq!(*step3.datetime(), ExpirationDate::Days(pos_or_panic!(70.0)));
}
#[test]
fn test_forward_and_backward() {
let value = pos_or_panic!(5.0);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(50.0));
let original = Xstep::new(value, time_unit, datetime);
let forward = original.next().unwrap();
let back_to_original = forward.previous().unwrap();
assert_eq!(*original.index(), 0);
assert_eq!(*forward.index(), 1);
assert_eq!(*back_to_original.index(), 0);
assert_eq!(
*original.datetime(),
ExpirationDate::Days(pos_or_panic!(50.0))
);
assert_eq!(
*forward.datetime(),
ExpirationDate::Days(pos_or_panic!(45.0))
);
assert_eq!(
*back_to_original.datetime(),
ExpirationDate::Days(pos_or_panic!(50.0))
);
}
}
#[cfg(test)]
mod tests_step {
use super::*;
use positive::pos_or_panic;
#[derive(Debug, Copy, Clone, PartialEq)]
struct TestValue(u32);
impl From<TestValue> for Positive {
fn from(val: TestValue) -> Self {
Positive::new(val.0 as f64).unwrap()
}
}
impl AddAssign for TestValue {
fn add_assign(&mut self, other: Self) {
self.0 += other.0;
}
}
impl Display for TestValue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[test]
fn test_step_new() {
let x_value = TestValue(5);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let y_value = 42.5;
let step = Step::new(x_value, time_unit, datetime, y_value);
assert_eq!(*step.x.index(), 0);
assert_eq!(step.x.step_size_in_time(), &TestValue(5));
assert_eq!(*step.x.time_unit(), TimeFrame::Day);
assert_eq!(
*step.x.datetime(),
ExpirationDate::Days(pos_or_panic!(30.0))
);
assert_eq!(*step.y.index(), 0);
assert_eq!(*step.y.value(), 42.5);
let result_next = step.next(100.0);
assert!(result_next.is_ok());
let next = result_next.unwrap();
assert!(
next.to_string()
.contains("Step { x: Xstep { index: 1, value: 5, time_unit: Day, datetime:")
);
let previous_next = step.previous(100.0);
assert!(previous_next.is_ok());
let previous = previous_next.unwrap();
assert!(
previous
.to_string()
.contains("Step { x: Xstep { index: -1, value: 5, time_unit: Day, datetime:")
);
assert_eq!(next.get_graph_x_value().unwrap(), Decimal::ONE);
assert_eq!(previous.get_graph_x_value().unwrap(), Decimal::NEGATIVE_ONE);
}
#[test]
fn test_step_next() {
let x_value = TestValue(5);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let y_value = 42.5;
let step = Step::new(x_value, time_unit, datetime, y_value);
let new_y_value = 45.0;
let next_step = step.next(new_y_value).unwrap();
assert_eq!(*step.x.index(), 0);
assert_eq!(step.x.step_size_in_time(), &TestValue(5));
assert_eq!(*step.x.time_unit(), TimeFrame::Day);
assert_eq!(
*step.x.datetime(),
ExpirationDate::Days(pos_or_panic!(30.0))
);
assert_eq!(*step.y.index(), 0);
assert_eq!(*step.y.value(), 42.5);
assert_eq!(*next_step.x.index(), 1);
assert_eq!(next_step.x.step_size_in_time(), &TestValue(5));
assert_eq!(*next_step.x.time_unit(), TimeFrame::Day);
assert_eq!(
*next_step.x.datetime(),
ExpirationDate::Days(pos_or_panic!(25.0))
);
assert_eq!(*next_step.y.index(), 1);
assert_eq!(*next_step.y.value(), 45.0);
}
#[test]
fn test_step_previous() {
let x_value = TestValue(5);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let y_value = 42.5;
let step = Step::new(x_value, time_unit, datetime, y_value);
let new_y_value = 38.0;
let prev_step = step.previous(new_y_value).unwrap();
assert_eq!(*step.x.index(), 0);
assert_eq!(step.x.step_size_in_time(), &TestValue(5));
assert_eq!(*step.x.time_unit(), TimeFrame::Day);
assert_eq!(
*step.x.datetime(),
ExpirationDate::Days(pos_or_panic!(30.0))
);
assert_eq!(*prev_step.y.value(), 38.0);
}
#[test]
fn test_step_with_timeframe_conversion() {
let x_value = TestValue(2);
let time_unit = TimeFrame::Week;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let y_value = 100.0;
let step = Step::new(x_value, time_unit, datetime, y_value);
let next_y = 105.0;
let next_step = step.next(next_y).unwrap();
assert_eq!(
*next_step.x.datetime(),
ExpirationDate::Days(pos_or_panic!(16.0))
);
assert_eq!(*next_step.y.value(), 105.0);
let prev_y = 95.0;
let prev_step = step.previous(prev_y).unwrap();
assert_eq!(
*prev_step.x.datetime(),
ExpirationDate::Days(pos_or_panic!(44.0))
);
assert_eq!(*prev_step.y.value(), 95.0);
}
#[test]
fn test_step_chain() {
let x_value = TestValue(10);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(Positive::HUNDRED);
let y_value = 50.0;
let initial = Step::new(x_value, time_unit, datetime, y_value);
let step1 = initial.next(55.0).unwrap();
let step2 = step1.next(60.0).unwrap();
let step3 = step2.next(65.0).unwrap();
assert_eq!(*step1.x.index(), 1);
assert_eq!(
*step1.x.datetime(),
ExpirationDate::Days(pos_or_panic!(90.0))
);
assert_eq!(*step1.y.index(), 1);
assert_eq!(*step1.y.value(), 55.0);
assert_eq!(*step2.x.index(), 2);
assert_eq!(
*step2.x.datetime(),
ExpirationDate::Days(pos_or_panic!(80.0))
);
assert_eq!(*step2.y.index(), 2);
assert_eq!(*step2.y.value(), 60.0);
assert_eq!(*step3.x.index(), 3);
assert_eq!(
*step3.x.datetime(),
ExpirationDate::Days(pos_or_panic!(70.0))
);
assert_eq!(*step3.y.index(), 3);
assert_eq!(*step3.y.value(), 65.0);
}
#[test]
fn test_with_positive_type() {
let x_value = pos_or_panic!(5.0);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let y_value = pos_or_panic!(50.0);
let step = Step::new(x_value, time_unit, datetime, y_value);
let next_step = step.next(pos_or_panic!(55.0)).unwrap();
assert_eq!(
*next_step.x.datetime(),
ExpirationDate::Days(pos_or_panic!(25.0))
);
assert_eq!(*next_step.y.value(), pos_or_panic!(55.0));
}
#[test]
fn test_with_zero_days() {
let x_value = pos_or_panic!(5.0);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(Positive::ZERO);
let y_value = pos_or_panic!(50.0);
let step = Step::new(x_value, time_unit, datetime, y_value);
let result = step.next(pos_or_panic!(55.0));
assert!(result.is_err());
let step_x = step.get_x_step();
assert_eq!(*step_x.index(), 0);
assert_eq!(*step_x.step_size_in_time(), pos_or_panic!(5.0));
let result = step_x.next();
assert!(result.is_err());
}
#[test]
fn next_ok_increments_indices_and_builds_self() {
let x_value = pos_or_panic!(5.0);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(Positive::TWO);
let y_value = pos_or_panic!(50.0);
let step = Step::new(x_value, time_unit, datetime, y_value);
let new_y = pos_or_panic!(55.0);
let next = step.next(new_y).unwrap();
assert_eq!(next.get_value(), &new_y);
assert_eq!(*next.x.index(), 1);
assert_eq!(*next.y.index(), step.y.index() + 1);
}
}
#[cfg(test)]
mod tests_step_serialization {
use super::*;
use chrono::{TimeZone, Utc};
use positive::pos_or_panic;
use serde_json::{self, Value};
fn create_test_step() -> Step<f64, f64> {
let x_value = 1.5;
let time_unit = TimeFrame::Day;
let expiration_date = ExpirationDate::Days(pos_or_panic!(30.0));
let y_value = 100.0;
Step::new(x_value, time_unit, expiration_date, y_value)
}
#[test]
fn test_step_serialization_with_days() {
let step = create_test_step();
let serialized = serde_json::to_string(&step).unwrap();
let json_value: Value = serde_json::from_str(&serialized).unwrap();
assert!(json_value.is_object());
assert!(json_value.get("x").is_some());
assert!(json_value.get("y").is_some());
let x = &json_value["x"];
assert_eq!(x["index"], 0);
assert_eq!(x["step_size_in_time"], 1.5);
let y = &json_value["y"];
assert_eq!(y["index"], 0);
assert_eq!(y["value"], 100.0);
}
#[test]
#[should_panic(expected = "ExpirationDate::DateTime is not supported for Step yet")]
fn test_step_with_datetime_panics() {
let x_value = 2.5;
let time_unit = TimeFrame::Hour;
let expiration_date =
ExpirationDate::DateTime(Utc.with_ymd_and_hms(2024, 12, 31, 23, 59, 59).unwrap());
let y_value = 200.0;
Step::new(x_value, time_unit, expiration_date, y_value);
}
#[test]
fn test_step_serialize() {
let x_value = pos_or_panic!(5.0);
let time_unit = TimeFrame::Day;
let datetime = ExpirationDate::Days(pos_or_panic!(30.0));
let y_value = 42.5;
let step = Step::new(x_value, time_unit, datetime, y_value);
let serialized = serde_json::to_string(&step).unwrap();
assert_eq!(
serialized,
r#"{"x":{"index":0,"step_size_in_time":5,"time_unit":"Day","datetime":{"days":30.0}},"y":{"index":0,"value":42.5}}"#
);
}
#[test]
fn test_step_pretty_serialization() {
let step = create_test_step();
let serialized = serde_json::to_string_pretty(&step).unwrap();
assert!(serialized.contains("{\n"));
assert!(serialized.contains(" \"x\": {"));
assert!(serialized.contains(" \"y\": {"));
let deserialized: Value = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized["x"]["step_size_in_time"], 1.5);
assert_eq!(deserialized["y"]["value"], 100.0);
}
}