use crate::errors::AssertionError;
use core::{
cmp::{Ord, Ordering, PartialOrd},
fmt::{Display, Formatter},
};
use math::FieldElement;
use utils::collections::Vec;
#[cfg(test)]
mod tests;
const MIN_STRIDE_LENGTH: usize = 2;
const NO_STRIDE: usize = 0;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Assertion<E: FieldElement> {
pub(super) column: usize,
pub(super) first_step: usize,
pub(super) stride: usize,
pub(super) values: Vec<E>,
}
impl<E: FieldElement> Assertion<E> {
pub fn single(column: usize, step: usize, value: E) -> Self {
Assertion {
column,
first_step: step,
stride: NO_STRIDE,
values: vec![value],
}
}
pub fn periodic(column: usize, first_step: usize, stride: usize, value: E) -> Self {
validate_stride(stride, first_step, column);
Assertion {
column,
first_step,
stride,
values: vec![value],
}
}
pub fn sequence(column: usize, first_step: usize, stride: usize, values: Vec<E>) -> Self {
validate_stride(stride, first_step, column);
assert!(
!values.is_empty(),
"invalid assertion for column {}: number of asserted values must be greater than zero",
column
);
assert!(
values.len().is_power_of_two(),
"invalid assertion for column {}: number of asserted values must be a power of two, but was {}",
column,
values.len()
);
Assertion {
column,
first_step,
stride: if values.len() == 1 { NO_STRIDE } else { stride },
values,
}
}
pub fn column(&self) -> usize {
self.column
}
pub fn first_step(&self) -> usize {
self.first_step
}
pub fn stride(&self) -> usize {
self.stride
}
pub fn values(&self) -> &[E] {
&self.values
}
pub fn is_single(&self) -> bool {
self.stride == NO_STRIDE
}
pub fn is_periodic(&self) -> bool {
self.stride != NO_STRIDE && self.values.len() == 1
}
pub fn is_sequence(&self) -> bool {
self.values.len() > 1
}
pub fn overlaps_with(&self, other: &Assertion<E>) -> bool {
if self.column != other.column {
return false;
}
if self.first_step == other.first_step {
return true;
}
if self.stride == other.stride {
return false;
}
if self.first_step < other.first_step {
if self.is_single() {
return false;
}
if other.is_single() || self.stride < other.stride {
(other.first_step - self.first_step) % self.stride == 0
} else {
false
}
} else {
if other.is_single() {
return false;
}
if self.is_single() || other.stride < self.stride {
(self.first_step - other.first_step) % other.stride == 0
} else {
false
}
}
}
pub fn validate_trace_width(&self, trace_width: usize) -> Result<(), AssertionError> {
if self.column >= trace_width {
return Err(AssertionError::TraceWidthTooShort(self.column, trace_width));
}
Ok(())
}
pub fn validate_trace_length(&self, trace_length: usize) -> Result<(), AssertionError> {
if !trace_length.is_power_of_two() {
return Err(AssertionError::TraceLengthNotPowerOfTwo(trace_length));
}
if self.is_single() {
if self.first_step >= trace_length {
return Err(AssertionError::TraceLengthTooShort(
(self.first_step + 1).next_power_of_two(),
trace_length,
));
}
} else if self.is_periodic() {
if self.stride > trace_length {
return Err(AssertionError::TraceLengthTooShort(
self.stride,
trace_length,
));
}
} else {
let expected_length = self.values.len() * self.stride;
if expected_length != trace_length {
return Err(AssertionError::TraceLengthNotExact(
expected_length,
trace_length,
));
}
}
Ok(())
}
pub fn apply<F>(&self, trace_length: usize, mut f: F)
where
F: FnMut(usize, E),
{
self.validate_trace_length(trace_length)
.unwrap_or_else(|err| {
panic!("invalid trace length: {}", err);
});
if self.is_single() {
f(self.first_step, self.values[0]);
} else if self.is_periodic() {
for i in 0..(trace_length / self.stride) {
f(self.first_step + self.stride * i, self.values[0]);
}
} else {
for (i, &value) in self.values.iter().enumerate() {
f(self.first_step + self.stride * i, value);
}
}
}
pub fn get_num_steps(&self, trace_length: usize) -> usize {
self.validate_trace_length(trace_length)
.unwrap_or_else(|err| {
panic!("invalid trace length: {}", err);
});
if self.is_single() {
1
} else if self.is_periodic() {
trace_length / self.stride
} else {
self.values.len()
}
}
}
impl<E: FieldElement> Ord for Assertion<E> {
fn cmp(&self, other: &Self) -> Ordering {
if self.stride == other.stride {
if self.first_step == other.first_step {
self.column.partial_cmp(&other.column).unwrap()
} else {
self.first_step.partial_cmp(&other.first_step).unwrap()
}
} else {
self.stride.partial_cmp(&other.stride).unwrap()
}
}
}
impl<E: FieldElement> PartialOrd for Assertion<E> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<E: FieldElement> Display for Assertion<E> {
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
write!(f, "(column={}, ", self.column)?;
match self.stride {
0 => write!(f, "step={}, ", self.first_step)?,
_ => {
let second_step = self.first_step + self.stride;
write!(f, "steps=[{}, {}, ...], ", self.first_step, second_step)?;
}
}
match self.values.len() {
1 => write!(f, "value={})", self.values[0]),
2 => write!(f, "values=[{}, {}])", self.values[0], self.values[1]),
_ => write!(f, "values=[{}, {}, ...])", self.values[0], self.values[1]),
}
}
}
fn validate_stride(stride: usize, first_step: usize, column: usize) {
assert!(
stride.is_power_of_two(),
"invalid assertion for column {}: stride must be a power of two, but was {}",
column,
stride
);
assert!(
stride >= MIN_STRIDE_LENGTH,
"invalid assertion for column {}: stride must be at least {}, but was {}",
column,
MIN_STRIDE_LENGTH,
stride
);
assert!(
first_step < stride,
"invalid assertion for column {}: first step must be smaller than stride ({} steps), but was {}",
column,
stride,
first_step
);
}