use anyhow::{anyhow, bail};
use chrono::Duration;
use chronoutil::RelativeDuration;
use hamelin_lib::func::defs::{
CalendarIntervalDivideInt, CalendarIntervalMinusCalendarInterval, CalendarIntervalMultiplyInt,
CalendarIntervalPlusCalendarInterval, CalendarIntervalPlusTimestamp,
IntMultiplyCalendarInterval, IntervalDivideNumeric, IntervalMinusInterval,
IntervalMultiplyNumeric, IntervalPlusInterval, IntervalPlusTimestamp, NumericMultiplyInterval,
TimestampMinusCalendarInterval, TimestampMinusInterval, TimestampMinusTimestamp,
TimestampPlusCalendarInterval, TimestampPlusInterval,
};
use crate::registry::EvalRegistry;
use crate::reverse_eval::domain::Constraint;
use crate::value::Value;
use crate::{null_propagate, null_propagate_reverse};
fn multiply_interval(interval: &Duration, multiplier: f64) -> anyhow::Result<Duration> {
let micros = interval
.num_microseconds()
.ok_or_else(|| anyhow!("Interval too large to represent in microseconds"))?;
let result_micros = (micros as f64 * multiplier).round() as i64;
Ok(Duration::microseconds(result_micros))
}
fn divide_interval(interval: &Duration, divisor: f64) -> anyhow::Result<Duration> {
if divisor == 0.0 {
bail!("Division by zero");
}
let micros = interval
.num_microseconds()
.ok_or_else(|| anyhow!("Interval too large to represent in microseconds"))?;
let result_micros = (micros as f64 / divisor).round() as i64;
Ok(Duration::microseconds(result_micros))
}
pub fn register(registry: &mut EvalRegistry) {
registry.register_eval::<IntervalMultiplyNumeric>(|mut params| {
let interval = null_propagate!(params.take()?).require_interval()?;
let numeric = null_propagate!(params.take()?);
let multiplier = numeric.coerce_require_double()?;
let result = multiply_interval(&interval, multiplier)?;
Ok(Value::Interval(result))
});
registry.register_reverse::<IntervalMultiplyNumeric>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let multiplier = null_propagate_reverse!(right_val, output_constraint)
.coerce_require_double()?;
output_constraint.map(|output_val| {
let out_interval = output_val.try_interval()?;
let result = divide_interval(out_interval, multiplier)?;
Ok(Value::Interval(result))
})
}
(Some(left_val), None) => {
let interval =
null_propagate_reverse!(left_val, output_constraint).try_interval()?;
output_constraint.map(|output_val| {
let out_interval = output_val.try_interval()?;
let interval_micros = interval.num_microseconds().ok_or_else(|| {
anyhow!("Interval too large to represent in microseconds")
})?;
if interval_micros == 0 {
bail!("Division by zero in reverse multiplication");
}
let out_micros = out_interval.num_microseconds().ok_or_else(|| {
anyhow!("Interval too large to represent in microseconds")
})?;
let multiplier = out_micros as f64 / interval_micros as f64;
Ok(Value::Double(multiplier))
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<NumericMultiplyInterval>(|mut params| {
let numeric = null_propagate!(params.take()?);
let interval = null_propagate!(params.take()?).require_interval()?;
let multiplier = numeric.coerce_require_double()?;
let result = multiply_interval(&interval, multiplier)?;
Ok(Value::Interval(result))
});
registry.register_reverse::<NumericMultiplyInterval>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let interval =
null_propagate_reverse!(right_val, output_constraint).try_interval()?;
output_constraint.map(|output_val| {
let out_interval = output_val.try_interval()?;
let interval_micros = interval.num_microseconds().ok_or_else(|| {
anyhow!("Interval too large to represent in microseconds")
})?;
if interval_micros == 0 {
bail!("Division by zero in reverse multiplication");
}
let out_micros = out_interval.num_microseconds().ok_or_else(|| {
anyhow!("Interval too large to represent in microseconds")
})?;
let multiplier = out_micros as f64 / interval_micros as f64;
Ok(Value::Double(multiplier))
})
}
(Some(left_val), None) => {
let multiplier =
null_propagate_reverse!(left_val, output_constraint).coerce_require_double()?;
output_constraint.map(|output_val| {
let out_interval = output_val.try_interval()?;
let result = divide_interval(out_interval, multiplier)?;
Ok(Value::Interval(result))
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<IntervalDivideNumeric>(|mut params| {
let interval = null_propagate!(params.take()?).require_interval()?;
let numeric = null_propagate!(params.take()?);
let divisor = numeric.coerce_require_double()?;
let result = divide_interval(&interval, divisor)?;
Ok(Value::Interval(result))
});
registry.register_reverse::<IntervalDivideNumeric>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let divisor = null_propagate_reverse!(right_val, output_constraint)
.coerce_require_double()?;
if divisor == 0.0 {
return Ok(Constraint::Empty);
}
output_constraint.map(|output_val| {
let out_interval = output_val.try_interval()?;
let result = multiply_interval(out_interval, divisor)?;
Ok(Value::Interval(result))
})
}
(Some(left_val), None) => {
let interval =
null_propagate_reverse!(left_val, output_constraint).try_interval()?;
output_constraint.map(|output_val| {
let out_interval = output_val.try_interval()?;
let interval_micros = interval.num_microseconds().ok_or_else(|| {
anyhow!("Interval too large to represent in microseconds")
})?;
let out_micros = out_interval.num_microseconds().ok_or_else(|| {
anyhow!("Interval too large to represent in microseconds")
})?;
if out_micros == 0 {
bail!("Division by zero in reverse division");
}
Ok(Value::Double(interval_micros as f64 / out_micros as f64))
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<CalendarIntervalPlusCalendarInterval>(|mut params| {
let left_ci = null_propagate!(params.take()?).require_calendar_interval()?;
let right_ci = null_propagate!(params.take()?).require_calendar_interval()?;
Ok(Value::CalendarInterval(left_ci + right_ci))
});
registry.register_reverse::<CalendarIntervalPlusCalendarInterval>(
|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let right_ci = null_propagate_reverse!(right_val, output_constraint)
.try_calendar_interval()?;
output_constraint.map(|output_val| {
let out_ci = output_val.try_calendar_interval()?;
Ok(Value::CalendarInterval(out_ci - right_ci))
})
}
(Some(left_val), None) => {
let left_ci = null_propagate_reverse!(left_val, output_constraint)
.try_calendar_interval()?;
output_constraint.map(|output_val| {
let out_ci = output_val.try_calendar_interval()?;
Ok(Value::CalendarInterval(out_ci - left_ci))
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
},
);
registry.register_eval::<CalendarIntervalMinusCalendarInterval>(|mut params| {
let left_ci = null_propagate!(params.take()?).require_calendar_interval()?;
let right_ci = null_propagate!(params.take()?).require_calendar_interval()?;
Ok(Value::CalendarInterval(left_ci - right_ci))
});
registry.register_reverse::<CalendarIntervalMinusCalendarInterval>(
|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let right_ci = null_propagate_reverse!(right_val, output_constraint)
.try_calendar_interval()?;
output_constraint.map(|output_val| {
let out_ci = output_val.try_calendar_interval()?;
Ok(Value::CalendarInterval(out_ci + right_ci))
})
}
(Some(left_val), None) => {
let left_ci = null_propagate_reverse!(left_val, output_constraint)
.try_calendar_interval()?;
output_constraint.map(|output_val| {
let out_ci = output_val.try_calendar_interval()?;
Ok(Value::CalendarInterval(left_ci - out_ci))
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
},
);
registry.register_eval::<CalendarIntervalMultiplyInt>(|mut params| {
let calendar_interval = null_propagate!(params.take()?).require_calendar_interval()?;
let numeric = null_propagate!(params.take()?);
let multiplier = numeric.require_int()?;
Ok(Value::CalendarInterval(
calendar_interval * multiplier as i32,
))
});
registry.register_reverse::<CalendarIntervalMultiplyInt>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let multiplier = null_propagate_reverse!(right_val, output_constraint).try_int()?;
if multiplier == 0 {
bail!("Division by zero in reverse multiplication");
}
output_constraint.map(|output_val| {
let out_ci = output_val.try_calendar_interval()?;
Ok(Value::CalendarInterval(out_ci / multiplier as i32))
})
}
(Some(left_val), None) => {
null_propagate_reverse!(left_val, output_constraint);
bail!("Cannot solve for numeric multiplier in calendar interval multiplication")
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<IntMultiplyCalendarInterval>(|mut params| {
let numeric = null_propagate!(params.take()?);
let calendar_interval = null_propagate!(params.take()?).require_calendar_interval()?;
let multiplier = numeric.require_int()?;
Ok(Value::CalendarInterval(
calendar_interval * multiplier as i32,
))
});
registry.register_reverse::<IntMultiplyCalendarInterval>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
null_propagate_reverse!(right_val, output_constraint);
bail!("Cannot solve for numeric multiplier in calendar interval multiplication")
}
(Some(left_val), None) => {
let multiplier = null_propagate_reverse!(left_val, output_constraint).try_int()?;
if multiplier == 0 {
bail!("Division by zero in reverse multiplication");
}
output_constraint.map(|output_val| {
let out_ci = output_val.try_calendar_interval()?;
Ok(Value::CalendarInterval(out_ci / multiplier as i32))
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<CalendarIntervalDivideInt>(|mut params| {
let calendar_interval = null_propagate!(params.take()?).require_calendar_interval()?;
let numeric = null_propagate!(params.take()?);
let divisor = numeric.require_int()?;
if divisor == 0 {
bail!("Division by zero");
}
Ok(Value::CalendarInterval(calendar_interval / divisor as i32))
});
registry.register_reverse::<CalendarIntervalDivideInt>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let divisor = null_propagate_reverse!(right_val, output_constraint).try_int()?;
if divisor == 0 {
return Ok(Constraint::Empty);
}
output_constraint.map(|output_val| {
let out_ci = output_val.try_calendar_interval()?;
Ok(Value::CalendarInterval(out_ci * divisor as i32))
})
}
(Some(left_val), None) => {
null_propagate_reverse!(left_val, output_constraint);
bail!("Cannot solve for numeric divisor in calendar interval division")
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<TimestampMinusInterval>(|mut params| {
let timestamp = null_propagate!(params.take()?).require_timestamp()?;
let interval = null_propagate!(params.take()?).require_interval()?;
Ok((timestamp - interval).into())
});
registry.register_reverse::<TimestampMinusInterval>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let interval =
null_propagate_reverse!(right_val, output_constraint).try_interval()?;
output_constraint.map(|output_val| {
let out_ts = output_val.try_timestamp()?;
let input_ts = out_ts + *interval;
Ok(input_ts.into())
})
}
(Some(left_val), None) => {
let ts = null_propagate_reverse!(left_val, output_constraint).try_timestamp()?;
output_constraint.map(|output_val| {
let out_ts = output_val.try_timestamp()?;
let interval = ts - out_ts;
Ok(Value::Interval(interval))
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<TimestampMinusCalendarInterval>(|mut params| {
let timestamp = null_propagate!(params.take()?).require_timestamp()?;
let months = null_propagate!(params.take()?).require_calendar_interval()?;
let negated = RelativeDuration::months(-months);
Ok((timestamp + negated).into())
});
registry.register_reverse::<TimestampMinusCalendarInterval>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let months = null_propagate_reverse!(right_val, output_constraint)
.try_calendar_interval()?;
output_constraint.map(|output_val| {
let out_ts = output_val.try_timestamp()?;
let input_ts = out_ts + RelativeDuration::months(months);
Ok(input_ts.into())
})
}
(Some(left_val), None) => {
null_propagate_reverse!(left_val, output_constraint);
bail!("Cannot solve for calendar interval from timestamp arithmetic")
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<IntervalPlusTimestamp>(|mut params| {
let interval = null_propagate!(params.take()?).require_interval()?;
let timestamp = null_propagate!(params.take()?).require_timestamp()?;
Ok((timestamp + interval).into())
});
registry.register_reverse::<IntervalPlusTimestamp>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let ts = null_propagate_reverse!(right_val, output_constraint).try_timestamp()?;
output_constraint.map(|output_val| {
let out_ts = output_val.try_timestamp()?;
let interval = out_ts - ts;
Ok(Value::Interval(interval))
})
}
(Some(left_val), None) => {
let interval =
null_propagate_reverse!(left_val, output_constraint).try_interval()?;
output_constraint.map(|output_val| {
let out_ts = output_val.try_timestamp()?;
let input_ts = out_ts - *interval;
Ok(input_ts.into())
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<CalendarIntervalPlusTimestamp>(|mut params| {
let months = null_propagate!(params.take()?).require_calendar_interval()?;
let timestamp = null_propagate!(params.take()?).require_timestamp()?;
Ok((timestamp + RelativeDuration::months(months)).into())
});
registry.register_reverse::<CalendarIntervalPlusTimestamp>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
null_propagate_reverse!(right_val, output_constraint);
bail!("Cannot solve for calendar interval from timestamp arithmetic")
}
(Some(left_val), None) => {
let months =
null_propagate_reverse!(left_val, output_constraint).try_calendar_interval()?;
output_constraint.map(|output_val| {
let out_ts = output_val.try_timestamp()?;
let negated = RelativeDuration::months(-months);
let input_ts = out_ts + negated;
Ok(input_ts.into())
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<TimestampMinusTimestamp>(|mut params| {
let left_timestamp = null_propagate!(params.take()?).require_timestamp()?;
let right_timestamp = null_propagate!(params.take()?).require_timestamp()?;
let diff = left_timestamp - right_timestamp;
Ok(Value::Interval(diff))
});
registry.register_reverse::<TimestampMinusTimestamp>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let right_ts =
null_propagate_reverse!(right_val, output_constraint).try_timestamp()?;
output_constraint.map(|output_val| {
let interval = output_val.try_interval()?;
let left_ts = right_ts + *interval;
Ok(left_ts.into())
})
}
(Some(left_val), None) => {
let left_ts =
null_propagate_reverse!(left_val, output_constraint).try_timestamp()?;
output_constraint.map(|output_val| {
let interval = output_val.try_interval()?;
let right_ts = left_ts - *interval;
Ok(right_ts.into())
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<TimestampPlusInterval>(|mut params| {
let timestamp = null_propagate!(params.take()?).require_timestamp()?;
let interval = null_propagate!(params.take()?).require_interval()?;
Ok((timestamp + interval).into())
});
registry.register_reverse::<TimestampPlusInterval>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let interval =
null_propagate_reverse!(right_val, output_constraint).try_interval()?;
output_constraint.map(|output_val| {
let out_ts = output_val.try_timestamp()?;
let input_ts = out_ts - *interval;
Ok(input_ts.into())
})
}
(Some(left_val), None) => {
let ts = null_propagate_reverse!(left_val, output_constraint).try_timestamp()?;
output_constraint.map(|output_val| {
let out_ts = output_val.try_timestamp()?;
let interval = out_ts - ts;
Ok(Value::Interval(interval))
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<TimestampPlusCalendarInterval>(|mut params| {
let timestamp = null_propagate!(params.take()?).require_timestamp()?;
let months = null_propagate!(params.take()?).require_calendar_interval()?;
let result = timestamp + RelativeDuration::months(months);
Ok(result.into())
});
registry.register_reverse::<TimestampPlusCalendarInterval>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let months = null_propagate_reverse!(right_val, output_constraint)
.try_calendar_interval()?;
output_constraint.map(|output_val| {
let out_ts = output_val.try_timestamp()?;
let negated = RelativeDuration::months(-months);
let input_ts = out_ts + negated;
Ok(input_ts.into())
})
}
(Some(left_val), None) => {
null_propagate_reverse!(left_val, output_constraint);
bail!("Cannot solve for calendar interval from timestamp arithmetic")
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<IntervalPlusInterval>(|mut params| {
let left_interval = null_propagate!(params.take()?).require_interval()?;
let right_interval = null_propagate!(params.take()?).require_interval()?;
Ok(Value::Interval(left_interval + right_interval))
});
registry.register_reverse::<IntervalPlusInterval>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let right_interval =
null_propagate_reverse!(right_val, output_constraint).try_interval()?;
output_constraint.map(|output_val| {
let out_interval = output_val.try_interval()?;
let left_interval = *out_interval - *right_interval;
Ok(Value::Interval(left_interval))
})
}
(Some(left_val), None) => {
let left_interval =
null_propagate_reverse!(left_val, output_constraint).try_interval()?;
output_constraint.map(|output_val| {
let out_interval = output_val.try_interval()?;
let right_interval = *out_interval - *left_interval;
Ok(Value::Interval(right_interval))
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
registry.register_eval::<IntervalMinusInterval>(|mut params| {
let left_interval = null_propagate!(params.take()?).require_interval()?;
let right_interval = null_propagate!(params.take()?).require_interval()?;
Ok(Value::Interval(left_interval - right_interval))
});
registry.register_reverse::<IntervalMinusInterval>(|output_constraint, params| {
let left = params.get_by_name("left").ok().and_then(|opt| opt.as_ref());
let right = params
.get_by_name("right")
.ok()
.and_then(|opt| opt.as_ref());
match (left, right) {
(None, Some(right_val)) => {
let right_interval =
null_propagate_reverse!(right_val, output_constraint).try_interval()?;
output_constraint.map(|output_val| {
let out_interval = output_val.try_interval()?;
let left_interval = *out_interval + *right_interval;
Ok(Value::Interval(left_interval))
})
}
(Some(left_val), None) => {
let left_interval =
null_propagate_reverse!(left_val, output_constraint).try_interval()?;
output_constraint.map(|output_val| {
let out_interval = output_val.try_interval()?;
let right_interval = *left_interval - *out_interval;
Ok(Value::Interval(right_interval))
})
}
_ => bail!("Invalid reverse evaluation: expected exactly one variable"),
}
});
}