use alloc::vec::Vec;
use math::{FieldElement, StarkField};
use crate::{errors::RandomCoinError, Digest, ElementHasher, RandomCoin};
pub struct DefaultRandomCoin<H: ElementHasher> {
seed: H::Digest,
counter: u64,
}
impl<H: ElementHasher> DefaultRandomCoin<H> {
fn next(&mut self) -> H::Digest {
self.counter += 1;
H::merge_with_int(self.seed, self.counter)
}
}
impl<B: StarkField, H: ElementHasher<BaseField = B>> RandomCoin for DefaultRandomCoin<H> {
type BaseField = B;
type Hasher = H;
fn new(seed: &[Self::BaseField]) -> Self {
let seed = H::hash_elements(seed);
Self { seed, counter: 0 }
}
fn reseed(&mut self, data: H::Digest) {
self.seed = H::merge(&[self.seed, data]);
self.counter = 0;
}
fn check_leading_zeros(&self, value: u64) -> u32 {
let new_seed = H::merge_with_int(self.seed, value);
let bytes = new_seed.as_bytes();
let seed_head = u64::from_le_bytes(bytes[..8].try_into().unwrap());
seed_head.trailing_zeros()
}
fn draw<E: FieldElement>(&mut self) -> Result<E, RandomCoinError> {
for _ in 0..1000 {
let value = self.next();
let bytes = &value.as_bytes()[..E::ELEMENT_BYTES];
if let Some(element) = E::from_random_bytes(bytes) {
return Ok(element);
}
}
Err(RandomCoinError::FailedToDrawFieldElement(1000))
}
fn draw_integers(
&mut self,
num_values: usize,
domain_size: usize,
nonce: u64,
) -> Result<Vec<usize>, RandomCoinError> {
assert!(domain_size.is_power_of_two(), "domain size must be a power of two");
assert!(num_values < domain_size, "number of values must be smaller than domain size");
self.seed = H::merge_with_int(self.seed, nonce);
self.counter = 0;
let v_mask = (domain_size - 1) as u64;
let mut values = Vec::new();
for _ in 0..1000 {
let bytes: [u8; 8] = self.next().as_bytes()[..8].try_into().unwrap();
let value = (u64::from_le_bytes(bytes) & v_mask) as usize;
values.push(value);
if values.len() == num_values {
break;
}
}
if values.len() < num_values {
return Err(RandomCoinError::FailedToDrawIntegers(num_values, values.len(), 1000));
}
Ok(values)
}
}