diff --git a/programs/compile_benches/dijkstra.cairo b/programs/compile_benches/dijkstra.cairo index 30213737ba..f2a6c3a778 100644 --- a/programs/compile_benches/dijkstra.cairo +++ b/programs/compile_benches/dijkstra.cairo @@ -1,31 +1,47 @@ // Copied from: https://github.com/keep-starknet-strange/alexandria // License: MIT -use nullable::FromNullableResult; +use core::nullable::{FromNullableResult, match_nullable}; //! Dijkstra algorithm using priority queue #[derive(Copy, Drop)] -struct Node { +pub struct Node { source: u32, dest: u32, weight: u128 } +#[generate_trait] +pub impl NodeGetters of NodeGettersTrait { + fn weight(self: @Node) -> @u128 { + self.weight + } + + fn dest(self: @Node) -> @u32 { + self.dest + } + + fn source(self: @Node) -> @u32 { + self.source + } +} /// Graph representation. -struct Graph { - nodes: Array, +pub struct Graph { + pub(crate) nodes: Array, adj_nodes: Felt252Dict, } /// Graph trait. -trait GraphTrait { +pub trait GraphTrait { /// Create a new graph instance. fn new() -> Graph>>; /// add an edge to graph fn add_edge(ref self: Graph>>, source: u32, dest: u32, weight: u128); /// return shortest path from s fn shortest_path(ref self: Graph>>, source: u32) -> Felt252Dict; + /// return shortest path from s + fn adj_nodes(ref self: Graph>>, source: felt252) -> Nullable>; } impl DestructGraph, +Felt252DictValue> of Destruct> { @@ -42,7 +58,7 @@ impl GraphImpl of GraphTrait { fn add_edge(ref self: Graph>>, source: u32, dest: u32, weight: u128) { let adj_nodes = self.adj_nodes.get(source.into()); let mut nodes: Array = array![]; - let mut is_null: bool = false; + let mut is_null = false; let node = Node { source, dest, weight }; let mut span = match match_nullable(adj_nodes) { FromNullableResult::Null => { @@ -54,47 +70,41 @@ impl GraphImpl of GraphTrait { }; // iterate over existing array to add new node - if (!is_null) { - let mut index = 0; - loop { - if index >= span.len() { - break; - } - - let new_node = *span.get(index).unwrap().unbox(); - nodes.append(new_node); - index += 1; + if !is_null { + for current_value in span { + nodes.append(*current_value); }; nodes.append(node); } // add node self.nodes.append(node); // add adj node - self.adj_nodes.insert(source.into(), nullable_from_box(BoxTrait::new(nodes.span()))); + self.adj_nodes.insert(source.into(), NullableTrait::new(nodes.span())); } fn shortest_path(ref self: Graph>>, source: u32) -> Felt252Dict { dijkstra(ref self, source) } + + + fn adj_nodes(ref self: Graph>>, source: felt252) -> Nullable> { + self.adj_nodes.get(source) + } } -fn dijkstra(ref self: Graph>>, source: u32) -> Felt252Dict { +pub fn dijkstra(ref self: Graph>>, source: u32) -> Felt252Dict { let mut priority_queue = array![]; let mut visited_node = array![]; let mut dist: Felt252Dict = Default::default(); let node_size = self.nodes.len(); let nodes = self.nodes.span(); // add first node to pripority queue - let initial_node = Node { source: source, dest: 0, weight: 0 }; + let initial_node = Node { source, dest: 0, weight: 0 }; priority_queue.append(initial_node); // init dist with infinite value let mut index = 0; - loop { - if index == node_size { - break; - } - + while index != node_size { let current_node = *nodes.at(index); dist.insert(current_node.dest.into(), 255_u128); index += 1; @@ -106,11 +116,7 @@ fn dijkstra(ref self: Graph>>, source: u32) -> Felt252Dict>>, source: u32) -> Felt252Dict>>, source: u32) -> Felt252Dict>>, source: u32) -> Felt252Dict, current_node: u32) -> bool { let mut index = 0; - let mut is_visited = false; let n = nodes.span(); loop { if index == n.len() { - break; + break false; } let source: u32 = *n.at(index); if source == current_node { - is_visited = true; - break; + break true; } index += 1; - }; - is_visited + } } diff --git a/programs/compile_benches/extended_euclidean_algorithm.cairo b/programs/compile_benches/extended_euclidean_algorithm.cairo index b5d073f1a8..1cbe9f969b 100644 --- a/programs/compile_benches/extended_euclidean_algorithm.cairo +++ b/programs/compile_benches/extended_euclidean_algorithm.cairo @@ -2,7 +2,7 @@ // License: MIT //! # Extended Euclidean Algorithm. -use integer::{u128_overflowing_sub, u128_overflowing_mul}; +use core::num::traits::{OverflowingMul, OverflowingSub}; /// Extended Euclidean Algorithm. /// # Arguments @@ -12,7 +12,7 @@ use integer::{u128_overflowing_sub, u128_overflowing_mul}; /// * `gcd` - Greatest common divisor. /// * `x` - First Bezout coefficient. /// * `y` - Second Bezout coefficient. -fn extended_euclidean_algorithm(a: u128, b: u128) -> (u128, u128, u128) { +pub fn extended_euclidean_algorithm(a: u128, b: u128) -> (u128, u128, u128) { // Initialize variables. let mut old_r = a; let mut rem = b; @@ -21,30 +21,21 @@ fn extended_euclidean_algorithm(a: u128, b: u128) -> (u128, u128, u128) { let mut old_t = 0; let mut coeff_t = 1; - // Loop until remainder is 0. - loop { - if rem == 0 { - break (old_r, old_s, old_t); - } + while (rem != 0) { let quotient = old_r / rem; update_step(ref rem, ref old_r, quotient); update_step(ref coeff_s, ref old_s, quotient); update_step(ref coeff_t, ref old_t, quotient); - } + }; + (old_r, old_s, old_t) } /// Update the step of the extended Euclidean algorithm. fn update_step(ref a: u128, ref old_a: u128, quotient: u128) { let temp = a; - let (bottom, _) = u128_overflowing_mul(quotient, temp); - a = u128_wrapping_sub(old_a, bottom); + let (bottom, _) = quotient.overflowing_mul(temp); + let (a_tmp, _) = old_a.overflowing_sub(bottom); + a = a_tmp; old_a = temp; } - -fn u128_wrapping_sub(a: u128, b: u128) -> u128 implicits(RangeCheck) nopanic { - match u128_overflowing_sub(a, b) { - Result::Ok(x) => x, - Result::Err(x) => x, - } -} diff --git a/programs/compile_benches/fast_power.cairo b/programs/compile_benches/fast_power.cairo index dc5d3cd053..e6ca5ae049 100644 --- a/programs/compile_benches/fast_power.cairo +++ b/programs/compile_benches/fast_power.cairo @@ -2,43 +2,94 @@ // License: MIT //! # Fast power algorithm -use array::ArrayTrait; -use option::OptionTrait; -use traits::{Into, TryInto}; - -// Calculate the ( base ^ power ) mod modulus -// using the fast powering algorithm # Arguments -// * ` base ` - The base of the exponentiation -// * ` power ` - The power of the exponentiation -// * ` modulus ` - The modulus used in the calculation # Returns -// * ` u128 ` - The result of ( base ^ power ) mod modulus - -fn fast_power(base: u128, mut power: u128, modulus: u128) -> u128 { - // Return invalid input error - if base == 0 { - panic_with_felt252('II') - } +use core::ops::DivAssign; - if modulus == 1 { - return 0; - } +/// Calculate the base ^ power +/// using the fast powering algorithm +/// # Arguments +/// * ` base ` - The base of the exponentiation +/// * ` power ` - The power of the exponentiation +/// # Returns +/// * ` T ` - The result of base ^ power +/// # Panics +/// * ` base ` is 0 +pub fn fast_power< + T, + +Div, + +DivAssign, + +Rem, + +Into, + +Into, + +TryInto, + +PartialEq, + +Copy, + +Drop +>( + base: T, mut power: T +) -> T { + assert!(base != 0_u8.into(), "fast_power: invalid input"); let mut base: u256 = base.into(); - let modulus: u256 = modulus.into(); let mut result: u256 = 1; - let res = loop { - if power == 0 { - break result; + loop { + if power % 2_u8.into() != 0_u8.into() { + result *= base; + } + power /= 2_u8.into(); + if (power == 0_u8.into()) { + break; } + base *= base; + }; + + result.try_into().expect('too large to fit output type') +} + +/// Calculate the ( base ^ power ) mod modulus +/// using the fast powering algorithm +/// # Arguments +/// * ` base ` - The base of the exponentiation +/// * ` power ` - The power of the exponentiation +/// * ` modulus ` - The modulus used in the calculation +/// # Returns +/// * ` T ` - The result of ( base ^ power ) mod modulus +/// # Panics +/// * ` base ` is 0 +pub fn fast_power_mod< + T, + +Div, + +DivAssign, + +Rem, + +Into, + +Into, + +TryInto, + +PartialEq, + +Copy, + +Drop +>( + base: T, mut power: T, modulus: T +) -> T { + assert!(base != 0_u8.into(), "fast_power: invalid input"); - if power % 2 != 0 { + if modulus == 1_u8.into() { + return 0_u8.into(); + } + + let mut base: u256 = base.into(); + let modulus: u256 = modulus.into(); + let mut result: u256 = 1; + + loop { + if power % 2_u8.into() != 0_u8.into() { result = (result * base) % modulus; } - + power /= 2_u8.into(); + if (power == 0_u8.into()) { + break; + } base = (base * base) % modulus; - power = power / 2; }; - res.try_into().expect('value cant be larger than u128') + result.try_into().expect('too large to fit output type') } diff --git a/programs/compile_benches/sha256.cairo b/programs/compile_benches/sha256.cairo deleted file mode 100644 index 6b1b18ee07..0000000000 --- a/programs/compile_benches/sha256.cairo +++ /dev/null @@ -1,285 +0,0 @@ -// Copied from: https://github.com/keep-starknet-strange/alexandria -// License: MIT - -use core::traits::TryInto; -use array::{ArrayTrait, SpanTrait}; -use clone::Clone; -use integer::{u32_wrapping_add, BoundedInt}; -use option::OptionTrait; -use traits::Into; - -fn ch(x: u32, y: u32, z: u32) -> u32 { - (x & y) ^ ((x ^ BoundedInt::::max().into()) & z) -} - -fn maj(x: u32, y: u32, z: u32) -> u32 { - (x & y) ^ (x & z) ^ (y & z) -} - -fn bsig0(x: u32) -> u32 { - let x: u128 = x.into(); - let x1 = (x / 0x4) | (x * 0x40000000); - let x2 = (x / 0x2000) | (x * 0x80000); - let x3 = (x / 0x400000) | (x * 0x400); - let result = (x1 ^ x2 ^ x3) & BoundedInt::::max().into(); - result.try_into().unwrap() -} - -fn bsig1(x: u32) -> u32 { - let x: u128 = x.into(); - let x1 = (x / 0x40) | (x * 0x4000000); - let x2 = (x / 0x800) | (x * 0x200000); - let x3 = (x / 0x2000000) | (x * 0x80); - let result = (x1 ^ x2 ^ x3) & BoundedInt::::max().into(); - result.try_into().unwrap() -} - -fn ssig0(x: u32) -> u32 { - let x: u128 = x.into(); - let x1 = (x / 0x80) | (x * 0x2000000); - let x2 = (x / 0x40000) | (x * 0x4000); - let x3 = (x / 0x8); - let result = (x1 ^ x2 ^ x3) & BoundedInt::::max().into(); - result.try_into().unwrap() -} - -fn ssig1(x: u32) -> u32 { - let x: u128 = x.into(); - let x1 = (x / 0x20000) | (x * 0x8000); - let x2 = (x / 0x80000) | (x * 0x2000); - let x3 = (x / 0x400); - let result = (x1 ^ x2 ^ x3) & BoundedInt::::max().into(); - result.try_into().unwrap() -} - -fn sha256(mut data: Array) -> Array { - let data_len: u64 = (data.len() * 8).into(); - - // add one - data.append(0x80); - // add padding - loop { - if (64 * ((data.len() - 1) / 64 + 1)) - 8 == data.len() { - break; - } - data.append(0); - }; - - // add length to the end - let mut res = (data_len & 0xff00000000000000) / 0x100000000000000; - data.append(res.try_into().unwrap()); - res = (data_len.into() & 0xff000000000000) / 0x1000000000000; - data.append(res.try_into().unwrap()); - res = (data_len.into() & 0xff0000000000) / 0x10000000000; - data.append(res.try_into().unwrap()); - res = (data_len.into() & 0xff00000000) / 0x100000000; - data.append(res.try_into().unwrap()); - res = (data_len.into() & 0xff000000) / 0x1000000; - data.append(res.try_into().unwrap()); - res = (data_len.into() & 0xff0000) / 0x10000; - data.append(res.try_into().unwrap()); - res = (data_len.into() & 0xff00) / 0x100; - data.append(res.try_into().unwrap()); - res = data_len.into() & 0xff; - data.append(res.try_into().unwrap()); - - let data = from_u8Array_to_u32Array(data.span()); - let h = get_h(); - let k = get_k(); - let res = sha256_inner(data.span(), 0, k.span(), h.span()); - - from_u32Array_to_u8Array(res) -} - -fn from_u32Array_to_u8Array(mut data: Span) -> Array { - let mut result = array![]; - loop { - match data.pop_front() { - Option::Some(val) => { - let mut res = (*val & 0xff000000) / 0x1000000; - result.append(res.try_into().unwrap()); - res = (*val & 0xff0000) / 0x10000; - result.append(res.try_into().unwrap()); - res = (*val & 0xff00) / 0x100; - result.append(res.try_into().unwrap()); - res = *val & 0xff; - result.append(res.try_into().unwrap()); - }, - Option::None => { break; }, - }; - }; - result -} - -fn sha256_inner(mut data: Span, i: usize, k: Span, mut h: Span) -> Span { - if 16 * i >= data.len() { - return h; - } - let w = create_message_schedule(data, i); - let h2 = compression(w, 0, k, h); - - let mut t = array![]; - t.append(u32_wrapping_add(*h[0], *h2[0])); - t.append(u32_wrapping_add(*h[1], *h2[1])); - t.append(u32_wrapping_add(*h[2], *h2[2])); - t.append(u32_wrapping_add(*h[3], *h2[3])); - t.append(u32_wrapping_add(*h[4], *h2[4])); - t.append(u32_wrapping_add(*h[5], *h2[5])); - t.append(u32_wrapping_add(*h[6], *h2[6])); - t.append(u32_wrapping_add(*h[7], *h2[7])); - h = t.span(); - sha256_inner(data, i + 1, k, h) -} - -fn compression(w: Span, i: usize, k: Span, mut h: Span) -> Span { - if i >= 64 { - return h; - } - let s1 = bsig1(*h[4]); - let ch = ch(*h[4], *h[5], *h[6]); - let temp1 = u32_wrapping_add( - u32_wrapping_add(u32_wrapping_add(u32_wrapping_add(*h[7], s1), ch), *k[i]), *w[i] - ); - let s0 = bsig0(*h[0]); - let maj = maj(*h[0], *h[1], *h[2]); - let temp2 = u32_wrapping_add(s0, maj); - let mut t = array![]; - t.append(u32_wrapping_add(temp1, temp2)); - t.append(*h[0]); - t.append(*h[1]); - t.append(*h[2]); - t.append(u32_wrapping_add(*h[3], temp1)); - t.append(*h[4]); - t.append(*h[5]); - t.append(*h[6]); - h = t.span(); - compression(w, i + 1, k, h) -} - -fn create_message_schedule(data: Span, i: usize) -> Span { - let mut j = 0; - let mut result = array![]; - loop { - if j >= 16 { - break; - } - result.append(*data[i * 16 + j]); - j += 1; - }; - let mut i = 16; - loop { - if i >= 64 { - break; - } - let s0 = ssig0(*result[i - 15]); - let s1 = ssig1(*result[i - 2]); - let res = u32_wrapping_add( - u32_wrapping_add(u32_wrapping_add(*result[i - 16], s0), *result[i - 7]), s1 - ); - result.append(res); - i += 1; - }; - result.span() -} - -fn from_u8Array_to_u32Array(mut data: Span) -> Array { - let mut result = array![]; - loop { - match data.pop_front() { - Option::Some(val1) => { - let val2 = data.pop_front().unwrap(); - let val3 = data.pop_front().unwrap(); - let val4 = data.pop_front().unwrap(); - let mut value = (*val1).into() * 0x1000000; - value = value + (*val2).into() * 0x10000; - value = value + (*val3).into() * 0x100; - value = value + (*val4).into(); - result.append(value); - }, - Option::None => { break; }, - }; - }; - result -} - -fn get_h() -> Array { - array![ - 0x6a09e667, - 0xbb67ae85, - 0x3c6ef372, - 0xa54ff53a, - 0x510e527f, - 0x9b05688c, - 0x1f83d9ab, - 0x5be0cd19 - ] -} - -fn get_k() -> Array { - array![ - 0x428a2f98, - 0x71374491, - 0xb5c0fbcf, - 0xe9b5dba5, - 0x3956c25b, - 0x59f111f1, - 0x923f82a4, - 0xab1c5ed5, - 0xd807aa98, - 0x12835b01, - 0x243185be, - 0x550c7dc3, - 0x72be5d74, - 0x80deb1fe, - 0x9bdc06a7, - 0xc19bf174, - 0xe49b69c1, - 0xefbe4786, - 0x0fc19dc6, - 0x240ca1cc, - 0x2de92c6f, - 0x4a7484aa, - 0x5cb0a9dc, - 0x76f988da, - 0x983e5152, - 0xa831c66d, - 0xb00327c8, - 0xbf597fc7, - 0xc6e00bf3, - 0xd5a79147, - 0x06ca6351, - 0x14292967, - 0x27b70a85, - 0x2e1b2138, - 0x4d2c6dfc, - 0x53380d13, - 0x650a7354, - 0x766a0abb, - 0x81c2c92e, - 0x92722c85, - 0xa2bfe8a1, - 0xa81a664b, - 0xc24b8b70, - 0xc76c51a3, - 0xd192e819, - 0xd6990624, - 0xf40e3585, - 0x106aa070, - 0x19a4c116, - 0x1e376c08, - 0x2748774c, - 0x34b0bcb5, - 0x391c0cb3, - 0x4ed8aa4a, - 0x5b9cca4f, - 0x682e6ff3, - 0x748f82ee, - 0x78a5636f, - 0x84c87814, - 0x8cc70208, - 0x90befffa, - 0xa4506ceb, - 0xbef9a3f7, - 0xc67178f2 - ] -} diff --git a/programs/compile_benches/sha512.cairo b/programs/compile_benches/sha512.cairo new file mode 100644 index 0000000000..ac1723267f --- /dev/null +++ b/programs/compile_benches/sha512.cairo @@ -0,0 +1,539 @@ +// Copied from: https://github.com/keep-starknet-strange/alexandria +// License: MIT + +use core::num::traits::Bounded; +use core::num::traits::WrappingAdd; +use core::traits::{BitAnd, BitXor, BitOr}; + +// Variable naming is compliant to RFC-6234 (https://datatracker.ietf.org/doc/html/rfc6234) + +pub const SHA512_LEN: usize = 64; + +pub const U64_BIT_NUM: u64 = 64; + +// Powers of two to avoid recomputing +pub const TWO_POW_56: u64 = 0x100000000000000; +pub const TWO_POW_48: u64 = 0x1000000000000; +pub const TWO_POW_40: u64 = 0x10000000000; +pub const TWO_POW_32: u64 = 0x100000000; +pub const TWO_POW_24: u64 = 0x1000000; +pub const TWO_POW_16: u64 = 0x10000; +pub const TWO_POW_8: u64 = 0x100; +pub const TWO_POW_4: u64 = 0x10; +pub const TWO_POW_2: u64 = 0x4; +pub const TWO_POW_1: u64 = 0x2; +pub const TWO_POW_0: u64 = 0x1; + +const TWO_POW_7: u64 = 0x80; +const TWO_POW_14: u64 = 0x4000; +const TWO_POW_18: u64 = 0x40000; +const TWO_POW_19: u64 = 0x80000; +const TWO_POW_28: u64 = 0x10000000; +const TWO_POW_34: u64 = 0x400000000; +const TWO_POW_39: u64 = 0x8000000000; +const TWO_POW_41: u64 = 0x20000000000; +const TWO_POW_61: u64 = 0x2000000000000000; + +const TWO_POW_64_MINUS_1: u64 = 0x8000000000000000; +const TWO_POW_64_MINUS_6: u64 = 0x40; +const TWO_POW_64_MINUS_8: u64 = 0x100000000000000; +const TWO_POW_64_MINUS_14: u64 = 0x4000000000000; +const TWO_POW_64_MINUS_18: u64 = 0x400000000000; +const TWO_POW_64_MINUS_19: u64 = 0x200000000000; +const TWO_POW_64_MINUS_28: u64 = 0x1000000000; +const TWO_POW_64_MINUS_34: u64 = 0x40000000; +const TWO_POW_64_MINUS_39: u64 = 0x2000000; +const TWO_POW_64_MINUS_41: u64 = 0x800000; +const TWO_POW_64_MINUS_61: u64 = 0x8; + +// Max u8 and u64 for bitwise operations +pub const MAX_U8: u64 = 0xff; +pub const MAX_U64: u128 = 0xffffffffffffffff; + +#[derive(Drop, Copy)] +pub struct Word64 { + pub data: u64, +} + +impl WordBitAnd of BitAnd { + fn bitand(lhs: Word64, rhs: Word64) -> Word64 { + let data = BitAnd::bitand(lhs.data, rhs.data); + Word64 { data } + } +} + +impl WordBitXor of BitXor { + fn bitxor(lhs: Word64, rhs: Word64) -> Word64 { + let data = BitXor::bitxor(lhs.data, rhs.data); + Word64 { data } + } +} + +impl WordBitOr of BitOr { + fn bitor(lhs: Word64, rhs: Word64) -> Word64 { + let data = BitOr::bitor(lhs.data, rhs.data); + Word64 { data } + } +} + +impl WordBitNot of BitNot { + fn bitnot(a: Word64) -> Word64 { + Word64 { data: Bounded::MAX - a.data } + } +} + +impl WordAdd of Add { + fn add(lhs: Word64, rhs: Word64) -> Word64 { + Word64 { data: lhs.data.wrapping_add(rhs.data) } + } +} + +impl U128IntoWord of Into { + fn into(self: u128) -> Word64 { + Word64 { data: self.try_into().unwrap() } + } +} + +impl U64IntoWord of Into { + fn into(self: u64) -> Word64 { + Word64 { data: self } + } +} + +pub trait WordOperations { + fn shr(self: T, n: u64) -> T; + fn shl(self: T, n: u64) -> T; + fn rotr(self: T, n: u64) -> T; + fn rotr_precomputed(self: T, two_pow_n: u64, two_pow_64_n: u64) -> T; + fn rotl(self: T, n: u64) -> T; +} + +pub impl Word64WordOperations of WordOperations { + fn shr(self: Word64, n: u64) -> Word64 { + Word64 { data: math_shr_u64(self.data, n) } + } + fn shl(self: Word64, n: u64) -> Word64 { + Word64 { data: math_shl_u64(self.data, n) } + } + fn rotr(self: Word64, n: u64) -> Word64 { + let data = BitOr::bitor( + math_shr_u64(self.data, n), math_shl_u64(self.data, (U64_BIT_NUM - n)) + ); + Word64 { data } + } + // does the work of rotr but with precomputed values 2**n and 2**(64-n) + fn rotr_precomputed(self: Word64, two_pow_n: u64, two_pow_64_n: u64) -> Word64 { + let data = self.data.into(); + let data: u128 = BitOr::bitor( + math_shr_precomputed::(data, two_pow_n.into()), + math_shl_precomputed::(data, two_pow_64_n.into()) + ); + + let data: u64 = match data.try_into() { + Option::Some(data) => data, + Option::None => (data & MAX_U64).try_into().unwrap() + }; + + Word64 { data } + } + fn rotl(self: Word64, n: u64) -> Word64 { + let data = BitOr::bitor( + math_shl_u64(self.data, n), math_shr_u64(self.data, (U64_BIT_NUM - n)) + ); + Word64 { data } + } +} + + +fn ch(x: Word64, y: Word64, z: Word64) -> Word64 { + (x & y) ^ (~x & z) +} + +fn maj(x: Word64, y: Word64, z: Word64) -> Word64 { + (x & y) ^ (x & z) ^ (y & z) +} + +/// Performs x.rotr(28) ^ x.rotr(34) ^ x.rotr(39), +/// Using precomputed values to avoid recomputation +fn bsig0(x: Word64) -> Word64 { + // x.rotr(28) ^ x.rotr(34) ^ x.rotr(39) + x.rotr_precomputed(TWO_POW_28, TWO_POW_64_MINUS_28) + ^ x.rotr_precomputed(TWO_POW_34, TWO_POW_64_MINUS_34) + ^ x.rotr_precomputed(TWO_POW_39, TWO_POW_64_MINUS_39) +} + +/// Performs x.rotr(14) ^ x.rotr(18) ^ x.rotr(41), +/// Using precomputed values to avoid recomputation +fn bsig1(x: Word64) -> Word64 { + // x.rotr(14) ^ x.rotr(18) ^ x.rotr(41) + x.rotr_precomputed(TWO_POW_14, TWO_POW_64_MINUS_14) + ^ x.rotr_precomputed(TWO_POW_18, TWO_POW_64_MINUS_18) + ^ x.rotr_precomputed(TWO_POW_41, TWO_POW_64_MINUS_41) +} + +/// Performs x.rotr(1) ^ x.rotr(8) ^ x.shr(7), +/// Using precomputed values to avoid recomputation +fn ssig0(x: Word64) -> Word64 { + // x.rotr(1) ^ x.rotr(8) ^ x.shr(7) + x.rotr_precomputed(TWO_POW_1, TWO_POW_64_MINUS_1) + ^ x.rotr_precomputed(TWO_POW_8, TWO_POW_64_MINUS_8) + ^ math_shr_precomputed::(x.data.into(), TWO_POW_7).into() // 2 ** 7 +} + +/// Performs x.rotr(19) ^ x.rotr(61) ^ x.shr(6), +/// Using precomputed values to avoid recomputation +fn ssig1(x: Word64) -> Word64 { + // x.rotr(19) ^ x.rotr(61) ^ x.shr(6) + x.rotr_precomputed(TWO_POW_19, TWO_POW_64_MINUS_19) + ^ x.rotr_precomputed(TWO_POW_61, TWO_POW_64_MINUS_61) + ^ math_shr_precomputed::(x.data, TWO_POW_64_MINUS_6).into() // 2 ** 6 +} + +/// Calculates base ** power +pub fn fpow(mut base: u128, mut power: u128) -> u128 { + // Return invalid input error + assert!(base != 0, "fpow: invalid input"); + + let mut result = 1; + while (power != 0) { + let (q, r) = DivRem::div_rem(power, 2); + if r == 1 { + result = (result * base); + } + base = base * base; + power = q; + }; + + result +} + +const two_squarings: [ + u64 + ; 6] = [ + TWO_POW_1, TWO_POW_2, TWO_POW_4, TWO_POW_8, TWO_POW_16, TWO_POW_32 +]; +// Uses cache for faster powers of 2 in a u128 +// Uses TWO_POW_* constants +// Generic T to use with both u128 and u64 +pub fn two_pow, +Mul, +Into, +Drop>(mut power: u64) -> T { + let mut i = 0; + let mut result: T = 1_u64.into(); + let two_squarings = two_squarings.span(); + while (power != 0) { + let (q, r) = DivRem::div_rem(power, 2); + if r == 1 { + result = result * (*two_squarings[i]).into(); + } + i = i + 1; + power = q; + }; + + result +} + +// Shift left with math_shl_precomputed function +fn math_shl(x: u128, n: u64) -> u128 { + math_shl_precomputed(x, two_pow(n)) +} + +// Shift right with math_shr_precomputed function +fn math_shr(x: u128, n: u64) -> u128 { + math_shr_precomputed(x, two_pow(n)) +} + +// Shift left with precomputed powers of 2 +fn math_shl_precomputed, +Rem, +Drop, +Copy, +Into>( + x: T, two_power_n: T +) -> T { + x * two_power_n +} + +// Shift right with precomputed powers of 2 +fn math_shr_precomputed, +Rem, +Drop, +Copy, +Into>( + x: T, two_power_n: T +) -> T { + x / two_power_n +} + +// Shift left wrapper for u64 +fn math_shl_u64(x: u64, n: u64) -> u64 { + (math_shl(x.into(), n) % Bounded::::MAX.into()).try_into().unwrap() +} + +// Shift right wrapper for u64 +fn math_shr_u64(x: u64, n: u64) -> u64 { + (math_shr(x.into(), n) % Bounded::::MAX.into()).try_into().unwrap() +} + +fn add_trailing_zeroes(ref data: Array, msg_len: usize) { + let mdi = msg_len % 128; + let padding_len = if (mdi < 112) { + 119 - mdi + } else { + 247 - mdi + }; + + let mut i = 0; + while (i < padding_len) { + data.append(0); + i += 1; + }; +} + +fn from_u8Array_to_WordArray(data: Array) -> Array { + let mut new_arr: Array = array![]; + let mut i = 0; + + // Use precomputed powers of 2 for shift left to avoid recomputation + // Safe to use u64 coz we shift u8 to the left by max 56 bits in u64 + while (i < data.len()) { + let new_word: u64 = math_shl_precomputed::((*data[i + 0]).into(), TWO_POW_56) + + math_shl_precomputed((*data[i + 1]).into(), TWO_POW_48) + + math_shl_precomputed((*data[i + 2]).into(), TWO_POW_40) + + math_shl_precomputed((*data[i + 3]).into(), TWO_POW_32) + + math_shl_precomputed((*data[i + 4]).into(), TWO_POW_24) + + math_shl_precomputed((*data[i + 5]).into(), TWO_POW_16) + + math_shl_precomputed((*data[i + 6]).into(), TWO_POW_8) + + math_shl_precomputed((*data[i + 7]).into(), TWO_POW_0); + new_arr.append(Word64 { data: new_word }); + i += 8; + }; + new_arr +} + +fn from_WordArray_to_u8array(data: Span) -> Array { + let mut arr: Array = array![]; + + let mut i = 0; + // Use precomputed powers of 2 for shift right to avoid recomputation + while (i != data.len()) { + let mut res = math_shr_precomputed((*data.at(i).data).into(), TWO_POW_56) & MAX_U8; + arr.append(res.try_into().unwrap()); + res = math_shr_precomputed((*data.at(i).data).into(), TWO_POW_48) & MAX_U8; + arr.append(res.try_into().unwrap()); + res = math_shr_precomputed((*data.at(i).data).into(), TWO_POW_40) & MAX_U8; + arr.append(res.try_into().unwrap()); + res = math_shr_precomputed((*data.at(i).data).into(), TWO_POW_32) & MAX_U8; + arr.append(res.try_into().unwrap()); + res = math_shr_precomputed((*data.at(i).data).into(), TWO_POW_24) & MAX_U8; + arr.append(res.try_into().unwrap()); + res = math_shr_precomputed((*data.at(i).data).into(), TWO_POW_16) & MAX_U8; + arr.append(res.try_into().unwrap()); + res = math_shr_precomputed((*data.at(i).data).into(), TWO_POW_8) & MAX_U8; + arr.append(res.try_into().unwrap()); + res = math_shr_precomputed((*data.at(i).data).into(), TWO_POW_0) & MAX_U8; + arr.append(res.try_into().unwrap()); + i += 1; + }; + arr +} + +fn digest_hash(data: Span, msg_len: usize) -> Array { + let k = get_k().span(); + let h = get_h().span(); + + let block_nb = msg_len / 128; + + let mut h_0: Word64 = *h[0]; + let mut h_1: Word64 = *h[1]; + let mut h_2: Word64 = *h[2]; + let mut h_3: Word64 = *h[3]; + let mut h_4: Word64 = *h[4]; + let mut h_5: Word64 = *h[5]; + let mut h_6: Word64 = *h[6]; + let mut h_7: Word64 = *h[7]; + + let mut i = 0; + + while (i != block_nb) { + // Prepare message schedule + let mut t: usize = 0; + + let mut W: Array = array![]; + while (t != 80) { + if t < 16 { + W.append(*data.at(i * 16 + t)); + } else { + let buf = ssig1(*W.at(t - 2)) + *W.at(t - 7) + ssig0(*W.at(t - 15)) + *W.at(t - 16); + W.append(buf); + } + t += 1; + }; + + let mut a = h_0; + let mut b = h_1; + let mut c = h_2; + let mut d = h_3; + let mut e = h_4; + let mut f = h_5; + let mut g = h_6; + let mut h = h_7; + + let mut t: usize = 0; + while (t != 80) { + let T1 = h + bsig1(e) + ch(e, f, g) + *k.at(t) + *W.at(t); + let T2 = bsig0(a) + maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + t += 1; + }; + + h_0 = a + h_0; + h_1 = b + h_1; + h_2 = c + h_2; + h_3 = d + h_3; + h_4 = e + h_4; + h_5 = f + h_5; + h_6 = g + h_6; + h_7 = h + h_7; + + i += 1; + }; + + array![h_0, h_1, h_2, h_3, h_4, h_5, h_6, h_7] +} + +pub fn sha512(mut data: Array) -> Array { + let bit_numbers: u128 = data.len().into() * 8; + // any u32 * 8 fits in u64 + // let bit_numbers = bit_numbers & Bounded::::MAX.into(); + + let max_u8: u128 = MAX_U8.into(); + let mut msg_len = data.len(); + + // Appends 1 + data.append(0x80); + + add_trailing_zeroes(ref data, msg_len); + + // add length to the end + // Use precomputed powers of 2 for shift right to avoid recomputation + let mut res: u128 = math_shr_precomputed(bit_numbers, TWO_POW_56.into()) & max_u8; + data.append(res.try_into().unwrap()); + res = math_shr_precomputed(bit_numbers, TWO_POW_48.into()) & max_u8; + data.append(res.try_into().unwrap()); + res = math_shr_precomputed(bit_numbers, TWO_POW_40.into()) & max_u8; + data.append(res.try_into().unwrap()); + res = math_shr_precomputed(bit_numbers, TWO_POW_32.into()) & max_u8; + data.append(res.try_into().unwrap()); + res = math_shr_precomputed(bit_numbers, TWO_POW_24.into()) & max_u8; + data.append(res.try_into().unwrap()); + res = math_shr_precomputed(bit_numbers, TWO_POW_16.into()) & max_u8; + data.append(res.try_into().unwrap()); + res = math_shr_precomputed(bit_numbers, TWO_POW_8.into()) & max_u8; + data.append(res.try_into().unwrap()); + res = math_shr_precomputed(bit_numbers, TWO_POW_0.into()) & max_u8; + data.append(res.try_into().unwrap()); + + msg_len = data.len(); + + let mut data = from_u8Array_to_WordArray(data); + + let hash = digest_hash(data.span(), msg_len); + from_WordArray_to_u8array(hash.span()) +} + +fn get_h() -> Array { + let mut h: Array = array![]; + h.append(Word64 { data: 0x6a09e667f3bcc908 }); + h.append(Word64 { data: 0xbb67ae8584caa73b }); + h.append(Word64 { data: 0x3c6ef372fe94f82b }); + h.append(Word64 { data: 0xa54ff53a5f1d36f1 }); + h.append(Word64 { data: 0x510e527fade682d1 }); + h.append(Word64 { data: 0x9b05688c2b3e6c1f }); + h.append(Word64 { data: 0x1f83d9abfb41bd6b }); + h.append(Word64 { data: 0x5be0cd19137e2179 }); + h +} + +fn get_k() -> Array { + let mut k: Array = array![]; + k.append(Word64 { data: 0x428a2f98d728ae22 }); + k.append(Word64 { data: 0x7137449123ef65cd }); + k.append(Word64 { data: 0xb5c0fbcfec4d3b2f }); + k.append(Word64 { data: 0xe9b5dba58189dbbc }); + k.append(Word64 { data: 0x3956c25bf348b538 }); + k.append(Word64 { data: 0x59f111f1b605d019 }); + k.append(Word64 { data: 0x923f82a4af194f9b }); + k.append(Word64 { data: 0xab1c5ed5da6d8118 }); + k.append(Word64 { data: 0xd807aa98a3030242 }); + k.append(Word64 { data: 0x12835b0145706fbe }); + k.append(Word64 { data: 0x243185be4ee4b28c }); + k.append(Word64 { data: 0x550c7dc3d5ffb4e2 }); + k.append(Word64 { data: 0x72be5d74f27b896f }); + k.append(Word64 { data: 0x80deb1fe3b1696b1 }); + k.append(Word64 { data: 0x9bdc06a725c71235 }); + k.append(Word64 { data: 0xc19bf174cf692694 }); + k.append(Word64 { data: 0xe49b69c19ef14ad2 }); + k.append(Word64 { data: 0xefbe4786384f25e3 }); + k.append(Word64 { data: 0x0fc19dc68b8cd5b5 }); + k.append(Word64 { data: 0x240ca1cc77ac9c65 }); + k.append(Word64 { data: 0x2de92c6f592b0275 }); + k.append(Word64 { data: 0x4a7484aa6ea6e483 }); + k.append(Word64 { data: 0x5cb0a9dcbd41fbd4 }); + k.append(Word64 { data: 0x76f988da831153b5 }); + k.append(Word64 { data: 0x983e5152ee66dfab }); + k.append(Word64 { data: 0xa831c66d2db43210 }); + k.append(Word64 { data: 0xb00327c898fb213f }); + k.append(Word64 { data: 0xbf597fc7beef0ee4 }); + k.append(Word64 { data: 0xc6e00bf33da88fc2 }); + k.append(Word64 { data: 0xd5a79147930aa725 }); + k.append(Word64 { data: 0x06ca6351e003826f }); + k.append(Word64 { data: 0x142929670a0e6e70 }); + k.append(Word64 { data: 0x27b70a8546d22ffc }); + k.append(Word64 { data: 0x2e1b21385c26c926 }); + k.append(Word64 { data: 0x4d2c6dfc5ac42aed }); + k.append(Word64 { data: 0x53380d139d95b3df }); + k.append(Word64 { data: 0x650a73548baf63de }); + k.append(Word64 { data: 0x766a0abb3c77b2a8 }); + k.append(Word64 { data: 0x81c2c92e47edaee6 }); + k.append(Word64 { data: 0x92722c851482353b }); + k.append(Word64 { data: 0xa2bfe8a14cf10364 }); + k.append(Word64 { data: 0xa81a664bbc423001 }); + k.append(Word64 { data: 0xc24b8b70d0f89791 }); + k.append(Word64 { data: 0xc76c51a30654be30 }); + k.append(Word64 { data: 0xd192e819d6ef5218 }); + k.append(Word64 { data: 0xd69906245565a910 }); + k.append(Word64 { data: 0xf40e35855771202a }); + k.append(Word64 { data: 0x106aa07032bbd1b8 }); + k.append(Word64 { data: 0x19a4c116b8d2d0c8 }); + k.append(Word64 { data: 0x1e376c085141ab53 }); + k.append(Word64 { data: 0x2748774cdf8eeb99 }); + k.append(Word64 { data: 0x34b0bcb5e19b48a8 }); + k.append(Word64 { data: 0x391c0cb3c5c95a63 }); + k.append(Word64 { data: 0x4ed8aa4ae3418acb }); + k.append(Word64 { data: 0x5b9cca4f7763e373 }); + k.append(Word64 { data: 0x682e6ff3d6b2b8a3 }); + k.append(Word64 { data: 0x748f82ee5defb2fc }); + k.append(Word64 { data: 0x78a5636f43172f60 }); + k.append(Word64 { data: 0x84c87814a1f0ab72 }); + k.append(Word64 { data: 0x8cc702081a6439ec }); + k.append(Word64 { data: 0x90befffa23631e28 }); + k.append(Word64 { data: 0xa4506cebde82bde9 }); + k.append(Word64 { data: 0xbef9a3f7b2c67915 }); + k.append(Word64 { data: 0xc67178f2e372532b }); + k.append(Word64 { data: 0xca273eceea26619c }); + k.append(Word64 { data: 0xd186b8c721c0c207 }); + k.append(Word64 { data: 0xeada7dd6cde0eb1e }); + k.append(Word64 { data: 0xf57d4f7fee6ed178 }); + k.append(Word64 { data: 0x06f067aa72176fba }); + k.append(Word64 { data: 0x0a637dc5a2c898a6 }); + k.append(Word64 { data: 0x113f9804bef90dae }); + k.append(Word64 { data: 0x1b710b35131c471b }); + k.append(Word64 { data: 0x28db77f523047d84 }); + k.append(Word64 { data: 0x32caab7b40c72493 }); + k.append(Word64 { data: 0x3c9ebe0a15c9bebc }); + k.append(Word64 { data: 0x431d67c49c100d4c }); + k.append(Word64 { data: 0x4cc5d4becb3e42b6 }); + k.append(Word64 { data: 0x597f299cfc657e2a }); + k.append(Word64 { data: 0x5fcb6fab3ad6faec }); + k.append(Word64 { data: 0x6c44198c4a475817 }); + k +}