diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/typenum/build | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/typenum/build')
-rw-r--r-- | vendor/typenum/build/main.rs | 186 | ||||
-rw-r--r-- | vendor/typenum/build/op.rs | 559 | ||||
-rw-r--r-- | vendor/typenum/build/tests.rs | 328 |
3 files changed, 1073 insertions, 0 deletions
diff --git a/vendor/typenum/build/main.rs b/vendor/typenum/build/main.rs new file mode 100644 index 000000000..03c4697d4 --- /dev/null +++ b/vendor/typenum/build/main.rs @@ -0,0 +1,186 @@ +use std::env; +use std::fmt; +use std::fs::File; +use std::io::Write; +use std::path::Path; + +mod op; +mod tests; + +pub enum UIntCode { + Term, + Zero(Box<UIntCode>), + One(Box<UIntCode>), +} + +pub enum IntCode { + Zero, + Pos(Box<UIntCode>), + Neg(Box<UIntCode>), +} + +impl fmt::Display for UIntCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + UIntCode::Term => write!(f, "UTerm"), + UIntCode::Zero(ref inner) => write!(f, "UInt<{}, B0>", inner), + UIntCode::One(ref inner) => write!(f, "UInt<{}, B1>", inner), + } + } +} + +impl fmt::Display for IntCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + IntCode::Zero => write!(f, "Z0"), + IntCode::Pos(ref inner) => write!(f, "PInt<{}>", inner), + IntCode::Neg(ref inner) => write!(f, "NInt<{}>", inner), + } + } +} + +pub fn gen_uint(u: u64) -> UIntCode { + let mut result = UIntCode::Term; + let mut x = 1u64 << 63; + while x > u { + x >>= 1 + } + while x > 0 { + result = if x & u > 0 { + UIntCode::One(Box::new(result)) + } else { + UIntCode::Zero(Box::new(result)) + }; + x >>= 1; + } + result +} + +pub fn gen_int(i: i64) -> IntCode { + use std::cmp::Ordering::{Equal, Greater, Less}; + + match i.cmp(&0) { + Greater => IntCode::Pos(Box::new(gen_uint(i as u64))), + Less => IntCode::Neg(Box::new(gen_uint(i.abs() as u64))), + Equal => IntCode::Zero, + } +} + +#[cfg_attr( + feature = "no_std", + deprecated( + since = "1.3.0", + note = "the `no_std` flag is no longer necessary and will be removed in the future" + ) +)] +pub fn no_std() {} + +// fixme: get a warning when testing without this +#[allow(dead_code)] +fn main() { + let highest: u64 = 1024; + + // Use hardcoded values to avoid issues with cross-compilation. + // See https://github.com/paholg/typenum/issues/162 + let first2: u32 = 11; // (highest as f64).log(2.0).round() as u32 + 1; + let first10: u32 = 4; // (highest as f64).log(10.0) as u32 + 1; + let uints = (0..(highest + 1)) + .chain((first2..64).map(|i| 2u64.pow(i))) + .chain((first10..20).map(|i| 10u64.pow(i))); + + let out_dir = env::var("OUT_DIR").unwrap(); + let dest = Path::new(&out_dir).join("consts.rs"); + println!("cargo:rustc-env=TYPENUM_BUILD_CONSTS={}", dest.display()); + + let mut f = File::create(&dest).unwrap(); + + no_std(); + + // Header stuff here! + write!( + f, + " +/** +Type aliases for many constants. + +This file is generated by typenum's build script. + +For unsigned integers, the format is `U` followed by the number. We define aliases for + +- Numbers 0 through {highest} +- Powers of 2 below `u64::MAX` +- Powers of 10 below `u64::MAX` + +These alias definitions look like this: + +```rust +use typenum::{{B0, B1, UInt, UTerm}}; + +# #[allow(dead_code)] +type U6 = UInt<UInt<UInt<UTerm, B1>, B1>, B0>; +``` + +For positive signed integers, the format is `P` followed by the number and for negative +signed integers it is `N` followed by the number. For the signed integer zero, we use +`Z0`. We define aliases for + +- Numbers -{highest} through {highest} +- Powers of 2 between `i64::MIN` and `i64::MAX` +- Powers of 10 between `i64::MIN` and `i64::MAX` + +These alias definitions look like this: + +```rust +use typenum::{{B0, B1, UInt, UTerm, PInt, NInt}}; + +# #[allow(dead_code)] +type P6 = PInt<UInt<UInt<UInt<UTerm, B1>, B1>, B0>>; +# #[allow(dead_code)] +type N6 = NInt<UInt<UInt<UInt<UTerm, B1>, B1>, B0>>; +``` + +# Example +```rust +# #[allow(unused_imports)] +use typenum::{{U0, U1, U2, U3, U4, U5, U6}}; +# #[allow(unused_imports)] +use typenum::{{N3, N2, N1, Z0, P1, P2, P3}}; +# #[allow(unused_imports)] +use typenum::{{U774, N17, N10000, P1024, P4096}}; +``` + +We also define the aliases `False` and `True` for `B0` and `B1`, respectively. +*/ +#[allow(missing_docs)] +pub mod consts {{ + use crate::uint::{{UInt, UTerm}}; + use crate::int::{{PInt, NInt}}; + + pub use crate::bit::{{B0, B1}}; + pub use crate::int::Z0; + + pub type True = B1; + pub type False = B0; +", + highest = highest + ) + .unwrap(); + + for u in uints { + writeln!(f, " pub type U{} = {};", u, gen_uint(u)).unwrap(); + if u <= ::std::i64::MAX as u64 && u != 0 { + let i = u as i64; + writeln!( + f, + " pub type P{i} = PInt<U{i}>; pub type N{i} = NInt<U{i}>;", + i = i + ) + .unwrap(); + } + } + write!(f, "}}").unwrap(); + + tests::build_tests().unwrap(); + + op::write_op_macro().unwrap(); +} diff --git a/vendor/typenum/build/op.rs b/vendor/typenum/build/op.rs new file mode 100644 index 000000000..756f37229 --- /dev/null +++ b/vendor/typenum/build/op.rs @@ -0,0 +1,559 @@ +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum OpType { + Operator, + Function, +} + +use self::OpType::*; + +struct Op { + token: &'static str, + operator: &'static str, + example: (&'static str, &'static str), + precedence: u8, + n_args: u8, + op_type: OpType, +} + +pub fn write_op_macro() -> ::std::io::Result<()> { + let out_dir = ::std::env::var("OUT_DIR").unwrap(); + let dest = ::std::path::Path::new(&out_dir).join("op.rs"); + println!("cargo:rustc-env=TYPENUM_BUILD_OP={}", dest.display()); + let mut f = ::std::fs::File::create(&dest).unwrap(); + + // Operator precedence is taken from + // https://doc.rust-lang.org/reference.html#operator-precedence + // + // We choose 16 as the highest precedence (functions are set to 255 but it doesn't matter + // for them). We also only use operators that are left associative so we don't have to worry + // about that. + let ops = &[ + Op { + token: "*", + operator: "Prod", + example: ("P2 * P3", "P6"), + precedence: 16, + n_args: 2, + op_type: Operator, + }, + Op { + token: "/", + operator: "Quot", + example: ("P6 / P2", "P3"), + precedence: 16, + n_args: 2, + op_type: Operator, + }, + Op { + token: "%", + operator: "Mod", + example: ("P5 % P3", "P2"), + precedence: 16, + n_args: 2, + op_type: Operator, + }, + Op { + token: "+", + operator: "Sum", + example: ("P2 + P3", "P5"), + precedence: 15, + n_args: 2, + op_type: Operator, + }, + Op { + token: "-", + operator: "Diff", + example: ("P2 - P3", "N1"), + precedence: 15, + n_args: 2, + op_type: Operator, + }, + Op { + token: "<<", + operator: "Shleft", + example: ("U1 << U5", "U32"), + precedence: 14, + n_args: 2, + op_type: Operator, + }, + Op { + token: ">>", + operator: "Shright", + example: ("U32 >> U5", "U1"), + precedence: 14, + n_args: 2, + op_type: Operator, + }, + Op { + token: "&", + operator: "And", + example: ("U5 & U3", "U1"), + precedence: 13, + n_args: 2, + op_type: Operator, + }, + Op { + token: "^", + operator: "Xor", + example: ("U5 ^ U3", "U6"), + precedence: 12, + n_args: 2, + op_type: Operator, + }, + Op { + token: "|", + operator: "Or", + example: ("U5 | U3", "U7"), + precedence: 11, + n_args: 2, + op_type: Operator, + }, + Op { + token: "==", + operator: "Eq", + example: ("P5 == P3 + P2", "True"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: "!=", + operator: "NotEq", + example: ("P5 != P3 + P2", "False"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: "<=", + operator: "LeEq", + example: ("P6 <= P3 + P2", "False"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: ">=", + operator: "GrEq", + example: ("P6 >= P3 + P2", "True"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: "<", + operator: "Le", + example: ("P4 < P3 + P2", "True"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: ">", + operator: "Gr", + example: ("P5 < P3 + P2", "False"), + precedence: 10, + n_args: 2, + op_type: Operator, + }, + Op { + token: "cmp", + operator: "Compare", + example: ("cmp(P2, P3)", "Less"), + precedence: !0, + n_args: 2, + op_type: Function, + }, + Op { + token: "sqr", + operator: "Square", + example: ("sqr(P2)", "P4"), + precedence: !0, + n_args: 1, + op_type: Function, + }, + Op { + token: "sqrt", + operator: "Sqrt", + example: ("sqrt(U9)", "U3"), + precedence: !0, + n_args: 1, + op_type: Function, + }, + Op { + token: "abs", + operator: "AbsVal", + example: ("abs(N2)", "P2"), + precedence: !0, + n_args: 1, + op_type: Function, + }, + Op { + token: "cube", + operator: "Cube", + example: ("cube(P2)", "P8"), + precedence: !0, + n_args: 1, + op_type: Function, + }, + Op { + token: "pow", + operator: "Exp", + example: ("pow(P2, P3)", "P8"), + precedence: !0, + n_args: 2, + op_type: Function, + }, + Op { + token: "min", + operator: "Minimum", + example: ("min(P2, P3)", "P2"), + precedence: !0, + n_args: 2, + op_type: Function, + }, + Op { + token: "max", + operator: "Maximum", + example: ("max(P2, P3)", "P3"), + precedence: !0, + n_args: 2, + op_type: Function, + }, + Op { + token: "log2", + operator: "Log2", + example: ("log2(U9)", "U3"), + precedence: !0, + n_args: 1, + op_type: Function, + }, + Op { + token: "gcd", + operator: "Gcf", + example: ("gcd(U9, U21)", "U3"), + precedence: !0, + n_args: 2, + op_type: Function, + }, + ]; + + use std::io::Write; + write!( + f, + " +/** +Convenient type operations. + +Any types representing values must be able to be expressed as `ident`s. That means they need to be +in scope. + +For example, `P5` is okay, but `typenum::P5` is not. + +You may combine operators arbitrarily, although doing so excessively may require raising the +recursion limit. + +# Example +```rust +#![recursion_limit=\"128\"] +#[macro_use] extern crate typenum; +use typenum::consts::*; + +fn main() {{ + assert_type!( + op!(min((P1 - P2) * (N3 + N7), P5 * (P3 + P4)) == P10) + ); +}} +``` +Operators are evaluated based on the operator precedence outlined +[here](https://doc.rust-lang.org/reference.html#operator-precedence). + +The full list of supported operators and functions is as follows: + +{} + +They all expand to type aliases defined in the `operator_aliases` module. Here is an expanded list, +including examples: + +", + ops.iter() + .map(|op| format!("`{}`", op.token)) + .collect::<Vec<_>>() + .join(", ") + )?; + + //write!(f, "Token | Alias | Example\n ===|===|===\n")?; + + for op in ops.iter() { + write!( + f, + "---\nOperator `{token}`. Expands to `{operator}`. + +```rust +# #[macro_use] extern crate typenum; +# use typenum::*; +# fn main() {{ +assert_type_eq!(op!({ex0}), {ex1}); +# }} +```\n +", + token = op.token, + operator = op.operator, + ex0 = op.example.0, + ex1 = op.example.1 + )?; + } + + write!( + f, + "*/ +#[macro_export(local_inner_macros)] +macro_rules! op {{ + ($($tail:tt)*) => ( __op_internal__!($($tail)*) ); +}} + + #[doc(hidden)] + #[macro_export(local_inner_macros)] + macro_rules! __op_internal__ {{ +" + )?; + + // We first us the shunting-yard algorithm to produce our tokens in Polish notation. + // See: https://en.wikipedia.org/wiki/Shunting-yard_algorithm + + // Note: Due to macro asymmetry, "the top of the stack" refers to the first element, not the + // last + + // ----------------------------------------------------------------------------------------- + // Stage 1: There are tokens to be read: + + // ------- + // Case 1: Token is a function => Push it onto the stack: + for fun in ops.iter().filter(|f| f.op_type == Function) { + write!( + f, + " +(@stack[$($stack:ident,)*] @queue[$($queue:ident,)*] @tail: {f_token} $($tail:tt)*) => ( + __op_internal__!(@stack[{f_op}, $($stack,)*] @queue[$($queue,)*] @tail: $($tail)*) +);", + f_token = fun.token, + f_op = fun.operator + )?; + } + + // ------- + // Case 2: Token is a comma => Until the top of the stack is a LParen, + // Pop operators from stack to queue + + // Base case: Top of stack is LParen, ditch comma and continue + write!( + f, + " +(@stack[LParen, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: , $($tail:tt)*) => ( + __op_internal__!(@stack[LParen, $($stack,)*] @queue[$($queue,)*] @tail: $($tail)*) +);" + )?; + // Recursive case: Not LParen, pop from stack to queue + write!( + f, + " +(@stack[$stack_top:ident, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: , $($tail:tt)*) => ( + __op_internal__!(@stack[$($stack,)*] @queue[$stack_top, $($queue,)*] @tail: , $($tail)*) +);" + )?; + + // ------- + // Case 3: Token is an operator, o1: + for o1 in ops.iter().filter(|op| op.op_type == Operator) { + // If top of stack is operator o2 with o1.precedence <= o2.precedence, + // Then pop o2 off stack onto queue: + for o2 in ops + .iter() + .filter(|op| op.op_type == Operator) + .filter(|o2| o1.precedence <= o2.precedence) + { + write!( + f, + " +(@stack[{o2_op}, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: {o1_token} $($tail:tt)*) => ( + __op_internal__!(@stack[$($stack,)*] @queue[{o2_op}, $($queue,)*] @tail: {o1_token} $($tail)*) +);", + o2_op = o2.operator, + o1_token = o1.token + )?; + } + // Base case: push o1 onto stack + write!( + f, + " +(@stack[$($stack:ident,)*] @queue[$($queue:ident,)*] @tail: {o1_token} $($tail:tt)*) => ( + __op_internal__!(@stack[{o1_op}, $($stack,)*] @queue[$($queue,)*] @tail: $($tail)*) +);", + o1_op = o1.operator, + o1_token = o1.token + )?; + } + + // ------- + // Case 4: Token is "(": push it onto stack as "LParen". Also convert the ")" to "RParen" to + // appease the macro gods: + write!( + f, + " +(@stack[$($stack:ident,)*] @queue[$($queue:ident,)*] @tail: ( $($stuff:tt)* ) $($tail:tt)* ) + => ( + __op_internal__!(@stack[LParen, $($stack,)*] @queue[$($queue,)*] + @tail: $($stuff)* RParen $($tail)*) +);" + )?; + + // ------- + // Case 5: Token is "RParen": + // 1. Pop from stack to queue until we see an "LParen", + // 2. Kill the "LParen", + // 3. If the top of the stack is a function, pop it onto the queue + // 2. Base case: + write!( + f, + " +(@stack[LParen, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: RParen $($tail:tt)*) => ( + __op_internal__!(@rp3 @stack[$($stack,)*] @queue[$($queue,)*] @tail: $($tail)*) +);" + )?; + // 1. Recursive case: + write!( + f, + " +(@stack[$stack_top:ident, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: RParen $($tail:tt)*) + => ( + __op_internal__!(@stack[$($stack,)*] @queue[$stack_top, $($queue,)*] @tail: RParen $($tail)*) +);" + )?; + // 3. Check for function: + for fun in ops.iter().filter(|f| f.op_type == Function) { + write!( + f, + " +(@rp3 @stack[{fun_op}, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail: $($tail:tt)*) => ( + __op_internal__!(@stack[$($stack,)*] @queue[{fun_op}, $($queue,)*] @tail: $($tail)*) +);", + fun_op = fun.operator + )?; + } + // 3. If no function found: + write!( + f, + " +(@rp3 @stack[$($stack:ident,)*] @queue[$($queue:ident,)*] @tail: $($tail:tt)*) => ( + __op_internal__!(@stack[$($stack,)*] @queue[$($queue,)*] @tail: $($tail)*) +);" + )?; + + // ------- + // Case 6: Token is a number: Push it onto the queue + write!( + f, + " +(@stack[$($stack:ident,)*] @queue[$($queue:ident,)*] @tail: $num:ident $($tail:tt)*) => ( + __op_internal__!(@stack[$($stack,)*] @queue[$num, $($queue,)*] @tail: $($tail)*) +);" + )?; + + // ------- + // Case 7: Out of tokens: + // Base case: Stack empty: Start evaluating + write!( + f, + " +(@stack[] @queue[$($queue:ident,)*] @tail: ) => ( + __op_internal__!(@reverse[] @input: $($queue,)*) +);" + )?; + // Recursive case: Pop stack to queue + write!( + f, + " +(@stack[$stack_top:ident, $($stack:ident,)*] @queue[$($queue:ident,)*] @tail:) => ( + __op_internal__!(@stack[$($stack,)*] @queue[$stack_top, $($queue,)*] @tail: ) +);" + )?; + + // ----------------------------------------------------------------------------------------- + // Stage 2: Reverse so we have RPN + write!( + f, + " +(@reverse[$($revved:ident,)*] @input: $head:ident, $($tail:ident,)* ) => ( + __op_internal__!(@reverse[$head, $($revved,)*] @input: $($tail,)*) +);" + )?; + write!( + f, + " +(@reverse[$($revved:ident,)*] @input: ) => ( + __op_internal__!(@eval @stack[] @input[$($revved,)*]) +);" + )?; + + // ----------------------------------------------------------------------------------------- + // Stage 3: Evaluate in Reverse Polish Notation + // Operators / Operators with 2 args: + for op in ops.iter().filter(|op| op.n_args == 2) { + // Note: We have to switch $a and $b here, otherwise non-commutative functions are backwards + write!( + f, + " +(@eval @stack[$a:ty, $b:ty, $($stack:ty,)*] @input[{op}, $($tail:ident,)*]) => ( + __op_internal__!(@eval @stack[$crate::{op}<$b, $a>, $($stack,)*] @input[$($tail,)*]) +);", + op = op.operator + )?; + } + // Operators with 1 arg: + for op in ops.iter().filter(|op| op.n_args == 1) { + write!( + f, + " +(@eval @stack[$a:ty, $($stack:ty,)*] @input[{op}, $($tail:ident,)*]) => ( + __op_internal__!(@eval @stack[$crate::{op}<$a>, $($stack,)*] @input[$($tail,)*]) +);", + op = op.operator + )?; + } + + // Wasn't a function or operator, so must be a value => push onto stack + write!( + f, + " +(@eval @stack[$($stack:ty,)*] @input[$head:ident, $($tail:ident,)*]) => ( + __op_internal__!(@eval @stack[$head, $($stack,)*] @input[$($tail,)*]) +);" + )?; + + // No input left: + write!( + f, + " +(@eval @stack[$stack:ty,] @input[]) => ( + $stack +);" + )?; + + // ----------------------------------------------------------------------------------------- + // Stage 0: Get it started + write!( + f, + " +($($tail:tt)* ) => ( + __op_internal__!(@stack[] @queue[] @tail: $($tail)*) +);" + )?; + + write!( + f, + " +}}" + )?; + + Ok(()) +} diff --git a/vendor/typenum/build/tests.rs b/vendor/typenum/build/tests.rs new file mode 100644 index 000000000..b0453a95f --- /dev/null +++ b/vendor/typenum/build/tests.rs @@ -0,0 +1,328 @@ +use std::{env, fmt, fs, io, path}; + +use super::{gen_int, gen_uint}; + +/// Computes the greatest common divisor of two integers. +fn gcdi(mut a: i64, mut b: i64) -> i64 { + a = a.abs(); + b = b.abs(); + + while a != 0 { + let tmp = b % a; + b = a; + a = tmp; + } + + b +} + +fn gcdu(mut a: u64, mut b: u64) -> u64 { + while a != 0 { + let tmp = b % a; + b = a; + a = tmp; + } + + b +} + +fn sign(i: i64) -> char { + use std::cmp::Ordering::*; + match i.cmp(&0) { + Greater => 'P', + Less => 'N', + Equal => '_', + } +} + +struct UIntTest { + a: u64, + op: &'static str, + b: Option<u64>, + r: u64, +} + +impl fmt::Display for UIntTest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.b { + Some(b) => write!( + f, + " +#[test] +#[allow(non_snake_case)] +fn test_{a}_{op}_{b}() {{ + type A = {gen_a}; + type B = {gen_b}; + type U{r} = {result}; + + #[allow(non_camel_case_types)] + type U{a}{op}U{b} = <<A as {op}<B>>::Output as Same<U{r}>>::Output; + + assert_eq!(<U{a}{op}U{b} as Unsigned>::to_u64(), <U{r} as Unsigned>::to_u64()); +}}", + gen_a = gen_uint(self.a), + gen_b = gen_uint(b), + r = self.r, + result = gen_uint(self.r), + a = self.a, + b = b, + op = self.op + ), + None => write!( + f, + " +#[test] +#[allow(non_snake_case)] +fn test_{a}_{op}() {{ + type A = {gen_a}; + type U{r} = {result}; + + #[allow(non_camel_case_types)] + type {op}U{a} = <<A as {op}>::Output as Same<U{r}>>::Output; + assert_eq!(<{op}U{a} as Unsigned>::to_u64(), <U{r} as Unsigned>::to_u64()); +}}", + gen_a = gen_uint(self.a), + r = self.r, + result = gen_uint(self.r), + a = self.a, + op = self.op + ), + } + } +} + +fn uint_binary_test(left: u64, operator: &'static str, right: u64, result: u64) -> UIntTest { + UIntTest { + a: left, + op: operator, + b: Option::Some(right), + r: result, + } +} + +// fn uint_unary_test(op: &'static str, a: u64, result: u64) -> UIntTest { +// UIntTest { a: a, op: op, b: Option::None, r: result } +// } + +struct IntBinaryTest { + a: i64, + op: &'static str, + b: i64, + r: i64, +} + +impl fmt::Display for IntBinaryTest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + " +#[test] +#[allow(non_snake_case)] +fn test_{sa}{a}_{op}_{sb}{b}() {{ + type A = {gen_a}; + type B = {gen_b}; + type {sr}{r} = {result}; + + #[allow(non_camel_case_types)] + type {sa}{a}{op}{sb}{b} = <<A as {op}<B>>::Output as Same<{sr}{r}>>::Output; + + assert_eq!(<{sa}{a}{op}{sb}{b} as Integer>::to_i64(), <{sr}{r} as Integer>::to_i64()); +}}", + gen_a = gen_int(self.a), + gen_b = gen_int(self.b), + r = self.r.abs(), + sr = sign(self.r), + result = gen_int(self.r), + a = self.a.abs(), + b = self.b.abs(), + sa = sign(self.a), + sb = sign(self.b), + op = self.op + ) + } +} + +fn int_binary_test(left: i64, operator: &'static str, right: i64, result: i64) -> IntBinaryTest { + IntBinaryTest { + a: left, + op: operator, + b: right, + r: result, + } +} + +struct IntUnaryTest { + op: &'static str, + a: i64, + r: i64, +} + +impl fmt::Display for IntUnaryTest { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + " +#[test] +#[allow(non_snake_case)] +fn test_{sa}{a}_{op}() {{ + type A = {gen_a}; + type {sr}{r} = {result}; + + #[allow(non_camel_case_types)] + type {op}{sa}{a} = <<A as {op}>::Output as Same<{sr}{r}>>::Output; + assert_eq!(<{op}{sa}{a} as Integer>::to_i64(), <{sr}{r} as Integer>::to_i64()); +}}", + gen_a = gen_int(self.a), + r = self.r.abs(), + sr = sign(self.r), + result = gen_int(self.r), + a = self.a.abs(), + sa = sign(self.a), + op = self.op + ) + } +} + +fn int_unary_test(operator: &'static str, num: i64, result: i64) -> IntUnaryTest { + IntUnaryTest { + op: operator, + a: num, + r: result, + } +} + +fn uint_cmp_test(a: u64, b: u64) -> String { + format!( + " +#[test] +#[allow(non_snake_case)] +fn test_{a}_Cmp_{b}() {{ + type A = {gen_a}; + type B = {gen_b}; + + #[allow(non_camel_case_types)] + type U{a}CmpU{b} = <A as Cmp<B>>::Output; + assert_eq!(<U{a}CmpU{b} as Ord>::to_ordering(), Ordering::{result:?}); +}}", + a = a, + b = b, + gen_a = gen_uint(a), + gen_b = gen_uint(b), + result = a.cmp(&b) + ) +} + +fn int_cmp_test(a: i64, b: i64) -> String { + format!( + " +#[test] +#[allow(non_snake_case)] +fn test_{sa}{a}_Cmp_{sb}{b}() {{ + type A = {gen_a}; + type B = {gen_b}; + + #[allow(non_camel_case_types)] + type {sa}{a}Cmp{sb}{b} = <A as Cmp<B>>::Output; + assert_eq!(<{sa}{a}Cmp{sb}{b} as Ord>::to_ordering(), Ordering::{result:?}); +}}", + a = a.abs(), + b = b.abs(), + sa = sign(a), + sb = sign(b), + gen_a = gen_int(a), + gen_b = gen_int(b), + result = a.cmp(&b) + ) +} + +// Allow for rustc 1.22 compatibility. +#[allow(bare_trait_objects)] +pub fn build_tests() -> Result<(), Box<::std::error::Error>> { + // will test all permutations of number pairs up to this (and down to its opposite for ints) + let high: i64 = 5; + + let uints = (0u64..high as u64 + 1).flat_map(|a| (a..a + 1).cycle().zip(0..high as u64 + 1)); + let ints = (-high..high + 1).flat_map(|a| (a..a + 1).cycle().zip(-high..high + 1)); + + let out_dir = env::var("OUT_DIR")?; + let dest = path::Path::new(&out_dir).join("tests.rs"); + let f = fs::File::create(&dest)?; + let mut writer = io::BufWriter::new(&f); + use std::io::Write; + writer.write_all( + b" +extern crate typenum; + +use std::ops::*; +use std::cmp::Ordering; +use typenum::*; +", + )?; + use std::cmp; + // uint operators: + for (a, b) in uints { + write!(writer, "{}", uint_binary_test(a, "BitAnd", b, a & b))?; + write!(writer, "{}", uint_binary_test(a, "BitOr", b, a | b))?; + write!(writer, "{}", uint_binary_test(a, "BitXor", b, a ^ b))?; + write!(writer, "{}", uint_binary_test(a, "Shl", b, a << b))?; + write!(writer, "{}", uint_binary_test(a, "Shr", b, a >> b))?; + write!(writer, "{}", uint_binary_test(a, "Add", b, a + b))?; + write!(writer, "{}", uint_binary_test(a, "Min", b, cmp::min(a, b)))?; + write!(writer, "{}", uint_binary_test(a, "Max", b, cmp::max(a, b)))?; + write!(writer, "{}", uint_binary_test(a, "Gcd", b, gcdu(a, b)))?; + if a >= b { + write!(writer, "{}", uint_binary_test(a, "Sub", b, a - b))?; + } + write!(writer, "{}", uint_binary_test(a, "Mul", b, a * b))?; + if b != 0 { + write!(writer, "{}", uint_binary_test(a, "Div", b, a / b))?; + write!(writer, "{}", uint_binary_test(a, "Rem", b, a % b))?; + if a % b == 0 { + write!(writer, "{}", uint_binary_test(a, "PartialDiv", b, a / b))?; + } + } + write!(writer, "{}", uint_binary_test(a, "Pow", b, a.pow(b as u32)))?; + write!(writer, "{}", uint_cmp_test(a, b))?; + } + // int operators: + for (a, b) in ints { + write!(writer, "{}", int_binary_test(a, "Add", b, a + b))?; + write!(writer, "{}", int_binary_test(a, "Sub", b, a - b))?; + write!(writer, "{}", int_binary_test(a, "Mul", b, a * b))?; + write!(writer, "{}", int_binary_test(a, "Min", b, cmp::min(a, b)))?; + write!(writer, "{}", int_binary_test(a, "Max", b, cmp::max(a, b)))?; + write!(writer, "{}", int_binary_test(a, "Gcd", b, gcdi(a, b)))?; + if b != 0 { + write!(writer, "{}", int_binary_test(a, "Div", b, a / b))?; + write!(writer, "{}", int_binary_test(a, "Rem", b, a % b))?; + if a % b == 0 { + write!(writer, "{}", int_binary_test(a, "PartialDiv", b, a / b))?; + } + } + if b >= 0 || a.abs() == 1 { + let result = if b < 0 { + if a == 1 { + a + } else if a == -1 { + a.pow((-b) as u32) + } else { + unreachable!() + } + } else { + a.pow(b as u32) + }; + write!(writer, "{}", int_binary_test(a, "Pow", b, result))?; + } + write!(writer, "{}", int_cmp_test(a, b))?; + } + + // int unary operators: + for n in -high..high + 1 { + write!(writer, "{}", int_unary_test("Neg", n, -n))?; + write!(writer, "{}", int_unary_test("Abs", n, n.abs()))?; + } + + writer.flush()?; + + Ok(()) +} |