use rustev_contract::canonical::{CanonicalError, canonical_bytes, tagged_digest};
use serde::Serialize;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PermuteError {
Canonical(CanonicalError),
DigestEncoding,
Overflow,
}
#[derive(Debug, Clone, Copy, Serialize)]
#[serde(untagged)]
pub enum DrawKey<'a> {
Permutation(u64),
Comparison(&'a str),
}
pub fn draw(
seed: &str,
step: &str,
instance: &[String],
j: DrawKey<'_>,
i: u64,
) -> Result<(u64, u64), PermuteError> {
let range = u128::from(i) + 1;
let space = 1u128 << 64;
let limit = (space / range) * range;
let mut attempt = 0u64;
loop {
let bytes = canonical_bytes(&(seed, step, instance, j, i, attempt))
.map_err(PermuteError::Canonical)?;
let digest = tagged_digest("rustev.permute/1", &bytes);
let prefix = digest.get(7..23).ok_or(PermuteError::DigestEncoding)?;
let v = u64::from_str_radix(prefix, 16).map_err(|_| PermuteError::DigestEncoding)?;
if u128::from(v) < limit {
return Ok(((u128::from(v) % range) as u64, attempt));
}
attempt = attempt.checked_add(1).ok_or(PermuteError::Overflow)?;
}
}
pub fn permutation(
size: usize,
seed: &str,
step: &str,
instance: &[String],
j: u64,
) -> Result<Vec<usize>, PermuteError> {
let mut positions: Vec<_> = (0..size).collect();
for i in (1..size).rev() {
let index = u64::try_from(i).map_err(|_| PermuteError::Overflow)?;
let (r, _) = draw(seed, step, instance, DrawKey::Permutation(j), index)?;
let r = usize::try_from(r).map_err(|_| PermuteError::Overflow)?;
positions.swap(i, r);
}
Ok(positions)
}
pub fn presentation_bit(
seed: &str,
step: &str,
instance: &[String],
comparison: &str,
) -> Result<bool, PermuteError> {
draw(seed, step, instance, DrawKey::Comparison(comparison), 1).map(|(v, _)| v == 1)
}