use std::sync::Arc;
use crate::native::bignum::{BigInt, BigUint, Zero};
use crate::native::floats::sign_aware_lte;
use crate::native::intervalsets::IntervalSet;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct IntegerChoice {
pub min_value: BigInt,
pub max_value: BigInt,
pub shrink_towards: BigInt,
}
impl IntegerChoice {
pub(crate) fn clamped_shrink_towards(&self) -> BigInt {
self.shrink_towards
.clone()
.clamp(self.min_value.clone(), self.max_value.clone())
}
pub fn simplest(&self) -> BigInt {
self.clamped_shrink_towards()
}
pub fn unit(&self) -> BigInt {
let s = self.simplest();
let succ = &s + BigInt::from(1);
if self.validate(&succ) {
return succ;
}
let pred = &s - BigInt::from(1);
if self.validate(&pred) {
return pred;
}
s
}
pub fn validate(&self, value: &BigInt) -> bool {
self.min_value <= *value && *value <= self.max_value
}
pub fn sort_key(&self, value: &BigInt) -> (BigUint, bool) {
let target = self.clamped_shrink_towards();
let distance = (value - &target).magnitude();
(distance, *value < target)
}
pub fn max_index(&self) -> BigUint {
(&self.max_value - &self.min_value).magnitude()
}
pub fn to_index(&self, value: &BigInt) -> BigUint {
let s = self.simplest();
if *value == s {
return BigUint::zero();
}
let above = (&self.max_value - &s).magnitude();
let below = (&s - &self.min_value).magnitude();
let d_abs = (value - &s).magnitude();
let one = BigUint::from(1u32);
let d_minus_one = &d_abs - &one;
let mut count = std::cmp::min(&d_minus_one, &above) + std::cmp::min(&d_minus_one, &below);
if *value > s {
return count + &one;
}
if d_abs <= above {
count += BigUint::from(1u32);
}
count + BigUint::from(1u32)
}
#[allow(clippy::wrong_self_convention)]
pub fn from_index(&self, index: BigUint) -> Option<BigInt> {
let s = self.simplest();
if index.is_zero() {
return Some(s);
}
let above = (&self.max_value - &s).magnitude();
let below = (&s - &self.min_value).magnitude();
if index > &above + &below {
return None;
}
let two_a = std::cmp::min(&above, &below) << 1usize;
let one = BigUint::from(1u32);
let (d, up) = if index <= two_a {
let d = (&index + &one) >> 1u32;
let up = !(&index % &BigUint::from(2u32)).is_zero();
(d, up)
} else {
let d = &index - std::cmp::min(&above, &below);
(d, above > below)
};
let d = BigInt::from(d);
if up { Some(s + d) } else { Some(s - d) }
}
pub fn max_children(&self) -> BigUint {
self.max_index() + BigUint::from(1u32)
}
pub fn value_from_bigint(&self, v: &BigInt) -> Option<BigInt> {
if self.validate(v) {
Some(v.clone())
} else {
None
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BooleanChoice;
impl BooleanChoice {
pub fn simplest(&self) -> bool {
false
}
pub fn unit(&self) -> bool {
true
}
pub fn max_index(&self) -> crate::native::bignum::BigUint {
crate::native::bignum::BigUint::from(1u32)
}
pub fn to_index(&self, value: bool) -> crate::native::bignum::BigUint {
crate::native::bignum::BigUint::from(u32::from(value))
}
#[allow(clippy::wrong_self_convention)]
pub fn from_index(&self, index: crate::native::bignum::BigUint) -> Option<bool> {
use crate::native::bignum::BigUint;
if index == BigUint::from(0u32) {
Some(false)
} else if index == BigUint::from(1u32) {
Some(true)
} else {
None
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BytesChoice {
pub min_size: usize,
pub max_size: usize,
}
impl BytesChoice {
pub fn simplest(&self) -> Vec<u8> {
vec![0u8; self.min_size]
}
pub fn unit(&self) -> Vec<u8> {
if self.min_size > 0 {
let mut v = vec![0u8; self.min_size];
*v.last_mut().unwrap() = 1;
v
} else if self.max_size > 0 {
vec![1u8]
} else {
self.simplest()
}
}
pub fn validate(&self, value: &[u8]) -> bool {
self.min_size <= value.len() && value.len() <= self.max_size
}
pub fn sort_key(&self, value: &[u8]) -> (usize, Vec<u8>) {
(value.len(), value.to_vec())
}
pub fn max_index(&self) -> crate::native::bignum::BigUint {
self.to_index(&vec![0xffu8; self.max_size])
}
pub fn to_index(&self, value: &[u8]) -> crate::native::bignum::BigUint {
use crate::native::bignum::{BigUint, Zero};
let base = BigUint::from(256u32);
let mut offset = BigUint::zero();
for length in self.min_size..value.len() {
offset += base.pow(length as u32);
}
let mut position = BigUint::zero();
for &b in value {
position = position * &base + BigUint::from(b);
}
offset + position
}
#[allow(clippy::wrong_self_convention)]
pub fn from_index(&self, index: crate::native::bignum::BigUint) -> Option<Vec<u8>> {
use crate::native::bignum::BigUint;
let base = BigUint::from(256u32);
let mut remaining = index;
for length in self.min_size..=self.max_size {
let bucket = base.pow(length as u32);
if remaining < bucket {
let mut result: Vec<u8> = Vec::with_capacity(length);
for _ in 0..length {
let b: u8 = (&remaining % &base)
.try_into()
.expect("byte < 256 fits in u8");
result.push(b);
remaining /= &base;
}
result.reverse();
return Some(result);
}
remaining -= bucket;
}
None
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StringChoice {
pub intervals: IntervalSet,
pub min_size: usize,
pub max_size: usize,
}
impl StringChoice {
pub fn codepoint_key(&self, codepoint: u32) -> u32 {
let c = char::from_u32(codepoint).expect("non-surrogate codepoint");
self.intervals.index_from_char_in_shrink_order(c) as u32
}
pub fn key_to_codepoint(&self, key: u32) -> Option<u32> {
let key = key as usize;
if key >= self.intervals.len() {
return None;
}
Some(self.intervals.char_in_shrink_order(key) as u32)
}
pub(crate) fn simplest_codepoint(&self) -> u32 {
assert!(
!self.intervals.is_empty(),
"StringChoice::simplest_codepoint: empty alphabet"
);
self.intervals.char_in_shrink_order(0) as u32
}
pub fn simplest(&self) -> Vec<u32> {
vec![self.simplest_codepoint(); self.min_size]
}
pub fn unit(&self) -> Vec<u32> {
let simplest_cp = self.simplest_codepoint();
let second_cp = self.key_to_codepoint(1);
match second_cp {
Some(cp) if cp != simplest_cp => {
if self.min_size > 0 {
let mut v = self.simplest();
*v.last_mut().unwrap() = cp;
v
} else if self.max_size > 0 {
vec![cp]
} else {
self.simplest()
}
}
_ => {
if self.min_size < self.max_size {
vec![simplest_cp; self.min_size + 1]
} else {
self.simplest()
}
}
}
}
pub fn validate(&self, value: &[u32]) -> bool {
if !(self.min_size <= value.len() && value.len() <= self.max_size) {
return false;
}
value.iter().all(|&cp| self.intervals.contains(cp))
}
pub fn sort_key(&self, value: &[u32]) -> (usize, Vec<u32>) {
let keys: Vec<u32> = value.iter().map(|&cp| self.codepoint_key(cp)).collect();
(keys.len(), keys)
}
pub fn alpha_size(&self) -> u64 {
self.intervals.len() as u64
}
pub fn codepoint_rank(&self, codepoint: u32) -> u64 {
u64::from(self.codepoint_key(codepoint))
}
pub fn codepoint_at_rank(&self, rank: u64) -> u32 {
self.key_to_codepoint(rank as u32)
.expect("rank within alpha_size")
}
pub fn max_index(&self) -> crate::native::bignum::BigUint {
use crate::native::bignum::{BigUint, Zero};
let alpha = BigUint::from(self.alpha_size());
let mut total = BigUint::zero();
for length in self.min_size..=self.max_size {
total += alpha.pow(length as u32);
}
total - BigUint::from(1u32)
}
pub fn to_index(&self, value: &[u32]) -> crate::native::bignum::BigUint {
use crate::native::bignum::{BigUint, Zero};
let alpha = BigUint::from(self.alpha_size());
let mut offset = BigUint::zero();
for length in self.min_size..value.len() {
offset += alpha.pow(length as u32);
}
let mut position = BigUint::zero();
for &cp in value {
position = position * &alpha + BigUint::from(self.codepoint_rank(cp));
}
offset + position
}
#[allow(clippy::wrong_self_convention)]
pub fn from_index(&self, index: crate::native::bignum::BigUint) -> Option<Vec<u32>> {
use crate::native::bignum::{BigUint, Zero};
let alpha = BigUint::from(self.alpha_size());
assert!(!alpha.is_zero(), "StringChoice::from_index: empty alphabet");
let mut remaining = index;
for length in self.min_size..=self.max_size {
let bucket_size = alpha.pow(length as u32);
if remaining < bucket_size {
let mut cps: Vec<u32> = Vec::with_capacity(length);
for _ in 0..length {
let r: u64 = (&remaining % &alpha)
.try_into()
.expect("rank < alpha_size fits in u64");
cps.push(self.codepoint_at_rank(r));
remaining /= α
}
cps.reverse();
return Some(cps);
}
remaining -= bucket_size;
}
None
}
}
#[derive(Clone, Debug)]
pub struct FloatChoice {
pub min_value: f64,
pub max_value: f64,
pub allow_nan: bool,
pub allow_infinity: bool,
}
impl PartialEq for FloatChoice {
fn eq(&self, other: &Self) -> bool {
self.min_value.to_bits() == other.min_value.to_bits()
&& self.max_value.to_bits() == other.max_value.to_bits()
&& self.allow_nan == other.allow_nan
&& self.allow_infinity == other.allow_infinity
}
}
impl Eq for FloatChoice {}
impl FloatChoice {
pub fn simplest(&self) -> f64 {
use super::float_index::{float_to_index, index_to_float};
if self.validate(0.0) {
return 0.0;
}
let mut best: Option<f64> = None;
let mut best_key: (u64, bool) = (u64::MAX, true);
macro_rules! try_candidate {
($v:expr) => {{
let v: f64 = $v;
if !v.is_nan() && self.validate(v) {
let is_neg = v.is_sign_negative();
let mag = if is_neg { -v } else { v };
let key = (float_to_index(mag), is_neg);
if key < best_key {
best = Some(v);
best_key = key;
}
}
}};
}
if self.min_value.is_finite() {
try_candidate!(self.min_value);
}
if self.max_value.is_finite() {
try_candidate!(self.max_value);
}
if self.max_value >= 0.0 {
let lo_int = self.min_value.max(0.0).ceil() as i64;
try_candidate!(lo_int as f64);
}
if self.min_value <= 0.0 {
let hi_int = self.max_value.min(0.0).floor() as i64;
try_candidate!(hi_int as f64);
}
for exp_enc in 0u64..64 {
let base_idx = (1u64 << 63) | (exp_enc << 52);
if (base_idx, false) >= best_key {
break;
}
for mantissa_enc in 0u64..8 {
let idx = base_idx | mantissa_enc;
if (idx, false) >= best_key {
break;
}
let v = index_to_float(idx);
try_candidate!(v);
try_candidate!(-v);
}
}
if let Some(v) = best {
return v;
}
if self.allow_infinity && self.validate(f64::INFINITY) {
return f64::INFINITY;
}
if self.allow_infinity && self.validate(f64::NEG_INFINITY) {
return f64::NEG_INFINITY;
}
if self.allow_nan {
return f64::NAN;
}
panic!("FloatChoice::simplest: no valid float for this choice")
}
pub fn unit(&self) -> f64 {
use super::float_index::{float_to_index, index_to_float};
let s = self.simplest();
if s.is_nan() {
return s;
}
let base = float_to_index(s.abs());
let is_neg = s.is_sign_negative();
for offset in 1u64..4 {
let v_mag = index_to_float(base + offset);
let v = if is_neg { -v_mag } else { v_mag };
if !v.is_nan() && self.validate(v) {
return v;
}
}
s
}
pub fn validate(&self, v: f64) -> bool {
if v.is_nan() {
return self.allow_nan;
}
if v.is_infinite() {
if !self.allow_infinity {
return false;
}
if v == f64::NEG_INFINITY && self.min_value > f64::NEG_INFINITY {
return false;
}
if v == f64::INFINITY && self.max_value < f64::INFINITY {
return false;
}
return true;
}
sign_aware_lte(self.min_value, v) && sign_aware_lte(v, self.max_value)
}
pub fn sort_key(&self, v: f64) -> (u64, bool) {
use super::float_index::float_to_index;
if v.is_nan() {
return (u64::MAX, false);
}
let is_neg = v.is_sign_negative();
let mag = if is_neg { -v } else { v };
(float_to_index(mag), is_neg)
}
pub fn max_index(&self) -> crate::native::bignum::BigUint {
use crate::native::bignum::BigUint;
max_finite_global_rank() + BigUint::from(2u32) + BigUint::from(1u64 << 53)
}
pub fn to_index(&self, value: f64) -> crate::native::bignum::BigUint {
float_global_rank(value) - float_global_rank(self.simplest())
}
#[allow(clippy::wrong_self_convention)]
pub fn from_index(&self, index: crate::native::bignum::BigUint) -> Option<f64> {
let raw = float_global_rank(self.simplest()) + index;
let value = float_from_global_rank(raw)?;
if self.validate(value) {
Some(value)
} else {
None
}
}
}
fn float_global_rank(v: f64) -> crate::native::bignum::BigUint {
use super::float_index::float_to_index;
use crate::native::bignum::BigUint;
if v.is_nan() {
let bits = v.to_bits();
let nan_offset = bits & ((1u64 << 52) - 1);
let sign = bits >> 63;
return max_finite_global_rank()
+ BigUint::from(3u32)
+ BigUint::from(nan_offset) * BigUint::from(2u32)
+ BigUint::from(sign);
}
if v.is_infinite() {
return if v > 0.0 {
max_finite_global_rank() + BigUint::from(1u32)
} else {
max_finite_global_rank() + BigUint::from(2u32)
};
}
let is_neg = v.is_sign_negative();
let mag = if is_neg { -v } else { v };
let mag_idx = float_to_index(mag);
BigUint::from(mag_idx) * BigUint::from(2u32) + BigUint::from(u32::from(is_neg))
}
fn float_from_global_rank(rank: crate::native::bignum::BigUint) -> Option<f64> {
use super::float_index::index_to_float;
use crate::native::bignum::BigUint;
let max_finite = max_finite_global_rank();
if rank > max_finite {
let offset = &rank - &max_finite;
if offset == BigUint::from(1u32) {
return Some(f64::INFINITY);
}
if offset == BigUint::from(2u32) {
return Some(f64::NEG_INFINITY);
}
let nan_rel = offset - BigUint::from(3u32);
let sign: u64 = (&nan_rel % BigUint::from(2u32))
.try_into()
.expect("mod 2 fits in u64");
let mantissa_base: u64 = (nan_rel / BigUint::from(2u32)).try_into().ok()?;
let mantissa = mantissa_base | (1u64 << 51);
let bits = (sign << 63) | (0x7FFu64 << 52) | mantissa;
let v = f64::from_bits(bits);
return if v.is_nan() { Some(v) } else { None };
}
let is_neg_u: u64 = (&rank % BigUint::from(2u32))
.try_into()
.expect("mod 2 fits in u64");
let mag_big = rank / BigUint::from(2u32);
let mag_idx: u64 = (&mag_big).try_into().ok()?;
let mag = index_to_float(mag_idx);
Some(if is_neg_u == 1 { -mag } else { mag })
}
fn max_finite_global_rank() -> crate::native::bignum::BigUint {
use crate::native::bignum::BigUint;
let max_finite_lex = (1u64 << 63) | (2046u64 << 52) | ((1u64 << 52) - 1);
BigUint::from(max_finite_lex) * BigUint::from(2u32) + BigUint::from(1u32)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ChoiceKind {
Integer(IntegerChoice),
Boolean(BooleanChoice),
Float(FloatChoice),
Bytes(BytesChoice),
String(StringChoice),
}
#[derive(Clone, Debug)]
pub enum ChoiceValue {
Integer(BigInt),
Boolean(bool),
Float(f64),
Bytes(Vec<u8>),
String(Vec<u32>),
}
impl PartialEq for ChoiceValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(ChoiceValue::Integer(a), ChoiceValue::Integer(b)) => a == b,
(ChoiceValue::Boolean(a), ChoiceValue::Boolean(b)) => a == b,
(ChoiceValue::Float(a), ChoiceValue::Float(b)) => a.to_bits() == b.to_bits(),
(ChoiceValue::Bytes(a), ChoiceValue::Bytes(b)) => a == b,
(ChoiceValue::String(a), ChoiceValue::String(b)) => a == b,
_ => false,
}
}
}
impl Eq for ChoiceValue {}
impl std::hash::Hash for ChoiceValue {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
ChoiceValue::Integer(n) => n.hash(state),
ChoiceValue::Boolean(b) => b.hash(state),
ChoiceValue::Float(f) => f.to_bits().hash(state),
ChoiceValue::Bytes(v) => v.hash(state),
ChoiceValue::String(v) => v.hash(state),
}
}
}
fn sequence_max_children_saturating(
alphabet: u128,
min_size: usize,
max_size: usize,
cap: u128,
) -> u128 {
let mut total: u128 = 0;
let mut power: u128 = 1; for len in 0..=max_size {
if len >= min_size {
total = total.saturating_add(power);
if total >= cap {
return cap;
}
}
power = power.saturating_mul(alphabet);
}
total
}
impl ChoiceKind {
pub fn simplest(&self) -> ChoiceValue {
match self {
ChoiceKind::Integer(ic) => ChoiceValue::Integer(ic.simplest()),
ChoiceKind::Boolean(bc) => ChoiceValue::Boolean(bc.simplest()),
ChoiceKind::Float(fc) => ChoiceValue::Float(fc.simplest()),
ChoiceKind::Bytes(bc) => ChoiceValue::Bytes(bc.simplest()),
ChoiceKind::String(sc) => ChoiceValue::String(sc.simplest()),
}
}
pub fn unit(&self) -> ChoiceValue {
match self {
ChoiceKind::Integer(ic) => ChoiceValue::Integer(ic.unit()),
ChoiceKind::Boolean(bc) => ChoiceValue::Boolean(bc.unit()),
ChoiceKind::Float(fc) => ChoiceValue::Float(fc.unit()),
ChoiceKind::Bytes(bc) => ChoiceValue::Bytes(bc.unit()),
ChoiceKind::String(sc) => ChoiceValue::String(sc.unit()),
}
}
pub fn max_index(&self) -> crate::native::bignum::BigUint {
match self {
ChoiceKind::Integer(ic) => ic.max_index(),
ChoiceKind::Boolean(bc) => bc.max_index(),
ChoiceKind::Float(fc) => fc.max_index(),
ChoiceKind::Bytes(bc) => bc.max_index(),
ChoiceKind::String(sc) => sc.max_index(),
}
}
pub fn to_index(&self, value: &ChoiceValue) -> crate::native::bignum::BigUint {
match (self, value) {
(ChoiceKind::Integer(ic), ChoiceValue::Integer(v)) => ic.to_index(v),
(ChoiceKind::Boolean(bc), ChoiceValue::Boolean(v)) => bc.to_index(*v),
(ChoiceKind::Float(fc), ChoiceValue::Float(v)) => fc.to_index(*v),
(ChoiceKind::Bytes(bc), ChoiceValue::Bytes(v)) => bc.to_index(v),
(ChoiceKind::String(sc), ChoiceValue::String(v)) => sc.to_index(v),
_ => panic!("ChoiceKind::to_index: kind/value mismatch"),
}
}
#[allow(clippy::wrong_self_convention)]
pub fn from_index(&self, index: crate::native::bignum::BigUint) -> Option<ChoiceValue> {
match self {
ChoiceKind::Integer(ic) => ic.from_index(index).map(ChoiceValue::Integer),
ChoiceKind::Boolean(bc) => bc.from_index(index).map(ChoiceValue::Boolean),
ChoiceKind::Float(fc) => fc.from_index(index).map(ChoiceValue::Float),
ChoiceKind::Bytes(bc) => bc.from_index(index).map(ChoiceValue::Bytes),
ChoiceKind::String(sc) => sc.from_index(index).map(ChoiceValue::String),
}
}
pub fn validate(&self, value: &ChoiceValue) -> bool {
match (self, value) {
(ChoiceKind::Integer(ic), ChoiceValue::Integer(v)) => ic.validate(v),
(ChoiceKind::Boolean(_), ChoiceValue::Boolean(_)) => true,
(ChoiceKind::Float(fc), ChoiceValue::Float(v)) => fc.validate(*v),
(ChoiceKind::Bytes(bc), ChoiceValue::Bytes(v)) => bc.validate(v),
(ChoiceKind::String(sc), ChoiceValue::String(v)) => sc.validate(v),
_ => false,
}
}
pub fn max_children(&self) -> crate::native::bignum::BigUint {
use crate::native::bignum::BigUint;
match self {
ChoiceKind::Integer(ic) => ic.max_children(),
ChoiceKind::Boolean(_) => BigUint::from(2u32),
ChoiceKind::Float(fc) => fc.max_index() + BigUint::from(1u32),
ChoiceKind::Bytes(bc) => bc.max_index() + BigUint::from(1u32),
ChoiceKind::String(sc) => sc.max_index() + BigUint::from(1u32),
}
}
pub fn max_children_saturating(&self, cap: u128) -> u128 {
use crate::native::bignum::ToPrimitive;
let scalar = |max_index: crate::native::bignum::BigUint| {
max_index
.to_u128()
.map_or(cap, |mi| mi.saturating_add(1).min(cap))
};
match self {
ChoiceKind::Boolean(_) => 2u128.min(cap),
ChoiceKind::Integer(ic) => scalar(ic.max_index()),
ChoiceKind::Float(fc) => scalar(fc.max_index()),
ChoiceKind::Bytes(bc) => {
sequence_max_children_saturating(256, bc.min_size, bc.max_size, cap)
}
ChoiceKind::String(sc) => sequence_max_children_saturating(
sc.intervals.len() as u128,
sc.min_size,
sc.max_size,
cap,
),
}
}
pub fn random_value(&self, rng: &mut crate::native::rng::EngineRng) -> ChoiceValue {
match self {
ChoiceKind::Integer(ic) => {
ChoiceValue::Integer(crate::native::core::state::biased_integer_sample(ic, rng))
}
ChoiceKind::Boolean(_) => ChoiceValue::Boolean(
crate::native::core::state::weighted_boolean_sample(0.5, rng),
),
ChoiceKind::Float(fc) => {
ChoiceValue::Float(crate::native::core::state::biased_float_sample(fc, rng))
}
ChoiceKind::Bytes(bc) => {
ChoiceValue::Bytes(crate::native::core::state::biased_bytes_sample(bc, rng))
}
ChoiceKind::String(sc) => {
ChoiceValue::String(crate::native::core::state::biased_string_sample(sc, rng))
}
}
}
pub fn enumerate(&self, cap: u64) -> Option<Vec<ChoiceValue>> {
if self.max_children_saturating(cap as u128 + 1) > cap as u128 {
return None;
}
match self {
ChoiceKind::Integer(ic) => {
let mut v = Vec::new();
let mut n = ic.min_value.clone();
loop {
v.push(ChoiceValue::Integer(n.clone()));
if n == ic.max_value {
break;
}
n += 1;
}
Some(v)
}
ChoiceKind::Boolean(_) => Some(vec![
ChoiceValue::Boolean(false),
ChoiceValue::Boolean(true),
]),
ChoiceKind::Float(_) => {
unreachable!("FloatChoice max_children always exceeds u64::MAX cap")
}
ChoiceKind::Bytes(bc) => {
if bc.max_size == 0 {
Some(vec![ChoiceValue::Bytes(Vec::new())])
} else {
None
}
}
ChoiceKind::String(sc) => {
if sc.max_size == 0 {
Some(vec![ChoiceValue::String(Vec::new())])
} else {
None
}
}
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ChoiceNode {
pub kind: Arc<ChoiceKind>,
pub value: ChoiceValue,
pub was_forced: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ChoiceTemplateKind {
Simplest,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChoiceTemplate {
pub kind: ChoiceTemplateKind,
pub count: Option<usize>,
}
impl ChoiceTemplate {
pub fn simplest(count: Option<usize>) -> Self {
if let Some(n) = count {
assert!(n > 0, "ChoiceTemplate count must be positive (got 0)");
}
Self {
kind: ChoiceTemplateKind::Simplest,
count,
}
}
}
impl ChoiceNode {
pub fn new(kind: ChoiceKind, value: ChoiceValue, was_forced: bool) -> Self {
Self {
kind: Arc::new(kind),
value,
was_forced,
}
}
pub fn with_value(&self, value: ChoiceValue) -> ChoiceNode {
ChoiceNode {
kind: Arc::clone(&self.kind),
value,
was_forced: self.was_forced,
}
}
pub fn sort_key(&self) -> NodeSortKey {
match (self.kind.as_ref(), &self.value) {
(ChoiceKind::Integer(ic), ChoiceValue::Integer(v)) => {
let (mag, neg) = ic.sort_key(v);
NodeSortKey::Scalar(mag, neg)
}
(ChoiceKind::Boolean(_), ChoiceValue::Boolean(v)) => {
NodeSortKey::Scalar(BigUint::from(u32::from(*v)), false)
}
(ChoiceKind::Float(fc), ChoiceValue::Float(v)) => {
let (mag, neg) = fc.sort_key(*v);
NodeSortKey::Scalar(BigUint::from(mag), neg)
}
(ChoiceKind::Bytes(bc), ChoiceValue::Bytes(v)) => {
let (len, bytes) = bc.sort_key(v);
NodeSortKey::Sequence(len, bytes.into_iter().map(u32::from).collect())
}
(ChoiceKind::String(sc), ChoiceValue::String(v)) => {
let (len, keys) = sc.sort_key(v);
NodeSortKey::Sequence(len, keys)
}
_ => unreachable!("mismatched choice kind and value"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NodeSortKey {
Scalar(crate::native::bignum::BigUint, bool),
Sequence(usize, Vec<u32>),
}
pub enum NodeSortKeyRef<'a> {
Scalar(crate::native::bignum::BigUint, bool),
Bytes(&'a [u8]),
String(&'a StringChoice, &'a [u32]),
}
impl<'a> NodeSortKeyRef<'a> {
fn category(&self) -> u8 {
match self {
NodeSortKeyRef::Scalar(..) => 0,
NodeSortKeyRef::Bytes(..) | NodeSortKeyRef::String(..) => 1,
}
}
fn seq_len(&self) -> usize {
match self {
NodeSortKeyRef::Bytes(b) => b.len(),
NodeSortKeyRef::String(_, cps) => cps.len(),
NodeSortKeyRef::Scalar(..) => unreachable!("seq_len on scalar"),
}
}
fn seq_key_at(&self, i: usize) -> u32 {
match self {
NodeSortKeyRef::Bytes(b) => b[i] as u32,
NodeSortKeyRef::String(sc, cps) => sc.codepoint_key(cps[i]),
NodeSortKeyRef::Scalar(..) => unreachable!("seq_key_at on scalar"),
}
}
}
impl<'a> PartialEq for NodeSortKeyRef<'a> {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == std::cmp::Ordering::Equal
}
}
impl<'a> Eq for NodeSortKeyRef<'a> {}
impl<'a> PartialOrd for NodeSortKeyRef<'a> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Ord for NodeSortKeyRef<'a> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
use std::cmp::Ordering;
match (self, other) {
(NodeSortKeyRef::Scalar(am, an), NodeSortKeyRef::Scalar(bm, bn)) => {
(am, an).cmp(&(bm, bn))
}
(NodeSortKeyRef::Scalar(..), _) | (_, NodeSortKeyRef::Scalar(..)) => {
self.category().cmp(&other.category())
}
_ => {
let la = self.seq_len();
let lb = other.seq_len();
match la.cmp(&lb) {
Ordering::Equal => {}
ord => return ord,
}
for i in 0..la {
match self.seq_key_at(i).cmp(&other.seq_key_at(i)) {
Ordering::Equal => continue,
ord => return ord,
}
}
Ordering::Equal
}
}
}
}
impl ChoiceNode {
pub fn sort_key_ref(&self) -> NodeSortKeyRef<'_> {
match (self.kind.as_ref(), &self.value) {
(ChoiceKind::Integer(ic), ChoiceValue::Integer(v)) => {
let (mag, neg) = ic.sort_key(v);
NodeSortKeyRef::Scalar(mag, neg)
}
(ChoiceKind::Boolean(_), ChoiceValue::Boolean(v)) => {
NodeSortKeyRef::Scalar(BigUint::from(u32::from(*v)), false)
}
(ChoiceKind::Float(fc), ChoiceValue::Float(v)) => {
let (mag, neg) = fc.sort_key(*v);
NodeSortKeyRef::Scalar(BigUint::from(mag), neg)
}
(ChoiceKind::Bytes(_), ChoiceValue::Bytes(v)) => NodeSortKeyRef::Bytes(v),
(ChoiceKind::String(sc), ChoiceValue::String(v)) => NodeSortKeyRef::String(sc, v),
_ => unreachable!("mismatched choice kind and value"),
}
}
}
#[derive(Clone, Copy)]
pub struct NodesSortKey<'a>(pub &'a [ChoiceNode]);
impl<'a> PartialEq for NodesSortKey<'a> {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == std::cmp::Ordering::Equal
}
}
impl<'a> Eq for NodesSortKey<'a> {}
impl<'a> PartialOrd for NodesSortKey<'a> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Ord for NodesSortKey<'a> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
use std::cmp::Ordering;
match self.0.len().cmp(&other.0.len()) {
Ordering::Equal => {}
ord => return ord,
}
for (a, b) in self.0.iter().zip(other.0.iter()) {
match a.sort_key_ref().cmp(&b.sort_key_ref()) {
Ordering::Equal => continue,
ord => return ord,
}
}
Ordering::Equal
}
}
pub fn sort_key(nodes: &[ChoiceNode]) -> NodesSortKey<'_> {
NodesSortKey(nodes)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Status {
EarlyStop = 0,
Invalid = 1,
Valid = 2,
Interesting = 3,
}
#[derive(Debug)]
pub enum EngineError {
Overrun,
InvalidTestCase,
InvalidArgument(String),
}
impl std::fmt::Display for EngineError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EngineError::Overrun => write!(f, "choice buffer exhausted (Overrun)"),
EngineError::InvalidTestCase => write!(f, "engine rejected test case (Invalid)"),
EngineError::InvalidArgument(msg) => write!(f, "{msg}"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct InterestingOrigin(pub String);
#[cfg(test)]
#[path = "../../../tests/embedded/native/choices_tests.rs"]
mod tests;