Skip to content

Commit

Permalink
signoff: u32::next_power_of_two
Browse files Browse the repository at this point in the history
  • Loading branch information
jan-ferdinand committed Dec 9, 2024
1 parent f19787d commit f40dfc7
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@
{
"name": "tasmlib_arithmetic_u32_next_power_of_two",
"benchmark_result": {
"clock_cycle_count": 24,
"clock_cycle_count": 22,
"hash_table_height": 30,
"u32_table_height": 62,
"op_stack_table_height": 18,
"op_stack_table_height": 14,
"ram_table_height": 0
},
"case": "CommonCase"
},
{
"name": "tasmlib_arithmetic_u32_next_power_of_two",
"benchmark_result": {
"clock_cycle_count": 24,
"clock_cycle_count": 22,
"hash_table_height": 30,
"u32_table_height": 70,
"op_stack_table_height": 18,
"op_stack_table_height": 14,
"ram_table_height": 0
},
"case": "WorstCase"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
{
"name": "tasmlib_verifier_stark_verify_dynamic_inner_padded_height_256_fri_exp_4",
"benchmark_result": {
"clock_cycle_count": 188621,
"clock_cycle_count": 188619,
"hash_table_height": 139021,
"u32_table_height": 25078,
"op_stack_table_height": 167616,
"op_stack_table_height": 167612,
"ram_table_height": 289172
},
"case": "CommonCase"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
{
"name": "tasmlib_verifier_stark_verify_dynamic_inner_padded_height_512_fri_exp_4",
"benchmark_result": {
"clock_cycle_count": 196148,
"clock_cycle_count": 196146,
"hash_table_height": 146713,
"u32_table_height": 33679,
"op_stack_table_height": 172342,
"op_stack_table_height": 172338,
"ram_table_height": 290324
},
"case": "CommonCase"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
{
"name": "tasmlib_verifier_stark_verify_static_inner_padded_height_256_fri_exp_4",
"benchmark_result": {
"clock_cycle_count": 176918,
"hash_table_height": 124981,
"clock_cycle_count": 176916,
"hash_table_height": 124975,
"u32_table_height": 25078,
"op_stack_table_height": 159810,
"op_stack_table_height": 159806,
"ram_table_height": 285273
},
"case": "CommonCase"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
{
"name": "tasmlib_verifier_stark_verify_static_inner_padded_height_512_fri_exp_4",
"benchmark_result": {
"clock_cycle_count": 184445,
"hash_table_height": 132673,
"clock_cycle_count": 184443,
"hash_table_height": 132667,
"u32_table_height": 33679,
"op_stack_table_height": 164536,
"op_stack_table_height": 164532,
"ram_table_height": 286425
},
"case": "CommonCase"
Expand Down
177 changes: 97 additions & 80 deletions tasm-lib/src/arithmetic/u32/next_power_of_two.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,41 @@
use std::collections::HashMap;

use triton_vm::prelude::*;

use crate::data_type::DataType;
use crate::library::Library;
use crate::traits::basic_snippet::BasicSnippet;

/// Returns the smallest power of two greater than or equal to self.
/// Behaves like the `rustc` method for all inputs of type `u32`.
use crate::traits::basic_snippet::Reviewer;
use crate::traits::basic_snippet::SignOffFingerprint;

/// Returns the smallest power of two greater than or equal to argument `arg`.
/// Behaves like the [`rustc` method in debug mode][rustc_pow] for all inputs
/// of type `u32`.
///
/// ### Behavior
///
/// ```text
/// BEFORE: _ arg
/// AFTER: _ u32::next_power_of_two(arg)
/// ```
///
/// ### Pre-conditions
///
/// - `arg` is a valid u32
/// - `arg` is smaller than or equal to 2^31
///
/// ### Post-conditions
///
/// - the output is the smallest power of two greater than or equal to `arg`
///
/// [rustc_pow]: u32::next_power_of_two
#[derive(Debug, Clone, Copy)]
pub struct NextPowerOfTwo;

impl NextPowerOfTwo {
pub const INPUT_TOO_LARGE_ERROR_ID: i128 = 130;
}

impl BasicSnippet for NextPowerOfTwo {
fn inputs(&self) -> Vec<(DataType, String)> {
vec![(DataType::U32, "self".to_owned())]
Expand All @@ -22,76 +49,80 @@ impl BasicSnippet for NextPowerOfTwo {
"tasmlib_arithmetic_u32_next_power_of_two".to_string()
}

fn code(&self, _library: &mut Library) -> Vec<LabelledInstruction> {
fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
let entrypoint = self.entrypoint();
let zero_or_one_label = format!("{entrypoint}_zero_or_one_label");
let greater_than_one_label = format!("{entrypoint}_greater_than_one");
triton_asm!(
{entrypoint}:
// _ self_
// _ arg

push 1
push 2
dup 2
lt
// _ arg 1 (arg < 2)

skiz
call {zero_or_one_label}
// if arg < 2: _ 1 0
// if arg >= 2: _ arg 1

skiz
call {greater_than_one_label}
// _

return

{zero_or_one_label}:
// _ self_ 1

pop 2
// _

push 1
// _ result

push 0
// _ result 0
{zero_or_one_label}:
// _ arg 1

return

{greater_than_one_label}:
// _ self_ 1
pop 2
push 1
push 0
// _ 1 0

push -1
add
// _ (value - 1)
return

log_2_floor
push 1
add
// _ (log_2_floor(value - 1) + 1)
{greater_than_one_label}:
// _ arg

push 2
// _ (log_2_floor(value - 1) + 1) 2
addi -1
log_2_floor
addi 1
// _ log₂(⌊value - 1⌋ + 1)

pow
// _ npo2(self_)
push 2
pow
// _ 2^log₂(⌊value - 1⌋ + 1)

// Assert result *not* 1^{32}
dup 0
push {1u64 << 32}
eq
push 0
eq
assert error_id 130
/* Assert result *not* 2^{32} */
dup 0
push {1u64 << 32}
eq
push 0
eq
assert error_id {Self::INPUT_TOO_LARGE_ERROR_ID}

return
return
)
}

fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
let mut sign_offs = HashMap::new();
sign_offs.insert(Reviewer("ferdinand"), 0x131c49afe5bf05af.into());
sign_offs
}
}

#[cfg(test)]
mod tests {
use itertools::Itertools;
use rand::prelude::*;

use super::*;
use crate::pop_encodable;
use crate::push_encodable;
use crate::snippet_bencher::BenchmarkCase;
use crate::test_helpers::test_assertion_failure;
use crate::traits::closure::Closure;
use crate::traits::closure::ShadowedClosure;
Expand All @@ -100,49 +131,38 @@ mod tests {

impl Closure for NextPowerOfTwo {
fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
let self_: u32 = stack.pop().unwrap().try_into().unwrap();
let npo2: u32 = self_.next_power_of_two();
stack.push(BFieldElement::new(npo2 as u64));
let arg = pop_encodable::<u32>(stack);
push_encodable(stack, &arg.next_power_of_two());
}

fn pseudorandom_initial_state(
&self,
seed: [u8; 32],
bench_case: Option<crate::snippet_bencher::BenchmarkCase>,
bench_case: Option<BenchmarkCase>,
) -> Vec<BFieldElement> {
let self_: u32 = match bench_case {
Some(crate::snippet_bencher::BenchmarkCase::CommonCase) => (1 << 27) - 1,
Some(crate::snippet_bencher::BenchmarkCase::WorstCase) => (1 << 31) - 1,
None => {
let mut rng = StdRng::from_seed(seed);
rng.next_u32() / 2
}
let arg = match bench_case {
Some(BenchmarkCase::CommonCase) => (1 << 27) - 1,
Some(BenchmarkCase::WorstCase) => (1 << 31) - 1,
None => StdRng::from_seed(seed).next_u32() / 2,
};

self.prepare_vm_stack(self_)
self.prepare_vm_stack(arg)
}

fn corner_case_initial_states(&self) -> Vec<Vec<BFieldElement>> {
let small_inputs = (0u32..=66).map(|i| self.prepare_vm_stack(i)).collect_vec();
let big_but_valid_inputs = [
1u32 << 31,
(1u32 << 31) - 1,
(1u32 << 31) - 2,
(1u32 << 31) - 3,
(1u32 << 31) - 4,
(1u32 << 31) - 5,
]
.into_iter()
.map(|i| self.prepare_vm_stack(i))
.collect_vec();

[small_inputs, big_but_valid_inputs].concat()
let small_inputs = 0..=66;
let big_valid_inputs = (0..=5).map(|i| (1 << 31) - i);

small_inputs
.chain(big_valid_inputs)
.map(|i| self.prepare_vm_stack(i))
.collect()
}
}

impl NextPowerOfTwo {
fn prepare_vm_stack(&self, self_: u32) -> Vec<BFieldElement> {
[self.init_stack_for_isolated_run(), bfe_vec![self_]].concat()
fn prepare_vm_stack(&self, arg: u32) -> Vec<BFieldElement> {
[self.init_stack_for_isolated_run(), bfe_vec![arg]].concat()
}
}

Expand All @@ -153,18 +173,15 @@ mod tests {

#[test]
fn npo2_overflow_negative_test() {
for self_ in [
(1u32 << 31) + 1,
(1u32 << 31) + 2,
(1u32 << 31) + 10,
u32::MAX - 2,
u32::MAX - 1,
u32::MAX,
] {
let init_stack = NextPowerOfTwo.prepare_vm_stack(self_);
let init_state = InitVmState::with_stack(init_stack);

test_assertion_failure(&ShadowedClosure::new(NextPowerOfTwo), init_state, &[130]);
let greater_two_pow_31 = (1..=5).map(|i| (1 << 31) + i);
let smaller_equal_u32_max = (0..=2).map(|i| u32::MAX - i);

for arg in greater_two_pow_31.chain(smaller_equal_u32_max) {
test_assertion_failure(
&ShadowedClosure::new(NextPowerOfTwo),
InitVmState::with_stack(NextPowerOfTwo.prepare_vm_stack(arg)),
&[NextPowerOfTwo::INPUT_TOO_LARGE_ERROR_ID],
);
}
}
}
Expand Down

0 comments on commit f40dfc7

Please sign in to comment.