// Interpreter makes it work for all integer types.
impl integer {
    fn min(self, other: integer) -> integer {
        #[arcis_circuit = "min"]
        fn min_circ<T>(a: T, b: T) -> T {}
        min_circ(self, other)
    }
    fn max(self, other: integer) -> integer {
        #[arcis_circuit = "max"]
        fn max_circ<T>(a: T, b: T) -> T {}
        max_circ(self, other)
    }
    fn abs(self) -> integer {
        #[arcis_circuit = "abs"]
        fn abs_circ<T>(a: T) -> T {}
        abs_circ(self)
    }
    fn abs_diff(self, other: integer) -> integer {
        #[arcis_circuit = "abs"]
        fn abs_circ(a: integer) -> integer {}
        abs_circ(self - other)
    }
    fn div_ceil(self, other: integer) -> integer {
        fn inner<T>(a: T, b: T) -> T {
            (0 - a) / (0 - b)
        }
        inner(self, other)
    }
    fn is_positive(self) -> bool {
        self > 0
    }
    fn is_negative(self) -> bool {
        self < 0
    }
    fn to_le_bytes(self) -> [u8] {
        fn to_le_bytes<T>(mut n: T) -> [u8; (T::BITS as usize + 7usize) / 8usize] {
            n -= 2 * T::MIN;
            let len = (T::BITS as usize + 7usize) / 8usize;
            let mut res = [0u8; len];
            for i in 0..len {
                res[i] = (n % 256) as u8;
                n >>= 8;
            }
            res
        }
        to_le_bytes(self)
    }
    fn to_be_bytes(self) -> [u8] {
        let mut res = self.to_le_bytes();
        res.reverse();
        res
    }
    fn wrapping_add(self, other: integer) -> integer {
        fn inner<T>(a: T, b: T) -> T {
            ((a + b) as ArcisField) as T
        }
        inner(self, other)
    }
    fn wrapping_sub(self, other: integer) -> integer {
        fn inner<T>(a: T, b: T) -> T {
            ((a - b) as ArcisField) as T
        }
        inner(self, other)
    }
    fn wrapping_mul(self, other: integer) -> integer {

        fn u64_from_le_bytes(bytes: [u8; 8]) -> u64 {
            let mut res = 0u64;
            let mut factor: u64 = 1;
            for x in bytes {
                res += x as u64 * factor;
                factor *= 256;
            }
            res
        }

        fn low_and_high<T>(a: T) -> (u64, u64) {
            let arr = a.to_le_bytes();
            let low = u64_from_le_bytes(arr[0..8]);
            let high = u64_from_le_bytes(arr[8..16]);
            (low, high)
        }
        fn inner<T>(a: T, b: T) -> T {
            if T::BITS == 128 {
                let exp = u64::MAX + 1;
                let (a_low, a_high) = low_and_high(a);
                let (b_low, b_high) = low_and_high(b);
                (a_low * b_low + (a_high * b_low + a_low * b_high) * exp) as T
            } else {
                ((a * b) as ArcisField) as T
            }
        }
        inner(self, other)
    }
}

impl ArcisField {
    fn from(a: u64) -> ArcisField {
        a as ArcisField
    }
}

impl u8 {
    const BITS: u32 = 8;
    const MIN: Self = 0u8;
    const MAX: Self = 255u8;
}

impl u16 {
    const BITS: u32 = 16;
    const MIN: Self = 0u16;
    const MAX: Self = 65535u16;
}

impl u32 {
    const BITS: u32 = 32;
    const MIN: Self = 0u32;
    const MAX: Self = 4294967295u32;
}

impl u64 {
    const BITS: u32 = 64;
    const MIN: Self = 0u64;
    const MAX: Self = 18446744073709551615u64;
}

impl u128 {
    const BITS: u32 = 128;
    const MIN: Self = 0u128;
    const MAX: Self = 340282366920938463463374607431768211455u128;
}

impl i8 {
    const BITS: u32 = 8;
    const MIN: Self = -128i8;
    const MAX: Self = 127i8;
}

impl i16 {
    const BITS: u32 = 16;
    const MIN: Self = -32768i16;
    const MAX: Self = 32767i16;
}

impl i32 {
    const BITS: u32 = 32;
    const MIN: Self = -2147483648i32;
    const MAX: Self = 2147483647i32;
}

impl i64 {
    const BITS: u32 = 64;
    const MIN: Self = -9223372036854775808i64;
    const MAX: Self = 9223372036854775807i64;
}

impl i128 {
    const BITS: u32 = 128;
    const MIN: Self = -170141183460469231731687303715884105728i128;
    const MAX: Self = 170141183460469231731687303715884105727i128;
}
