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
pub mod inner;
pub mod var_num;

pub use crate::inner::{VarFloat, VarInt, VarUInt};
pub use crate::var_num::VarNum;
use std::ops::{
    AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign, MulAssign, RemAssign, ShlAssign,
    ShrAssign, SubAssign,
};
macro_rules! impl_assign {
    ($($ty:ty => [$($trait:ident($fn:ident $op:tt)),*]),*) => {
        $(
            $(
                impl $trait for $ty {
                    fn $fn(&mut self, other: Self) {
                        *self = *self $op other;
                    }
                }
            )*
        )*
    }
}

macro_rules! impl_assignments {
    ($($ty:ty),*) => {
        $(
            impl_assign! {
                $ty => [
                    AddAssign(add_assign +),
                    SubAssign(sub_assign -),
                    MulAssign(mul_assign *),
                    DivAssign(div_assign /),
                    RemAssign(rem_assign %),
                    BitAndAssign(bitand_assign &),
                    BitOrAssign(bitor_assign |),
                    BitXorAssign(bitxor_assign ^),
                    ShlAssign(shl_assign <<),
                    ShrAssign(shr_assign >>)
                ]
            }
        )*
    }
}

impl_assignments! { VarInt, VarUInt, VarFloat, VarNum }

#[cfg(test)]
mod var_num_tests {
    use super::*;

    #[test]
    fn it_works() {
        let a: VarNum = 2.into();
        let b: VarNum = 2.0.into();
        let c: VarNum = a + b;
        assert_eq!(c, VarNum::Int(VarInt::I8(4)));
    }

    macro_rules! generate_tests_unary {
        ($($group_name:ident > $ty:ty: $op:tt),*) => {
            $(
                #[test]
                fn $group_name() {
                    let a: $ty = <$ty>::MAX / (2 as $ty);
                    let a_n: VarNum = a.into();
                    assert_eq!($op a, ($op a_n).into());
                }
            )*
        }
    }

    generate_tests_unary! {
        u8_not > u8: !,
        u16_not > u16: !,
        u32_not > u32: !,
        u64_not > u64: !,
        u128_not > u128: !,
        i8_not > i8: !,
        i16_not > i16: !,
        i32_not > i32: !,
        i64_not > i64: !,
        i128_not > i128: !,
        i8_neg > i8: -,
        i16_neg > i16: -,
        i32_neg > i32: -,
        i64_neg > i64: -,
        i128_neg > i128: -,
        f32_neg > f32: -,
        f64_neg > f64: -
    }

    macro_rules! generate_tests_binary {
        ($($group_name:ident > $ty:ty: $op:tt),*) => {
            $(
                #[test]
                fn $group_name() {
                    let a: $ty = 6 as $ty;
                    let b: $ty = 2 as $ty;
                    let a_n: VarNum = a.into();
                    let b_n: VarNum = b.into();
                    assert_eq!(a $op b, (a_n $op b_n).into());
                }
            )*
        }
    }

    generate_tests_binary! {
        u8_add > u8: +,
        f32_add > f32: +,
        f64_add > f64: +,
        u64_sub > u64: -,
        u128_sub > u128: -,
        i8_sub > i8: -,
        i16_sub > i16: -,
        u16_mul > u16: *,
        u32_mul > u32: *,
        u64_mul > u64: *,
        u128_mul > u128: *,
        i8_mul > i8: *,
        i16_mul > i16: *,
        f64_mul > f64: *,
        u128_div > u128: /,
        i8_div > i8: /,
        i32_div > i32: /,
        f64_div > f64: /,
        u64_rem > u64: %,
        i16_rem > i16: %,
        i32_rem > i32: %
    }
}