use std::ops::Add;
#[derive(Clone, Copy)]
pub enum Operation {
Add,
Sub,
Mul,
Div,
Sqrt
}
#[derive(Clone, Copy)]
pub struct ArithmeticError<T> {
pub left: T,
pub right: T,
pub op: Operation,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Safe<T>(T);
#[derive(Clone, Copy)]
pub struct SafeResult<T>(Result<Safe<T>, ArithmeticError<T>>);
pub trait IsSafe {}
impl<T> IsSafe for Safe<T> {}
pub trait SafeNum<T>: IsSafe + Add<T> + Add<Safe<T>> + Add<SafeResult<T>> {}
impl<T> SafeNum<T> for T
where T: IsSafe
+ Add<T>
+ Add<Safe<T>>
+ Add<SafeResult<T>>
{}
impl Add<u64> for Safe<u64> {
type Output = SafeResult<u64>;
fn add(self, other: u64) -> Self::Output {
match self.0.checked_add(other) {
Some(r) => SafeResult(Ok(Safe(r))),
None => SafeResult(Err(
ArithmeticError {
left: self.0,
right: other,
op: Operation::Add,
}
))
}
}
}
impl Add for Safe<u64> {
type Output = SafeResult<u64>;
fn add(self, other: Safe<u64>) -> Self::Output {
self + other.0
}
}
impl Add<SafeResult<u64>> for Safe<u64> {
type Output = SafeResult<u64>;
fn add(self, other: SafeResult<u64>) -> Self::Output {
match other.0 {
Ok(other) => self + other,
Err(e) => SafeResult(Err(e))
}
}
}
impl Add<u64> for SafeResult<u64> {
type Output = SafeResult<u64>;
fn add(self, other: u64) -> Self::Output {
match self.0 {
Ok(this) => this + other,
Err(e) => SafeResult(Err(e))
}
}
}
impl Add<Safe<u64>> for SafeResult<u64> {
type Output = SafeResult<u64>;
fn add(self, other: Safe<u64>) -> Self::Output {
match self.0 {
Ok(this) => this + other,
Err(e) => SafeResult(Err(e))
}
}
}
impl Add for SafeResult<u64> {
type Output = SafeResult<u64>;
fn add(self, other: SafeResult<u64>) -> Self::Output {
match self.0 {
Ok(this) => this + other,
Err(e) => SafeResult(Err(e))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn vector_length(x: Safe<u64>, y: Safe<u64>) -> SafeResult<u64> {
(x + y) + (x + y)
}
#[test]
fn test_vector_length() {
let r = vector_length(Safe(4u64), Safe(5u64));
}
}