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
142
143
144
145
146
147
148
149
150

#[macro_export]
macro_rules! make_units { ($System:ident, $allowed_root:ident; base { $($Type:ident, $constant:ident, $print_as:ident;)* } derived {$($Derived:ident($derived_constant: 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, NegDim, DimToString};
    use std::marker::PhantomData;

    #[derive(Copy, Clone)]
    pub struct $System<$($Type: Peano),*> {
        $($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),*> NegDim for $System<$($Type),*> where
        $($Type: Peano + Negate),* {
            type Output = $System<$(<$Type as Negate>::Output),*>;
        }

    impl<$($Type),*> DimToString for $System<$($Type),*>
        where $($Type: ToInt),* {
            fn to_string() -> String {
                // fimxe: make this betterer
                let mut _string = String::new();
                $(
                    let temp = match <$Type as ToInt>::to_int() {
                        0 => ("", "".to_string()),
                        1 => (stringify!($print_as), "*".to_string()),
                        _power => (stringify!($print_as), format!("^{}*", _power/<$allowed_root as ToInt>::to_int()))
                    };
                    _string = format!("{}{}{}", _string, temp.0, temp.1);
                )*
                _string.pop(); // get rid of the last '*'
                _string
            }
        }

    __make_types!($System, $allowed_root | $($Type),* | $($Type),* |);
    // fixme: when __make_types is fixed, use following line to call:
    // __make_types!($System, $allowed_root | $($Type),* | $($Type),*);

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

        // $(
        //   __make_derived_type!($Derived, $e);
        //   pub const $derived_constant: Dim<$Derived, f64> = Dim(1.0, PhantomData);
        //   )*
        );
}

// fixme: This is a bunch of malarky. Can't cycle the direction I want, so we reverse
// $Typse to compensate. The "correct" way is commented below, but won't work until RFC:
// https://github.com/rust-lang/rust/issues/24827 is fixed
#[doc_hidden]
#[macro_export]
macro_rules! __make_types {
    // this first arm filters out the end Types into Zeros with Num, so we go from say
    // (One | Meters, Seconds | Meters, Seconds) to (One, Zero | Meters, Seconds |
    // Meters) and then we move to a different branch I would like a cleaner way to
    // create the list $Num, Zero, Zero, ... but I have yet to find one.
    ($System: ident, $($Nums: ident),+ | $($Types: ident),+ | $DeadType: ident, $($Others: ident),* | $($New: ident),*) => (
        __make_types!($System, $($Nums),+, Zero | $($Types),+ | $($Others),* | $DeadType $(, $New)*);
        );
    // This branch creates our Unitless type and then trims off the last $Type that we no longer need:
    ($System: ident, $First: ident, $($Nums: ident),* | $($Types: ident),+ | $Type: ident | $($New: ident),+) => (
        pub type Unitless = $System<Zero, $($Nums),*>;
        impl Dimensionless for Unitless {}
        __make_types!($System, $($Nums),*, $First | $Type, $($New),* );
        );
    // Create the type, cycle the numbers, then call again:
    ($System: ident, $First: ident, $($Nums: ident),* | $Type: ident, $($Types: ident),+) => (
        pub type $Type = $System<$First, $($Nums),*>;
        __make_types!($System, $($Nums),*, $First | $($Types),*);
        );
    ($System: ident, $($Nums: ident),* | $Type: ident) => (
        pub type $Type = $System<$($Nums),*>;
        );
}
// end of malarky

// // we call this with __make_types!(Num | Types | Types)
// #[doc_hidden]
// #[macro_export]
// macro_rules! __make_types {
//     // this first arm filters out the end Types into Zeros with Num, so we go from say
//     // (One | Meters, Seconds | Meters, Seconds) to (One, Zero | Meters, Seconds |
//     // Meters) and then we move to a different branch I would like a cleaner way to
//     // create the list $Num, Zero, Zero, ... but I have yet to find one.
//     ($System: ident, $($Nums: ident),+ | $($Types: ident),+ | $DeadType: ident, $($Others: ident),+) => (
//         __make_types!($System, $($Nums),*, Zero | $($Types),* | $($Others),*);
//         );
//     // This branch creates our Unitless type and then trims off the last $Type that we no longer need:
//     ($System: ident, $First: ident, $($Nums: ident),* | $($Types: ident),+ | $Type: ident) => (
//         pub type Unitless = $System<Zero, $($Nums),*>;
//         impl Dimensionless for Unitless {}
//         __make_types!($System, $First, $($Nums),* | $($Types),* );
//         );
//     // Create the type, cycle the numbers, then call again:
//     ($System: ident, $($Nums: ident),*, $Last: ident | $Type: ident, $($Types: ident),+) => (
//         pub type $Type = $System<$($Nums),*, $Last>;
//         __make_types!($System, $Last, $($Nums),* | $($Types),*);
//         );
//     ($System: ident, $($Nums: ident),* | $Type: ident) => (
//         pub type $Type = $System<$($Nums),*>;
//         );
// }


// #[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);
// }