diff --git a/packages/macros/README.md b/packages/macros/README.md index 1a8aefbe..d310fafc 100644 --- a/packages/macros/README.md +++ b/packages/macros/README.md @@ -2,12 +2,70 @@ A collection of useful macros. -- [`pow!`](#pow) +- [Add, Sub, Mul, Div derives](#add-sub-mul-div-derives) +- [AddAssign, SubAssign, MulAssign, DivAssign derives](#addassign-subassign-mulassign-divassign-derives) +- [pow!](#pow) +- [Zero derive](#zero-derive) -## `pow!` +## Add, Sub, Mul, Div derives + +These macros add the ability to use addition `+`, subtraction `-`, multiplication `*` and division `/` operators on the type that derives them. All individual members of the struct must already support a particular operation for a derive to work. + +```rust +#[derive(Add, Sub, Mul, Div)] +struct Point { + x: u32, + y: u32 +} + +let a = Point { x: 6, y: 32 }; +let b = Point { x: 2, y: 2 }; +let c = a + b; // Point { x: 8, y: 34 } +let d = a - b; // Point { x: 4, y: 30 } +let e = a * b; // Point { x: 12, y: 64 } +let f = a / b; // Point { x: 3, y: 16 } +``` + +## AddAssign, SubAssign, MulAssign, DivAssign derives + +These macros add the ability to use add and assign `+=`, subtract and assign `-=`, multiply and assign `*=` and divide and assign `/=` operators on the type that derives them. All individual members of the struct must already support the particular operation for a derive to work. + +```rust +#[derive(AddAssign, SubAssign, MulAssign, DivAssign)] +struct Point { + x: u32, + y: u32 +} + +let mut a = Point { x: 6, y: 32 }; +let b = Point { x: 2, y: 2 }; +a += b; // Point { x: 8, y: 34 } +a -= b; // Point { x: 6, y: 32 } +a *= b; // Point { x: 12, y: 64 } +a /= b; // Point { x: 6, y: 32 }; +``` + +## pow! Power function. Takes two arguments, `x, y`, calculates the value of `x` raised to the power of `y`. ```cairo const MEGABYTE: u64 = pow!(2, 20); // will be set to 1048576 ``` + +## Zero derive + +Adds implementation of the `core::num::traits::Zero` trait. + +All members of the struct must already implement the `Zero` trait. + +```rust +#[derive(Zero, PartialEq, Debug)] +struct Point { + x: u64, + y: u64, +} +assert_eq!(Point { x: 0, y: 0 }, Zero::zero()); +assert!(Point { x: 0, y: 0 }.is_zero()); +assert!(Point { x: 1, y: 0 }.is_non_zero()); +``` diff --git a/packages/macros/src/lib.rs b/packages/macros/src/lib.rs index 5ad557c5..cbea9c56 100644 --- a/packages/macros/src/lib.rs +++ b/packages/macros/src/lib.rs @@ -1,54 +1,5 @@ -use bigdecimal::{num_traits::pow, BigDecimal}; -use cairo_lang_macro::{inline_macro, Diagnostic, ProcMacroResult, TokenStream}; -use cairo_lang_parser::utils::SimpleParserDatabase; -use cairo_lang_syntax::node::kind::SyntaxKind::Arg; +mod num_traits; +mod pow; +mod zero_trait; -/// Compile-time power function. -/// -/// Takes two arguments, `x, y`, calculates the value of `x` raised to the power of `y`. -/// -/// ``` -/// const MEGABYTE: u64 = pow!(2, 20); -/// assert_eq!(MEGABYTE, 1048576); -/// ``` -#[inline_macro] -pub fn pow(token_stream: TokenStream) -> ProcMacroResult { - let db = SimpleParserDatabase::default(); - let (parsed, _diag) = db.parse_virtual_with_diagnostics(token_stream); - - let macro_args: Vec = parsed - .descendants(&db) - .filter_map(|node| { - if let Arg = node.kind(&db) { - Some(node.get_text(&db)) - } else { - None - } - }) - .collect(); - - if macro_args.len() != 2 { - return ProcMacroResult::new(TokenStream::empty()) - .with_diagnostics(Diagnostic::error("Invalid number of arguments").into()); - } - - let base: BigDecimal = match macro_args[0].parse() { - Ok(val) => val, - Err(_) => { - return ProcMacroResult::new(TokenStream::empty()) - .with_diagnostics(Diagnostic::error("Invalid base value").into()); - } - }; - - let exp: usize = match macro_args[1].parse() { - Ok(val) => val, - Err(_) => { - return ProcMacroResult::new(TokenStream::empty()) - .with_diagnostics(Diagnostic::error("Invalid exponent value").into()); - } - }; - - let result: BigDecimal = pow(base, exp); - - ProcMacroResult::new(TokenStream::new(result.to_string())) -} +mod parse; diff --git a/packages/macros/src/num_traits.rs b/packages/macros/src/num_traits.rs new file mode 100644 index 00000000..48be4578 --- /dev/null +++ b/packages/macros/src/num_traits.rs @@ -0,0 +1,222 @@ +use cairo_lang_macro::{derive_macro, ProcMacroResult, TokenStream}; + +use crate::parse::{parse_struct_info, StructInfo}; + +struct OpInfo { + trait_name: String, + fn_name: String, + operator: String, +} + +fn generate_op_trait_impl(op_info: &OpInfo, s: &StructInfo) -> String { + let generic_params = s + .generic_params + .as_ref() + .map_or(String::new(), |params| format!("<{}>", params.join(", "))); + + let trait_bounds = s + .generic_params + .as_ref() + .map_or_else(String::new, |params| { + let bounds = params + .iter() + .flat_map(|param| { + vec![ + format!("+core::traits::{}<{}>", op_info.trait_name, param), + format!("+core::traits::Drop<{}>", param), + ] + }) + .collect::>() + .join(",\n"); + format!("<{},\n{}>", params.join(", "), bounds) + }); + + let members_op = s + .members + .iter() + .map(|member| format!("{0}: lhs.{0} {1} rhs.{0}", member, op_info.operator)) + .collect::>() + .join(", "); + + format!( + "\n +impl {0}{1}{2} +of core::traits::{1}<{0}{3}> {{ + fn {4}(lhs: {0}{3}, rhs: {0}{3}) -> {0}{3} {{ + {0} {{ {5} }} + }} +}}\n", + s.name, op_info.trait_name, trait_bounds, generic_params, op_info.fn_name, members_op + ) +} + +fn generate_op_assign_trait_impl(op_info: &OpInfo, s: &StructInfo) -> String { + let generic_params = s + .generic_params + .as_ref() + .map_or(String::new(), |params| format!("<{}>", params.join(", "))); + + let trait_bounds = s + .generic_params + .as_ref() + .map_or_else(String::new, |params| { + let bounds = params + .iter() + .flat_map(|param| { + vec![ + format!("+core::ops::{0}Assign<{1}, {1}>", op_info.trait_name, param), + format!("+core::traits::Drop<{}>", param), + ] + }) + .collect::>() + .join(",\n"); + format!("<{},\n{}>", params.join(", "), bounds) + }); + + let members_op = s + .members + .iter() + .map(|member| format!("self.{0} {1}= rhs.{0}", member, op_info.operator)) + .collect::>() + .join(";\n "); + + format!( + "\n +impl {0}{1}Assign{2} +of core::ops::{1}Assign<{0}{3}, {0}{3}> {{ + fn {4}_assign(ref self: {0}{3}, rhs: {0}{3}) {{ + {5}; + }} +}}\n", + s.name, op_info.trait_name, trait_bounds, generic_params, op_info.fn_name, members_op + ) +} + +/// Adds implementation for the `core::traits::Add` trait. +/// +/// Allows you to use the `+` oprator on a type. All members of +/// the struct must already implement the `Add` trait. +#[derive_macro] +pub fn add(token_stream: TokenStream) -> ProcMacroResult { + let op = OpInfo { + trait_name: "Add".to_string(), + fn_name: "add".to_string(), + operator: "+".to_string(), + }; + + let s = parse_struct_info(token_stream); + + ProcMacroResult::new(TokenStream::new(generate_op_trait_impl(&op, &s))) +} + +/// Adds implementation for the `core::traits::Sub` trait. +/// +/// Allows you to use the `-` operator on a type. All members of +/// the struct must already implement the `Sub` trait. +#[derive_macro] +pub fn sub(token_stream: TokenStream) -> ProcMacroResult { + let op = OpInfo { + trait_name: "Sub".to_string(), + fn_name: "sub".to_string(), + operator: "-".to_string(), + }; + let s = parse_struct_info(token_stream); + + ProcMacroResult::new(TokenStream::new(generate_op_trait_impl(&op, &s))) +} + +/// Adds implementation for the `core::traits::Mul` trait. +/// +/// Allows you to use the `*` operator on a type. All members of +/// the struct must already implement the `Mul` trait. +#[derive_macro] +pub fn mul(token_stream: TokenStream) -> ProcMacroResult { + let op = OpInfo { + trait_name: "Mul".to_string(), + fn_name: "mul".to_string(), + operator: "*".to_string(), + }; + let s = parse_struct_info(token_stream); + + ProcMacroResult::new(TokenStream::new(generate_op_trait_impl(&op, &s))) +} + +/// Adds implementation for the `core::traits::Div` trait. +/// +/// Allows you to use the `/` operator on a type. All members of +/// the struct must already implement the `Div` trait. +#[derive_macro] +pub fn div(token_stream: TokenStream) -> ProcMacroResult { + let op = OpInfo { + trait_name: "Div".to_string(), + fn_name: "div".to_string(), + operator: "/".to_string(), + }; + let s = parse_struct_info(token_stream); + + ProcMacroResult::new(TokenStream::new(generate_op_trait_impl(&op, &s))) +} + +/// Adds implementation for the `core::ops::AddAssign` trait. +/// +/// Allows you to use the `+=` operator on a type. All members of +/// the struct must already implement the `AddAssign` trait. +#[derive_macro] +fn add_assign(token_stream: TokenStream) -> ProcMacroResult { + let op = OpInfo { + trait_name: "Add".to_string(), + fn_name: "add".to_string(), + operator: "+".to_string(), + }; + let s = parse_struct_info(token_stream); + + ProcMacroResult::new(TokenStream::new(generate_op_assign_trait_impl(&op, &s))) +} + +/// Adds implementation for the `core::ops::SubAssign` trait. +/// +/// Allows you to use the `-=` operator on a type. All members of +/// the struct must already implement the `SubAssign` trait. +#[derive_macro] +fn sub_assign(token_stream: TokenStream) -> ProcMacroResult { + let op = OpInfo { + trait_name: "Sub".to_string(), + fn_name: "sub".to_string(), + operator: "-".to_string(), + }; + let s = parse_struct_info(token_stream); + + ProcMacroResult::new(TokenStream::new(generate_op_assign_trait_impl(&op, &s))) +} + +/// Adds implementation for the `core::ops::MulAssign` trait. +/// +/// Allows you to use the `*=` operator on a type. All members of +/// the struct must already implement the `MulAssign` trait. +#[derive_macro] +fn mul_assign(token_stream: TokenStream) -> ProcMacroResult { + let op = OpInfo { + trait_name: "Mul".to_string(), + fn_name: "mul".to_string(), + operator: "*".to_string(), + }; + let s = parse_struct_info(token_stream); + + ProcMacroResult::new(TokenStream::new(generate_op_assign_trait_impl(&op, &s))) +} + +/// Adds implementation for the `core::ops::DivAssign` trait. +/// +/// Allows you to use the `/=` operator on a type. All members of +/// the struct must already implement the `DivAssign` trait. +#[derive_macro] +fn div_assign(token_stream: TokenStream) -> ProcMacroResult { + let op = OpInfo { + trait_name: "Div".to_string(), + fn_name: "div".to_string(), + operator: "/".to_string(), + }; + let s = parse_struct_info(token_stream); + + ProcMacroResult::new(TokenStream::new(generate_op_assign_trait_impl(&op, &s))) +} diff --git a/packages/macros/src/parse.rs b/packages/macros/src/parse.rs new file mode 100644 index 00000000..57ac0d42 --- /dev/null +++ b/packages/macros/src/parse.rs @@ -0,0 +1,69 @@ +use cairo_lang_macro::TokenStream; +use cairo_lang_parser::utils::SimpleParserDatabase; +use cairo_lang_syntax::node::kind::SyntaxKind::{ + Member, OptionWrappedGenericParamListEmpty, TerminalStruct, TokenIdentifier, + WrappedGenericParamList, +}; + +pub(crate) struct StructInfo { + pub(crate) name: String, + pub(crate) generic_params: Option>, + pub(crate) members: Vec, +} + +pub(crate) fn parse_struct_info(token_stream: TokenStream) -> StructInfo { + let db = SimpleParserDatabase::default(); + let (parsed, _diag) = db.parse_virtual_with_diagnostics(token_stream); + let mut nodes = parsed.descendants(&db); + + // find struct name - the next TokenIdentifier after TeminalStruct + let mut struct_name = String::new(); + for node in nodes.by_ref() { + if node.kind(&db) == TerminalStruct { + struct_name = nodes + .find(|node| node.kind(&db) == TokenIdentifier) + .unwrap() + .get_text(&db); + break; + } + } + + // collect generic params or skip if there aren't any + let mut generic_params: Option> = None; + for node in nodes.by_ref() { + match node.kind(&db) { + WrappedGenericParamList => { + let params = node + .descendants(&db) + .filter(|node| node.kind(&db) == TokenIdentifier) + .map(|node| node.get_text(&db)) + .collect(); + generic_params = Some(params); + break; + } + OptionWrappedGenericParamListEmpty => { + break; + } + _ => {} + } + } + + // collect struct members - all TokenIdentifier nodes after each Member + let mut members = Vec::new(); + for node in nodes { + if node.kind(&db) == Member { + let member = node + .descendants(&db) + .find(|node| node.kind(&db) == TokenIdentifier) + .map(|node| node.get_text(&db)) + .unwrap(); + members.push(member); + } + } + + StructInfo { + name: struct_name, + generic_params, + members, + } +} diff --git a/packages/macros/src/pow.rs b/packages/macros/src/pow.rs new file mode 100644 index 00000000..5ad557c5 --- /dev/null +++ b/packages/macros/src/pow.rs @@ -0,0 +1,54 @@ +use bigdecimal::{num_traits::pow, BigDecimal}; +use cairo_lang_macro::{inline_macro, Diagnostic, ProcMacroResult, TokenStream}; +use cairo_lang_parser::utils::SimpleParserDatabase; +use cairo_lang_syntax::node::kind::SyntaxKind::Arg; + +/// Compile-time power function. +/// +/// Takes two arguments, `x, y`, calculates the value of `x` raised to the power of `y`. +/// +/// ``` +/// const MEGABYTE: u64 = pow!(2, 20); +/// assert_eq!(MEGABYTE, 1048576); +/// ``` +#[inline_macro] +pub fn pow(token_stream: TokenStream) -> ProcMacroResult { + let db = SimpleParserDatabase::default(); + let (parsed, _diag) = db.parse_virtual_with_diagnostics(token_stream); + + let macro_args: Vec = parsed + .descendants(&db) + .filter_map(|node| { + if let Arg = node.kind(&db) { + Some(node.get_text(&db)) + } else { + None + } + }) + .collect(); + + if macro_args.len() != 2 { + return ProcMacroResult::new(TokenStream::empty()) + .with_diagnostics(Diagnostic::error("Invalid number of arguments").into()); + } + + let base: BigDecimal = match macro_args[0].parse() { + Ok(val) => val, + Err(_) => { + return ProcMacroResult::new(TokenStream::empty()) + .with_diagnostics(Diagnostic::error("Invalid base value").into()); + } + }; + + let exp: usize = match macro_args[1].parse() { + Ok(val) => val, + Err(_) => { + return ProcMacroResult::new(TokenStream::empty()) + .with_diagnostics(Diagnostic::error("Invalid exponent value").into()); + } + }; + + let result: BigDecimal = pow(base, exp); + + ProcMacroResult::new(TokenStream::new(result.to_string())) +} diff --git a/packages/macros/src/zero_trait.rs b/packages/macros/src/zero_trait.rs new file mode 100644 index 00000000..8a66312c --- /dev/null +++ b/packages/macros/src/zero_trait.rs @@ -0,0 +1,81 @@ +use crate::parse::{parse_struct_info, StructInfo}; +use cairo_lang_macro::{derive_macro, ProcMacroResult, TokenStream}; + +fn generate_zero_trait_impl(s: &StructInfo) -> String { + let generic_params = s + .generic_params + .as_ref() + .map_or(String::new(), |params| format!("<{}>", params.join(", "))); + + let trait_bounds = s + .generic_params + .as_ref() + .map_or_else(String::new, |params| { + let bounds = params + .iter() + .flat_map(|param| { + vec![ + format!("+core::num::traits::Zero<{}>", param), + format!("+core::traits::Drop<{}>", param), + ] + }) + .collect::>() + .join(",\n"); + format!("<{},\n{}>", params.join(", "), bounds) + }); + + let zero_fn = s + .members + .iter() + .map(|member| format!("{}: core::num::traits::Zero::zero()", member)) + .collect::>() + .join(", "); + + let is_zero_fn = s + .members + .iter() + .map(|member| format!("self.{}.is_zero()", member)) + .collect::>() + .join(" && "); + + format!( + "\n +impl {0}ZeroImpl{1} +of core::num::traits::Zero<{0}{2}> {{ + fn zero() -> {0}{2} {{ + {0} {{ {3} }} + }} + + fn is_zero(self: @{0}{2}) -> bool {{ + {4} + }} + + fn is_non_zero(self: @{0}{2}) -> bool {{ + !self.is_zero() + }} +}}\n", + s.name, trait_bounds, generic_params, zero_fn, is_zero_fn + ) +} + +/// Adds implementation of the `core::num::traits::Zero` trait. +/// +/// All members of the struct must already implement the `Zero` trait. +/// +/// ``` +/// #[derive(Zero, PartialEq, Debug)] +/// struct Point { +/// x: u64, +/// y: u64, +/// } +/// +/// assert_eq!(Point { x: 0, y: 0 }, Zero::zero()); +/// assert!(Point { x: 0, y: 0 }.is_zero()); +/// assert!(Point { x: 1, y: 0 }.is_non_zero()); +/// ``` +#[derive_macro] +pub fn zero(token_stream: TokenStream) -> ProcMacroResult { + let s = parse_struct_info(token_stream); + + ProcMacroResult::new(TokenStream::new(generate_zero_trait_impl(&s))) +} diff --git a/packages/macros_tests/src/lib.cairo b/packages/macros_tests/src/lib.cairo index 6023f41d..c76e302c 100644 --- a/packages/macros_tests/src/lib.cairo +++ b/packages/macros_tests/src/lib.cairo @@ -1,15 +1,8 @@ -const TWO_TEN: u128 = pow!(2, 10); +#[cfg(test)] +mod test_pow; + +#[cfg(test)] +mod test_num_traits; #[cfg(test)] -mod tests { - #[test] - fn test_pow_macro() { - assert_eq!(super::TWO_TEN, 1024); - assert_eq!(pow!(10, 2), 100); - assert_eq!(pow!(20, 30), 1073741824000000000000000000000000000000_felt252); - assert_eq!( - pow!(2, 255), - 57896044618658097711785492504343953926634992332820282019728792003956564819968_u256 - ); - } -} +mod test_zero_trait; diff --git a/packages/macros_tests/src/test_num_traits.cairo b/packages/macros_tests/src/test_num_traits.cairo new file mode 100644 index 00000000..6e9492dd --- /dev/null +++ b/packages/macros_tests/src/test_num_traits.cairo @@ -0,0 +1,167 @@ +// a basic struct +#[derive(Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Debug, Drop, PartialEq)] +struct B { + pub a: u8, + b: u16 +} + +// a generic struct +#[derive(Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Debug, Drop, PartialEq)] +struct G { + x: T1, + pub y: T2, + z: T2 +} + + +// a complex struct +#[derive(Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign, Debug, Drop, PartialEq)] +struct C { + pub g: G, + i: u64, + j: u32 +} + +#[test] +fn test_add_derive() { + let b1 = B { a: 1, b: 2 }; + let b2 = B { a: 3, b: 4 }; + let b3 = b1 + b2; + assert_eq!(b3, B { a: 4, b: 6 }); + + let g1: G = G { x: 1, y: 2, z: 3 }; + let g2: G = G { x: 3, y: 4, z: 6 }; + let g3 = g1 + g2; + assert_eq!(g3, G { x: 4, y: 6, z: 9 }); + + let c1 = C { g: G { x: 1, y: 2, z: 3 }, i: 4, j: 5 }; + let c2 = C { g: G { x: 3, y: 4, z: 6 }, i: 8, j: 10 }; + let c3 = c1 + c2; + assert_eq!(c3, C { g: G { x: 4, y: 6, z: 9 }, i: 12, j: 15 }); +} + +#[test] +fn test_sub_derive() { + let b1 = B { a: 4, b: 8 }; + let b2 = B { a: 2, b: 8 }; + let b3 = b1 - b2; + assert_eq!(b3, B { a: 2, b: 0 }); + + let g1: G = G { x: 4, y: 8, z: 16 }; + let g2: G = G { x: 2, y: 4, z: 16 }; + let g3 = g1 - g2; + assert_eq!(g3, G { x: 2, y: 4, z: 0 }); + + let c1 = C { g: G { x: 4, y: 8, z: 16 }, i: 32, j: 64 }; + let c2 = C { g: G { x: 2, y: 4, z: 16 }, i: 16, j: 60 }; + let c3 = c1 - c2; + assert_eq!(c3, C { g: G { x: 2, y: 4, z: 0 }, i: 16, j: 4 }); +} + +#[test] +fn test_mul_derive() { + let b1 = B { a: 4, b: 8 }; + let b2 = B { a: 2, b: 8 }; + let b3 = b1 * b2; + assert_eq!(b3, B { a: 8, b: 64 }); + + let g1: G = G { x: 4, y: 8, z: 16 }; + let g2: G = G { x: 2, y: 4, z: 16 }; + let g3 = g1 * g2; + assert_eq!(g3, G { x: 8, y: 32, z: 256 }); + + let c1 = C { g: G { x: 4, y: 8, z: 16 }, i: 32, j: 64 }; + let c2 = C { g: G { x: 2, y: 4, z: 16 }, i: 16, j: 60 }; + let c3 = c1 * c2; + assert_eq!(c3, C { g: G { x: 8, y: 32, z: 256 }, i: 512, j: 3840 }); +} + +#[test] +fn test_div_derive() { + let b1 = B { a: 4, b: 8 }; + let b2 = B { a: 2, b: 8 }; + let b3 = b1 / b2; + assert_eq!(b3, B { a: 2, b: 1 }); + + let g1: G = G { x: 4, y: 8, z: 16 }; + let g2: G = G { x: 2, y: 4, z: 16 }; + let g3 = g1 / g2; + assert_eq!(g3, G { x: 2, y: 2, z: 1 }); + + let c1 = C { g: G { x: 4, y: 8, z: 16 }, i: 32, j: 64 }; + let c2 = C { g: G { x: 2, y: 4, z: 1 }, i: 16, j: 32 }; + let c3 = c1 / c2; + assert_eq!(c3, C { g: G { x: 2, y: 2, z: 16 }, i: 2, j: 2 }); +} + +#[test] +fn test_addassign_derive() { + let mut b1 = B { a: 1, b: 2 }; + let b2 = B { a: 3, b: 4 }; + b1 += b2; + assert_eq!(b1, B { a: 4, b: 6 }); + + let mut g1: G = G { x: 1, y: 2, z: 3 }; + let g2: G = G { x: 3, y: 4, z: 6 }; + g1 += g2; + assert_eq!(g1, G { x: 4, y: 6, z: 9 }); + + let mut c1 = C { g: G { x: 1, y: 2, z: 3 }, i: 4, j: 5 }; + let c2 = C { g: G { x: 3, y: 4, z: 6 }, i: 8, j: 10 }; + c1 += c2; + assert_eq!(c1, C { g: G { x: 4, y: 6, z: 9 }, i: 12, j: 15 }); +} + +#[test] +fn test_subassign_derive() { + let mut b1 = B { a: 4, b: 8 }; + let b2 = B { a: 2, b: 8 }; + b1 -= b2; + assert_eq!(b1, B { a: 2, b: 0 }); + + let mut g1: G = G { x: 4, y: 8, z: 16 }; + let g2: G = G { x: 2, y: 4, z: 16 }; + g1 -= g2; + assert_eq!(g1, G { x: 2, y: 4, z: 0 }); + + let mut c1 = C { g: G { x: 4, y: 8, z: 16 }, i: 32, j: 64 }; + let c2 = C { g: G { x: 2, y: 4, z: 16 }, i: 16, j: 60 }; + c1 -= c2; + assert_eq!(c1, C { g: G { x: 2, y: 4, z: 0 }, i: 16, j: 4 }); +} + +#[test] +fn test_mulassign_derive() { + let mut b1 = B { a: 4, b: 8 }; + let b2 = B { a: 2, b: 8 }; + b1 *= b2; + assert_eq!(b1, B { a: 8, b: 64 }); + + let mut g1: G = G { x: 4, y: 8, z: 16 }; + let g2: G = G { x: 2, y: 4, z: 16 }; + g1 *= g2; + assert_eq!(g1, G { x: 8, y: 32, z: 256 }); + + let mut c1 = C { g: G { x: 4, y: 8, z: 16 }, i: 32, j: 64 }; + let c2 = C { g: G { x: 2, y: 4, z: 16 }, i: 16, j: 60 }; + c1 *= c2; + assert_eq!(c1, C { g: G { x: 8, y: 32, z: 256 }, i: 512, j: 3840 }); +} + +#[test] +fn test_divassign_derive() { + let mut b1 = B { a: 4, b: 8 }; + let b2 = B { a: 2, b: 8 }; + b1 /= b2; + assert_eq!(b1, B { a: 2, b: 1 }); + + let mut g1: G = G { x: 4, y: 8, z: 16 }; + let g2: G = G { x: 2, y: 4, z: 16 }; + g1 /= g2; + assert_eq!(g1, G { x: 2, y: 2, z: 1 }); + + let mut c1 = C { g: G { x: 4, y: 8, z: 16 }, i: 32, j: 64 }; + let c2 = C { g: G { x: 2, y: 4, z: 1 }, i: 16, j: 32 }; + c1 /= c2; + assert_eq!(c1, C { g: G { x: 2, y: 2, z: 16 }, i: 2, j: 2 }); +} diff --git a/packages/macros_tests/src/test_pow.cairo b/packages/macros_tests/src/test_pow.cairo new file mode 100644 index 00000000..1ea1b6fe --- /dev/null +++ b/packages/macros_tests/src/test_pow.cairo @@ -0,0 +1,12 @@ +const TWO_TEN: u128 = pow!(2, 10); + +#[test] +fn test_pow_macro() { + assert_eq!(TWO_TEN, 1024); + assert_eq!(pow!(10, 2), 100); + assert_eq!(pow!(20, 30), 1073741824000000000000000000000000000000_felt252); + assert_eq!( + pow!(2, 255), + 57896044618658097711785492504343953926634992332820282019728792003956564819968_u256 + ); +} diff --git a/packages/macros_tests/src/test_zero_trait.cairo b/packages/macros_tests/src/test_zero_trait.cairo new file mode 100644 index 00000000..a2b7b087 --- /dev/null +++ b/packages/macros_tests/src/test_zero_trait.cairo @@ -0,0 +1,55 @@ +use core::num::traits::Zero; + +// a basic struct +#[derive(Zero, Debug, Drop, PartialEq)] +struct B { + pub a: u8, + b: u16 +} + +// a generic struct +#[derive(Zero, Debug, Drop, PartialEq)] +struct G { + x: T1, + pub y: T2, + z: T2 +} + +// a complex struct +#[derive(Zero, Debug, Drop, PartialEq)] +struct C { + pub g: G, + i: u64, + j: u32 +} + + +#[test] +fn test_zero_derive() { + let b0: B = B { a: 0, b: 0 }; + let b1: B = B { a: 1, b: 2 }; + + assert_eq!(b0, Zero::zero()); + assert!(b0.is_zero()); + assert!(b0.is_non_zero() == false); + assert!(b1.is_zero() == false); + assert!(b1.is_non_zero()); + + let g0: G = G { x: 0, y: 0, z: 0 }; + let g1: G = G { x: 1, y: 2, z: 3 }; + + assert_eq!(g0, Zero::zero()); + assert!(g0.is_zero()); + assert!(g0.is_non_zero() == false); + assert!(g1.is_zero() == false); + assert!(g1.is_non_zero()); + + let c0: C = C { g: G { x: 0, y: 0, z: 0 }, i: 0, j: 0 }; + let c1: C = C { g: G { x: 0, y: 0, z: 0 }, i: 4, j: 5 }; + + assert_eq!(c0, Zero::zero()); + assert!(c0.is_zero()); + assert!(c0.is_non_zero() == false); + assert!(c1.is_zero() == false); + assert!(c1.is_non_zero()); +}