summaryrefslogtreecommitdiffstats
path: root/vendor/typenum/build
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/typenum/build
parentInitial commit. (diff)
downloadrustc-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.rs186
-rw-r--r--vendor/typenum/build/op.rs559
-rw-r--r--vendor/typenum/build/tests.rs328
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(())
+}