1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

#[macro_export]
macro_rules! make_units { ($System:ident, $Unitless:ident, $one:ident; base { $($Type:ident, $constant:ident, $print_as:ident;)+ } derived {$($derived_constant:ident: $Derived:ident = $e:expr;)*} ) => (
    make_units_adv!{
        $System, $Unitless, $one, f64;
        base {
            $(One, $Type, $constant, $print_as;)*
        }
        derived {
            $($derived_constant: $Derived = $e;)*
        }
    }

    );
}

#[macro_export]
macro_rules! make_units_adv { ($System:ident, $Unitless:ident, $one:ident, $OneType:ident; base { $($Root:ident, $Type:ident, $constant:ident, $print_as:ident;)+ } derived {$($derived_constant:ident: $Derived:ident = $e: expr;    )*} ) => (
    #[allow(unused_imports)]
    use $crate::peano::{Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten};
    use $crate::peano::{Peano, KeepPeano, AddPeano, SubPeano, MulPeano, DivPeano, Negate, ToInt};
    use $crate::dimensioned::{Dimension, Dimensionless, Dim, KeepDim, MulDim, DivDim, PowerDim, RootDim, InvertDim, DimToString};
    use std::marker::PhantomData;

    #[derive(Copy, Clone)]
    pub struct $System<$($Type: Peano = Zero),*> {
        $($constant: PhantomData<$Type>),*
    }
    impl<$($Type: Peano),*> Dimension for $System<$($Type),*> {}

    // using $Type and $constant for these traits is confusing. It should really be $Type_Left and
    // $Type_Right or something, but as far as I can tell, that is not supported by Rust
    #[allow(non_camel_case_types)]
    impl<$($Type),*, $($constant),*> KeepDim<$System<$($constant),*>> for $System<$($Type),*> where
        $($Type: Peano + KeepPeano<$constant>),*, $($constant: Peano),* {
            type Output = $System<$(<$Type as KeepPeano<$constant>>::Output),*>;
        }
    #[allow(non_camel_case_types)]
    impl<$($Type),*, $($constant),*> MulDim<$System<$($constant),*>> for $System<$($Type),*> where
        $($Type: Peano + AddPeano<$constant>),*, $($constant: Peano),* {
            type Output = $System<$(<$Type as AddPeano<$constant>>::Output),*>;
        }
    #[allow(non_camel_case_types)]
    impl<$($Type),*, $($constant),*> DivDim<$System<$($constant),*>> for $System<$($Type),*> where
        $($Type: Peano + SubPeano<$constant>),*, $($constant: Peano),* {
            type Output = $System<$(<$Type as SubPeano<$constant>>::Output),*>;
        }
    impl<$($Type),*, RHS> PowerDim<RHS> for $System<$($Type),*> where
        $($Type: Peano + MulPeano<RHS>),*, RHS: Peano {
            type Output = $System<$(<$Type as MulPeano<RHS>>::Output),*>;
        }
    impl<$($Type),*, RHS> RootDim<RHS> for $System<$($Type),*> where
        $($Type: Peano + DivPeano<RHS>),*, RHS: Peano {
            type Output = $System<$(<$Type as DivPeano<RHS>>::Output),*>;
        }
    impl<$($Type),*> InvertDim for $System<$($Type),*> where
        $($Type: Peano + Negate),* {
            type Output = $System<$(<$Type as Negate>::Output),*>;
        }


    fn pretty_dim(roots: [i32; count_args!($($Type),*)], exps: [i32; count_args!($($Type),*)], tokens: [&'static str; count_args!($($Type),*)]) -> String {
        let mut __string = String::new();
        for ((&root, &exp), &token) in roots.iter().zip(exps.iter()).zip(tokens.iter()) {
            let __temp: (&'static str, String) = match exp {
                0 => ("", "".to_string()),
                1 => (token, "*".to_string()),
                _ => (token, format!("^{}*", exp/root)),
            };
            __string = format!("{}{}{}", __string, __temp.0, __temp.1);
        }
        __string.pop(); // remove last "*"
        __string
    }


    impl<$($Type),*> DimToString for $System<$($Type),*>
        where $($Type: ToInt),* {
            fn to_string() -> String {
                // fixme: add #[allow(unused_variables)] lints for these. Not working
                // for me for some reason.
                let allowed_roots = [$($Root::to_int()),*];
                let exponents = [$($Type::to_int()),*];
                let print_tokens = [$(stringify!($print_as)),*];

                pretty_dim(allowed_roots, exponents, print_tokens)
            }
        }

    pub type $Unitless = $System;
    impl Dimensionless for $Unitless {}
    #[allow(non_upper_case_globals)]
    pub const $one: Dim<$Unitless, $OneType> = Dim(1.0, PhantomData);

    __make_types!($System, $($Type, $Root),+ |);

    $(#[allow(non_upper_case_globals)] pub const $constant: Dim<$Type, $OneType> = Dim(1.0, PhantomData));*;

    // $(#[allow(non_upper_case_globals)] pub const $derived_constant: Dim<TYPEPEPEPE, f64> = $e;)*


    );
}

/// Just counts the number of arguments its called with and gives you the total
#[macro_export]
macro_rules! count_args {
    ($arg:ident, $($args:ident),+) => (
        1 + count_args!($($args),+);
    );
    ($arg:ident) => (
        1
    );
}

#[doc_hidden]
#[macro_export]
macro_rules! __make_types {
    ($System:ident, $Type:ident, $Root:ident, $($Types:ident, $Roots:ident),+ | $($Zeros:ident),*) => (
        pub type $Type = $System< $($Zeros,)* $Root>;
        __make_types!($System, $($Types, $Roots),+ | Zero $(, $Zeros)*);
        );
    ($System:ident, $Type:ident, $Root:ident | $($Zeros:ident),*) => (
        pub type $Type = $System<$($Zeros,)* $Root>;
        );
}

// #[macro_export]
// macro_rules! __make_derived_type {
//     ($D:ident, $e: expr) => (
//         pub type $D = __convert_expression!($e);
//         );
// }

// #[macro_export]
// macro_rules! __convert_expression {
//     ($a:ident * $b: expr) => ($a as MulDim<__convert_expression!($b)>>::Output);
//     ($a:ident / $b: expr) => ($a as DivDim<__convert_expression!($b)>>::Output);
//     ($a:ident) => ($a);
// }