use crate::cesr::bexter::Bexter;
use crate::cesr::number::Number;
use crate::cesr::{bex_dex, num_dex, BaseMatter, Parsable};
use crate::errors::MatterError;
use crate::keri::core::serdering::SadValue;
use crate::Matter;
use num_rational::Rational32;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::fmt::Debug;
#[derive(Debug, Clone, PartialEq)]
pub enum WeightSpec {
Simple(Rational32),
WeightedVec(Vec<WeightSpec>),
WeightedMap(Rational32, Vec<Rational32>),
}
#[derive(Debug, Clone)]
pub struct Tholder {
_weighted: bool,
_size: usize,
_sith: TholderSith,
_thold: TholderThold,
_bexter: Option<Bexter>,
_number: Option<Number>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum TholderSith {
Integer(usize),
HexString(String),
Json(String),
Weights(Vec<WeightedSithElement>),
}
impl TholderSith {
pub fn from_sad_value(val: SadValue) -> Result<Self, MatterError> {
match val {
SadValue::Number(num) => Ok(TholderSith::Integer(num.as_u64().unwrap() as usize)),
SadValue::String(s) => {
if s.contains("[") {
Ok(TholderSith::Json(s))
} else {
Ok(TholderSith::HexString(s))
}
}
_ => Err(MatterError::ValueError(format!(
"invalid sith value: {:?}",
val
))),
}
}
}
impl fmt::Display for TholderSith {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TholderSith::Integer(n) => write!(f, "{}", n),
TholderSith::HexString(s) => write!(f, "{}", s),
TholderSith::Json(s) => write!(f, "{}", s),
TholderSith::Weights(w) => {
match serde_json::to_string(w) {
Ok(json) => write!(f, "{}", json),
Err(_) => write!(f, "<invalid weights>"),
}
}
}
}
}
impl fmt::Display for WeightedSithElement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match serde_json::to_string(self) {
Ok(json) => write!(f, "{}", json),
Err(_) => write!(f, "<invalid weight element>"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum WeightedSithElement {
Simple(String),
Array(Vec<WeightedSithElement>),
Complex(HashMap<String, Vec<WeightedSithElement>>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum TholderThold {
Integer(usize),
Weighted(Vec<Vec<WeightSpec>>),
}
impl Default for Tholder {
fn default() -> Self {
Tholder {
_weighted: false,
_size: 0,
_sith: TholderSith::HexString(String::new()),
_thold: TholderThold::Integer(0),
_bexter: None,
_number: None,
}
}
}
impl Tholder {
pub fn new(
thold: Option<TholderThold>,
limen: Option<Vec<u8>>,
sith: Option<TholderSith>,
) -> Result<Self, MatterError> {
if let Some(t) = thold {
return Self::process_thold(t);
} else if let Some(l) = limen {
let mut tholder = Tholder::default();
tholder.process_limen(l.as_slice(), Some(false))?;
return Ok(tholder);
} else if let Some(s) = sith {
let mut tholder = Tholder::default();
tholder.process_sith(s)?;
return Ok(tholder);
}
Err(MatterError::EmptyMaterialError(
"Missing threshold expression.".to_string(),
))
}
fn process_thold(thold: TholderThold) -> Result<Self, MatterError> {
let mut tholder = Tholder::default();
match thold {
TholderThold::Integer(num) => {
tholder.process_unweighted(num)?;
}
TholderThold::Weighted(clauses) => {
tholder.process_weighted(clauses)?;
}
}
Ok(tholder)
}
pub fn process_limen(&mut self, limen: &[u8], strip: Option<bool>) -> Result<(), MatterError> {
let matter = BaseMatter::from_qb64b(&mut limen.to_vec(), strip)?;
if num_dex::MAP.contains_key(matter.code()) {
let number = Number::new(Some(matter.raw()), Some(matter.code()), None, None)?;
self.process_unweighted(number.num() as usize)?;
} else if bex_dex::MAP.contains_key(matter.code()) {
let bexter = Bexter::new(Some(matter.raw()), Some(matter.code()), None, None)?;
let t = bexter.bext()?.replace('s', "/");
let clauses: Vec<&str> = t.split('a').collect();
let mut thold = Vec::new();
for c in clauses {
let c_parts: Vec<&str> = c.split('c').collect();
let mut clause = Vec::new();
for e in c_parts {
if let Some(k_pos) = e.find('k') {
let k = &e[..k_pos];
let v = &e[(k_pos + 1)..];
let v_parts: Vec<&str> = v.split('v').collect();
let weights: Vec<Rational32> = v_parts
.iter()
.map(|w| Self::weight(w))
.collect::<Result<Vec<Rational32>, _>>()?;
clause.push(WeightSpec::WeightedMap(Self::weight(k)?, weights));
} else {
clause.push(WeightSpec::Simple(Self::weight(e)?));
}
}
thold.push(clause);
}
self.process_weighted(thold)?;
} else {
return Err(MatterError::InvalidCode(format!(
"Invalid code for limen = {}",
matter.code()
)));
}
Ok(())
}
pub fn process_sith(&mut self, sith: TholderSith) -> Result<(), MatterError> {
match sith {
TholderSith::Integer(threshold) => self.process_unweighted(threshold),
TholderSith::HexString(hex_str) => {
match i32::from_str_radix(&hex_str, 16) {
Ok(threshold) => self.process_unweighted(threshold as usize),
Err(_) => Err(MatterError::ValueError(format!(
"Invalid hex string sith = {}",
hex_str
))),
}
}
TholderSith::Json(json_val) => {
if let Ok(nested_clauses) =
serde_json::from_str::<Vec<Vec<WeightedSithElement>>>(json_val.as_str())
{
let mut thold = Vec::new();
for clause_elements in nested_clauses {
let mut clause_specs = Vec::new();
for element in clause_elements {
let mut element_specs = self.process_weight_clause(&element)?;
clause_specs.append(&mut element_specs);
}
thold.push(clause_specs);
}
self.process_weighted(thold)
} else {
let clauses: Vec<WeightedSithElement> =
serde_json::from_str(json_val.as_str()).unwrap();
if clauses.is_empty() {
return Err(MatterError::ValueError("Empty weight list".to_string()));
}
let mut single_clause = Vec::new();
for clause in clauses {
let mut processed_specs = self.process_weight_clause(&clause)?;
single_clause.append(&mut processed_specs);
}
self.process_weighted(vec![single_clause])
}
}
TholderSith::Weights(clauses) => {
if clauses.is_empty() {
return Err(MatterError::ValueError("Empty weight list".to_string()));
}
let mut single_clause = Vec::new();
for clause in clauses {
let mut processed_specs = self.process_weight_clause(&clause)?;
single_clause.append(&mut processed_specs);
}
self.process_weighted(vec![single_clause])
}
}
}
fn process_weight_clause(
&self,
clause: &WeightedSithElement,
) -> Result<Vec<WeightSpec>, MatterError> {
match clause {
WeightedSithElement::Simple(weight_str) => {
let weight = Self::weight(weight_str)?;
Ok(vec![WeightSpec::Simple(weight)])
}
WeightedSithElement::Array(elements) => {
if elements.is_empty() {
return Err(MatterError::ValueError(
"Empty weight array not allowed".to_string(),
));
}
let mut specs = Vec::new();
for element in elements {
let mut element_specs = self.process_weight_clause(element)?;
specs.append(&mut element_specs);
}
Ok(specs)
}
WeightedSithElement::Complex(weight_map) => {
if weight_map.is_empty() {
return Err(MatterError::ValueError(
"Empty weight map not allowed".to_string(),
));
}
let mut specs = Vec::new();
for (key_str, value_elements) in weight_map {
if value_elements.is_empty() {
return Err(MatterError::ValueError(
"Empty weight values not allowed".to_string(),
));
}
let key_weight = Self::weight(key_str)?;
let mut value_weights = Vec::new();
for value_element in value_elements {
if let WeightedSithElement::Simple(weight_str) = value_element {
value_weights.push(Self::weight(weight_str)?);
} else {
return Err(MatterError::ValueError(
"Complex weight values must be simple weights".to_string(),
));
}
}
specs.push(WeightSpec::WeightedMap(key_weight, value_weights));
}
Ok(specs)
}
}
}
pub fn satisfy(&self, indices: &[usize]) -> bool {
if self._weighted {
self.satisfy_weighted(indices)
} else {
self.satisfy_numeric(indices)
}
}
fn satisfy_numeric(&self, indices: &[usize]) -> bool {
if let TholderThold::Integer(num) = self._thold {
indices.len() >= num
} else {
false
}
}
fn satisfy_weighted(&self, indices: &[usize]) -> bool {
if indices.is_empty() {
return false;
}
let mut unique_indices: Vec<usize> = indices.iter().cloned().collect();
unique_indices.sort_unstable();
unique_indices.dedup();
let mut sats = vec![false; self._size];
for &idx in &unique_indices {
if idx < self._size {
sats[idx] = true;
}
}
if let TholderThold::Weighted(ref clauses) = self._thold {
let mut wio = 0;
for clause in clauses {
let mut cw = Rational32::new(0, 1);
for element in clause {
match element {
WeightSpec::Simple(weight) => {
if wio < sats.len() && sats[wio] {
cw += *weight;
}
wio += 1;
}
WeightSpec::WeightedMap(key_weight, nested_weights) => {
let mut vw = Rational32::new(0, 1); for nested_weight in nested_weights {
if wio < sats.len() && sats[wio] {
vw += *nested_weight;
}
wio += 1;
}
if vw >= Rational32::new(1, 1) {
cw += *key_weight;
}
}
WeightSpec::WeightedVec(nested_specs) => {
let mut vw = Rational32::new(0, 1);
for nested_spec in nested_specs {
if let WeightSpec::Simple(weight) = nested_spec {
if wio < sats.len() && sats[wio] {
vw += *weight;
}
wio += 1;
}
}
if vw >= Rational32::new(1, 1) {
cw += Rational32::new(1, 1); }
}
}
}
if cw < Rational32::new(1, 1) {
return false;
}
}
true
} else {
false
}
}
pub fn weighted(&self) -> bool {
self._weighted
}
pub fn thold(&self) -> &TholderThold {
&self._thold
}
pub fn size(&self) -> usize {
self._size
}
pub fn limen(&self) -> Vec<u8> {
if self._weighted {
self._bexter.as_ref().unwrap().qb64b()
} else {
self._number.as_ref().unwrap().qb64b()
}
}
pub fn sith(&self) -> TholderSith {
match &self._thold {
TholderThold::Weighted(clauses) => {
let mut sith_clauses = Vec::new();
for clause in clauses {
let mut sith_clause = Vec::new();
for element in clause {
match element {
WeightSpec::Simple(weight) => {
let weight_str = if *weight > Rational32::new(0, 1)
&& *weight < Rational32::new(1, 1)
{
format!("{}/{}", weight.numer(), weight.denom())
} else {
format!("{}", weight.numer() / weight.denom())
};
sith_clause.push(WeightedSithElement::Simple(weight_str));
}
WeightSpec::WeightedMap(key_weight, nested_weights) => {
let key_str = if *key_weight > Rational32::new(0, 1)
&& *key_weight < Rational32::new(1, 1)
{
format!("{}/{}", key_weight.numer(), key_weight.denom())
} else {
format!("{}", key_weight.numer() / key_weight.denom())
};
let value_elements: Vec<WeightedSithElement> = nested_weights
.iter()
.map(|w| {
let weight_str = if *w > Rational32::new(0, 1)
&& *w < Rational32::new(1, 1)
{
format!("{}/{}", w.numer(), w.denom())
} else {
format!("{}", w.numer() / w.denom())
};
WeightedSithElement::Simple(weight_str)
})
.collect();
let mut map = HashMap::new();
map.insert(key_str, value_elements);
sith_clause.push(WeightedSithElement::Complex(map));
}
WeightSpec::WeightedVec(_) => {
}
}
}
sith_clauses.push(sith_clause);
}
if sith_clauses.len() == 1 {
TholderSith::Weights(sith_clauses.into_iter().next().unwrap())
} else {
TholderSith::Json(serde_json::to_string(&sith_clauses).unwrap_or_default())
}
}
TholderThold::Integer(n) => TholderSith::HexString(format!("{:x}", n)),
}
}
pub fn json(&self) -> String {
match &self.sith() {
TholderSith::Json(json_str) => json_str.clone(), other => serde_json::to_string(other).unwrap_or_default(), }
}
pub fn num(&self) -> Option<usize> {
if !self._weighted {
if let TholderThold::Integer(n) = self._thold {
return Some(n);
}
}
None
}
fn process_unweighted(&mut self, thold: usize) -> Result<(), MatterError> {
if thold == 0 {
self._weighted = false;
self._size = thold;
self._sith = TholderSith::Integer(thold);
self._thold = TholderThold::Integer(thold);
self._bexter = None;
self._number = Some(Number::from_numh(format!("{:x}", thold).as_str())?);
Ok(())
} else {
self._weighted = false;
self._size = thold;
self._sith = TholderSith::Integer(thold);
self._thold = TholderThold::Integer(thold);
self._bexter = None;
self._number = Some(Number::from_numh(format!("{:x}", thold).as_str())?);
Ok(())
}
}
fn process_weighted(&mut self, thold: Vec<Vec<WeightSpec>>) -> Result<(), MatterError> {
for clause in &thold {
let mut top_weights = Vec::new();
for e in clause {
match e {
WeightSpec::Simple(weight) => {
top_weights.push(*weight);
}
WeightSpec::WeightedMap(key_weight, nested_weights) => {
top_weights.push(*key_weight);
let sum_nested: Rational32 = nested_weights.iter().sum();
if sum_nested < Rational32::new(1, 1) {
return Err(MatterError::ValueError(format!(
"Invalid sith clause, nested clause weight sum must be >= 1. Got: {:?}",
sum_nested
)));
}
}
WeightSpec::WeightedVec(nested_specs) => {
let mut nested_sum = Rational32::new(0, 1);
for nested_spec in nested_specs {
if let WeightSpec::Simple(weight) = nested_spec {
nested_sum = nested_sum + *weight;
}
}
if nested_sum < Rational32::new(1, 1) {
return Err(MatterError::ValueError(format!(
"Invalid sith clause, nested vec weight sum must be >= 1. Got: {:?}",
nested_sum
)));
}
top_weights.push(Rational32::new(1, 1));
}
}
}
let sum_top: Rational32 = top_weights.iter().sum();
if sum_top < Rational32::new(1, 1) {
return Err(MatterError::ValueError(format!(
"Invalid sith clause, top level weight sum must be >= 1. Got: {:?}",
sum_top
)));
}
}
let mut size = 0;
for clause in &thold {
for e in clause {
match e {
WeightSpec::Simple(_) => {
size += 1;
}
WeightSpec::WeightedMap(_, nested_weights) => {
size += nested_weights.len();
}
WeightSpec::WeightedVec(nested_specs) => {
size += nested_specs.len();
}
}
}
}
let mut ta = Vec::new();
for clause in &thold {
let mut bc = Vec::new();
for e in clause {
match e {
WeightSpec::Simple(weight) => {
let w_str =
if *weight > Rational32::new(0, 1) && *weight < Rational32::new(1, 1) {
format!("{}s{}", weight.numer(), weight.denom())
} else {
format!("{}", weight.numer() / weight.denom())
};
bc.push(w_str);
}
WeightSpec::WeightedMap(key_weight, nested_weights) => {
let k = if *key_weight > Rational32::new(0, 1)
&& *key_weight < Rational32::new(1, 1)
{
format!("{}s{}", key_weight.numer(), key_weight.denom())
} else {
format!("{}", key_weight.numer() / key_weight.denom())
};
let v = nested_weights
.iter()
.map(|f| {
if *f > Rational32::new(0, 1) && *f < Rational32::new(1, 1) {
format!("{}s{}", f.numer(), f.denom())
} else {
format!("{}", f.numer() / f.denom())
}
})
.collect::<Vec<String>>()
.join("v");
let kv = format!("{}k{}", k, v);
bc.push(kv);
}
WeightSpec::WeightedVec(_nested_specs) => {
bc.push("1".to_string()); }
}
}
ta.push(bc);
}
let bext = ta
.iter()
.map(|bc| bc.join("c"))
.collect::<Vec<String>>()
.join("a");
let bexter = Bexter::from_bext(bext.as_bytes())?;
self._weighted = true;
self._size = size;
self._sith = if thold.len() == 1 {
let mut weight_elements = Vec::new();
for element in &thold[0] {
match element {
WeightSpec::Simple(weight) => {
let weight_str =
if *weight > Rational32::new(0, 1) && *weight < Rational32::new(1, 1) {
format!("{}/{}", weight.numer(), weight.denom())
} else {
format!("{}", weight.numer() / weight.denom())
};
weight_elements.push(WeightedSithElement::Simple(weight_str));
}
WeightSpec::WeightedMap(key_weight, nested_weights) => {
let key_str = if *key_weight > Rational32::new(0, 1)
&& *key_weight < Rational32::new(1, 1)
{
format!("{}/{}", key_weight.numer(), key_weight.denom())
} else {
format!("{}", key_weight.numer() / key_weight.denom())
};
let value_elements: Vec<WeightedSithElement> = nested_weights
.iter()
.map(|w| {
let weight_str =
if *w > Rational32::new(0, 1) && *w < Rational32::new(1, 1) {
format!("{}/{}", w.numer(), w.denom())
} else {
format!("{}", w.numer() / w.denom())
};
WeightedSithElement::Simple(weight_str)
})
.collect();
let mut map = HashMap::new();
map.insert(key_str, value_elements);
weight_elements.push(WeightedSithElement::Complex(map));
}
WeightSpec::WeightedVec(_) => {
}
}
}
TholderSith::Weights(weight_elements)
} else {
let mut sith_clauses = Vec::new();
for clause in &thold {
let mut sith_clause = Vec::new();
for element in clause {
match element {
WeightSpec::Simple(weight) => {
let weight_str = if *weight > Rational32::new(0, 1)
&& *weight < Rational32::new(1, 1)
{
format!("{}/{}", weight.numer(), weight.denom())
} else {
format!("{}", weight.numer() / weight.denom())
};
sith_clause.push(WeightedSithElement::Simple(weight_str));
}
WeightSpec::WeightedMap(key_weight, nested_weights) => {
let key_str = if *key_weight > Rational32::new(0, 1)
&& *key_weight < Rational32::new(1, 1)
{
format!("{}/{}", key_weight.numer(), key_weight.denom())
} else {
format!("{}", key_weight.numer() / key_weight.denom())
};
let value_elements: Vec<WeightedSithElement> = nested_weights
.iter()
.map(|w| {
let weight_str = if *w > Rational32::new(0, 1)
&& *w < Rational32::new(1, 1)
{
format!("{}/{}", w.numer(), w.denom())
} else {
format!("{}", w.numer() / w.denom())
};
WeightedSithElement::Simple(weight_str)
})
.collect();
let mut map = HashMap::new();
map.insert(key_str, value_elements);
sith_clause.push(WeightedSithElement::Complex(map));
}
WeightSpec::WeightedVec(_) => {
}
}
}
sith_clauses.push(sith_clause);
}
TholderSith::Json(serde_json::to_string(&sith_clauses).unwrap_or_default())
};
self._thold = TholderThold::Weighted(thold);
self._bexter = Some(bexter);
self._number = None;
Ok(())
}
#[allow(missing_docs)]
pub fn weight(weight_str: &str) -> Result<Rational32, MatterError> {
if weight_str.is_empty() {
return Err(MatterError::ParseError("Empty weight string".to_string()));
}
if let Ok(float_val) = weight_str.parse::<f64>() {
let int_val = float_val as i32;
if (float_val - int_val as f64).abs() > f64::EPSILON {
return Err(MatterError::WeightError(format!(
"Invalid weight str got float w={}.",
weight_str
)));
}
}
if let Ok(int_val) = weight_str.parse::<i32>() {
if int_val < 0 || int_val > 1 {
return Err(MatterError::WeightError(format!(
"Invalid weight not 0 <= {} <= 1.",
int_val
)));
}
return Ok(Rational32::new(int_val, 1));
}
if let Some(idx) = weight_str.find('/') {
let numerator_str = &weight_str[0..idx];
let denominator_str = &weight_str[idx + 1..];
match (numerator_str.parse::<i32>(), denominator_str.parse::<i32>()) {
(Ok(num), Ok(denom)) => {
if denom <= 0 {
return Err(MatterError::WeightError(format!(
"Denominator must be positive, got: {}",
denom
)));
}
let rational = Rational32::new(num, denom);
if rational < Rational32::new(0, 1) || rational > Rational32::new(1, 1) {
return Err(MatterError::WeightError(format!(
"Invalid weight not 0 <= {} <= 1.",
rational
)));
}
Ok(rational)
}
_ => Err(MatterError::ParseError(format!(
"Failed to parse rational number: {}",
weight_str
))),
}
} else {
Err(MatterError::ParseError(format!(
"Invalid weight format: {}",
weight_str
)))
}
}
}
impl fmt::Display for Tholder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Tholder(weighted={}, size={})",
self._weighted, self._size
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tholder_empty_material_error() {
let result = Tholder::new(None, None, None);
assert!(result.is_err());
if let Err(MatterError::EmptyMaterialError(_)) = result {
} else {
panic!("Expected EmptyMaterialError, got: {:?}", result);
}
}
#[test]
fn test_tholder_validation_errors() {
let result = Tholder::new(None, None, Some(TholderSith::Integer(usize::MAX)));
let json_str1 = r#"[1]"#;
let weights_result1: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str1);
if weights_result1.is_ok() {
let weights1 = weights_result1.unwrap();
let result1 = Tholder::new(None, None, Some(TholderSith::Weights(weights1)));
assert!(result1.is_err(), "Should fail with integer weight");
}
let json_str2 = r#"[2]"#;
let weights_result2: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str2);
if weights_result2.is_ok() {
let weights2 = weights_result2.unwrap();
let result2 = Tholder::new(None, None, Some(TholderSith::Weights(weights2)));
assert!(result2.is_err(), "Should fail with integer weight");
}
let json_str3 = r#"["2"]"#;
let weights_result3: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str3);
if weights_result3.is_ok() {
let weights3 = weights_result3.unwrap();
let result3 = Tholder::new(None, None, Some(TholderSith::Weights(weights3)));
assert!(result3.is_err(), "Should fail with weight > 1");
}
let json_str4 = r#"["0.5", "0.5"]"#;
let weights_result4: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str4);
if weights_result4.is_ok() {
let weights4 = weights_result4.unwrap();
let result4 = Tholder::new(None, None, Some(TholderSith::Weights(weights4)));
assert!(result4.is_err(), "Should fail with float weights");
}
let result5 = Tholder::new(None, None, Some(TholderSith::HexString("1.0".to_string())));
assert!(
result5.is_err(),
"Should fail with float string for unweighted"
);
let result6 = Tholder::new(None, None, Some(TholderSith::HexString("0.5".to_string())));
assert!(
result6.is_err(),
"Should fail with float string for unweighted"
);
let result7 = Tholder::new(
None,
None,
Some(TholderSith::HexString("1.0/2.0".to_string())),
);
assert!(result7.is_err(), "Should fail with float ratio");
let empty_weights: Vec<WeightedSithElement> = vec![];
let result8 = Tholder::new(None, None, Some(TholderSith::Weights(empty_weights)));
assert!(result8.is_err(), "Should fail with empty weights array");
}
#[test]
fn test_tholder_weighted_validation_errors() {
let json_str1 = r#"["1/3", "1/2", []]"#;
let weights_result1: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str1);
assert!(weights_result1.is_ok(), "JSON parsing should succeed");
let weights1 = weights_result1.unwrap();
let result1 = Tholder::new(None, None, Some(TholderSith::Weights(weights1)));
assert!(
result1.is_err(),
"Should fail with mixed array containing empty element"
);
let json_str2 = r#"["1/3", "1/2"]"#;
let weights_result2: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str2);
if weights_result2.is_ok() {
let weights2 = weights_result2.unwrap();
let result2 = Tholder::new(None, None, Some(TholderSith::Weights(weights2)));
}
let json_str3 = r#"[[], []]"#;
let weights_result3: Result<Vec<Vec<WeightedSithElement>>, _> =
serde_json::from_str(json_str3);
if weights_result3.is_ok() {
let nested_weights = weights_result3.unwrap();
let flat_weights: Vec<WeightedSithElement> =
nested_weights.into_iter().flatten().collect();
let result3 = Tholder::new(None, None, Some(TholderSith::Weights(flat_weights)));
assert!(result3.is_err(), "Should fail with empty nested arrays");
}
let json_str4 = r#"["1/2", "1/2", "3/2"]"#;
let weights_result4: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str4);
if weights_result4.is_ok() {
let weights4 = weights_result4.unwrap();
let result4 = Tholder::new(None, None, Some(TholderSith::Weights(weights4)));
assert!(result4.is_err(), "Should fail with weight > 1 (3/2)");
}
let json_str5 = r#"["1/2", "1/2", "2/1"]"#;
let weights_result5: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str5);
if weights_result5.is_ok() {
let weights5 = weights_result5.unwrap();
let result5 = Tholder::new(None, None, Some(TholderSith::Weights(weights5)));
assert!(result5.is_err(), "Should fail with weight > 1 (2/1)");
}
let json_str6 = r#"["1/2", "1/2", "2"]"#;
let weights_result6: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str6);
if weights_result6.is_ok() {
let weights6 = weights_result6.unwrap();
let result6 = Tholder::new(None, None, Some(TholderSith::Weights(weights6)));
assert!(result6.is_err(), "Should fail with integer weight > 1");
}
}
#[test]
fn test_tholder_nested_validation_errors() {
let json_str1 = r#"[["1/2", "1/2", "3/2"]]"#;
let weights_result1: Result<Vec<Vec<WeightedSithElement>>, _> =
serde_json::from_str(json_str1);
if weights_result1.is_ok() {
let nested_weights = weights_result1.unwrap();
let flat_weights: Vec<WeightedSithElement> =
nested_weights.into_iter().flatten().collect();
let result1 = Tholder::new(None, None, Some(TholderSith::Weights(flat_weights)));
assert!(result1.is_err(), "Should fail with nested weight > 1");
}
let json_str2 = r#"[["1/2", "1/2"], "1"]"#;
let weights_result2: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str2);
if weights_result2.is_ok() {
let weights2 = weights_result2.unwrap();
let result2 = Tholder::new(None, None, Some(TholderSith::Weights(weights2)));
}
let json_str3 = r#"[["1/2", "1/2"], "1.0"]"#;
let weights_result3: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str3);
if weights_result3.is_ok() {
let weights3 = weights_result3.unwrap();
let result3 = Tholder::new(None, None, Some(TholderSith::Weights(weights3)));
assert!(
result3.is_err(),
"Should fail with float in mixed structure"
);
}
let json_str4 = r#"["1/2", "1/2", []]"#;
let weights_result4: Result<Vec<WeightedSithElement>, _> = serde_json::from_str(json_str4);
if weights_result4.is_ok() {
let weights4 = weights_result4.unwrap();
let result4 = Tholder::new(None, None, Some(TholderSith::Weights(weights4)));
assert!(
result4.is_err(),
"Should fail with empty array in mixed structure"
);
}
}
#[test]
fn test_tholder_unweighted_valid_case() {
let tholder = Tholder::new(None, None, Some(TholderSith::Integer(2))).unwrap();
assert!(!tholder.weighted());
assert_eq!(tholder.num().unwrap(), 2);
let TholderThold::Integer(thold) = tholder.thold() else {
panic!("Expected integer threshold");
};
assert_eq!(*thold, 2);
}
#[test]
fn test_tholder_unweighted() {
let expected_limen = b"MAAL";
let mut tholder = Tholder::default();
tholder
.process_sith(TholderSith::HexString("b".to_string()))
.unwrap();
assert!(!tholder.weighted());
let TholderThold::Integer(thold) = tholder.thold() else {
panic!("Invalid threshold")
};
assert_eq!(tholder.size(), *thold);
assert_eq!(*thold, 11);
assert_eq!(tholder.limen(), expected_limen);
let TholderSith::HexString(sith) = tholder.sith() else {
panic!("Invalid sith")
};
assert_eq!(sith, "b");
assert_eq!(tholder.json(), "\"b\"");
assert_eq!(tholder.num().unwrap(), 11);
let insufficient_indices = vec![0, 1, 2];
assert!(!tholder.satisfy(&insufficient_indices));
let sufficient_indices: Vec<usize> = (0..*thold).collect();
assert!(tholder.satisfy(&sufficient_indices));
}
#[test]
fn test_tholder_integer_sith() {
let expected_limen = b"MAAL";
let tholder = Tholder::new(None, None, Some(TholderSith::Integer(11))).unwrap();
assert!(!tholder.weighted());
let TholderThold::Integer(thold) = tholder.thold() else {
panic!("Invalid threshold")
};
assert_eq!(tholder.size(), *thold);
assert_eq!(*thold, 11);
assert_eq!(tholder.limen(), expected_limen);
let TholderSith::HexString(sith) = tholder.sith() else {
panic!("Invalid sith")
};
assert_eq!(sith, "b");
assert_eq!(tholder.json(), "\"b\"");
assert_eq!(tholder.num().unwrap(), 11);
assert!(!tholder.satisfy(&[0, 1, 2]));
assert!(tholder.satisfy(&(0..11).collect::<Vec<_>>()));
}
#[test]
fn test_tholder_from_limen() {
let limen = b"MAAL";
let tholder = Tholder::new(None, Some(limen.to_vec()), None).unwrap();
assert!(!tholder.weighted());
let TholderThold::Integer(thold) = tholder.thold() else {
panic!("Invalid threshold")
};
assert_eq!(tholder.size(), *thold);
assert_eq!(*thold, 11);
assert_eq!(tholder.limen(), limen);
let TholderSith::HexString(sith) = tholder.sith() else {
panic!("Invalid sith")
};
assert_eq!(sith, "b");
assert_eq!(tholder.json(), "\"b\"");
assert_eq!(tholder.num().unwrap(), 11);
assert!(!tholder.satisfy(&[0, 1, 2]));
assert!(tholder.satisfy(&(0..11).collect::<Vec<_>>()));
}
#[test]
fn test_tholder_from_thold() {
let expected_limen = b"MAAL";
let tholder = Tholder::new(Some(TholderThold::Integer(11)), None, None).unwrap();
assert!(!tholder.weighted());
let TholderThold::Integer(thold) = tholder.thold() else {
panic!("Invalid threshold")
};
assert_eq!(tholder.size(), *thold);
assert_eq!(*thold, 11);
assert_eq!(tholder.limen(), expected_limen);
let TholderSith::HexString(sith) = tholder.sith() else {
panic!("Invalid sith")
};
assert_eq!(sith, "b");
assert_eq!(tholder.json(), "\"b\"");
assert_eq!(tholder.num().unwrap(), 11);
assert!(!tholder.satisfy(&[0, 1, 2]));
assert!(tholder.satisfy(&(0..11).collect::<Vec<_>>()));
}
#[test]
fn test_tholder_hex_f() {
let expected_limen = b"MAAP";
let tholder =
Tholder::new(None, None, Some(TholderSith::HexString("f".to_string()))).unwrap();
assert!(!tholder.weighted());
let TholderThold::Integer(thold) = tholder.thold() else {
panic!("Invalid threshold")
};
assert_eq!(tholder.size(), *thold);
assert_eq!(*thold, 15);
assert_eq!(tholder.limen(), expected_limen);
let TholderSith::HexString(sith) = tholder.sith() else {
panic!("Invalid sith")
};
assert_eq!(sith, "f");
assert_eq!(tholder.json(), "\"f\"");
assert_eq!(tholder.num().unwrap(), 15);
assert!(!tholder.satisfy(&[0, 1, 2]));
assert!(tholder.satisfy(&(0..15).collect::<Vec<_>>()));
}
#[test]
fn test_tholder_integer_2() {
let expected_limen = b"MAAC";
let tholder = Tholder::new(None, None, Some(TholderSith::Integer(2))).unwrap();
assert!(!tholder.weighted());
let TholderThold::Integer(thold) = tholder.thold() else {
panic!("Invalid threshold")
};
assert_eq!(tholder.size(), *thold);
assert_eq!(*thold, 2);
assert_eq!(tholder.limen(), expected_limen);
let TholderSith::HexString(sith) = tholder.sith() else {
panic!("Invalid sith")
};
assert_eq!(sith, "2");
assert_eq!(tholder.json(), "\"2\"");
assert_eq!(tholder.num().unwrap(), 2);
assert!(tholder.satisfy(&[0, 1, 2])); assert!(tholder.satisfy(&(0..2).collect::<Vec<_>>())); }
#[test]
fn test_tholder_integer_1() {
let expected_limen = b"MAAB";
let tholder = Tholder::new(None, None, Some(TholderSith::Integer(1))).unwrap();
assert!(!tholder.weighted());
let TholderThold::Integer(thold) = tholder.thold() else {
panic!("Invalid threshold")
};
assert_eq!(tholder.size(), *thold);
assert_eq!(*thold, 1);
assert_eq!(tholder.limen(), expected_limen);
let TholderSith::HexString(sith) = tholder.sith() else {
panic!("Invalid sith")
};
assert_eq!(sith, "1");
assert_eq!(tholder.json(), "\"1\"");
assert_eq!(tholder.num().unwrap(), 1);
assert!(tholder.satisfy(&[0])); assert!(tholder.satisfy(&(0..1).collect::<Vec<_>>())); }
#[test]
fn test_deserialize_weighted_sith_elements() -> Result<(), Box<dyn std::error::Error>> {
let json_str1 = r#"["1/2", "1/2", "1/4", "1/4", "1/4"]"#;
let elements1: Vec<WeightedSithElement> = serde_json::from_str(json_str1)?;
assert_eq!(elements1.len(), 5);
if let WeightedSithElement::Simple(val) = &elements1[0] {
assert_eq!(val, "1/2");
} else {
panic!("Expected Simple element");
}
let json_str2 = r#"[["1/2", "1/2", "1/4", "1/4", "1/4"], ["1", "1"]]"#;
let elements2: Vec<Vec<WeightedSithElement>> = serde_json::from_str(json_str2)?;
assert_eq!(elements2.len(), 2);
assert_eq!(elements2[0].len(), 5);
assert_eq!(elements2[1].len(), 2);
if let WeightedSithElement::Simple(val) = &elements2[0][0] {
assert_eq!(val, "1/2");
} else {
panic!("Expected Simple element");
}
if let WeightedSithElement::Simple(val) = &elements2[1][0] {
assert_eq!(val, "1");
} else {
panic!("Expected Simple element");
}
let json_str3 = r#"[{"1/3":["1/2", "1/2", "1/2"]}, "1/3", "1/2", {"1/2": ["1", "1"]}]"#;
let elements3: Vec<WeightedSithElement> = serde_json::from_str(json_str3)?;
assert_eq!(elements3.len(), 4);
match &elements3[0] {
WeightedSithElement::Complex(map) => {
assert_eq!(map.len(), 1);
let values = map.get("1/3").expect("Key 1/3 should exist");
assert_eq!(values.len(), 3);
if let WeightedSithElement::Simple(val) = &values[0] {
assert_eq!(val, "1/2");
} else {
panic!("Expected Simple element in complex map");
}
}
_ => panic!("Expected Complex element"),
}
if let WeightedSithElement::Simple(val) = &elements3[1] {
assert_eq!(val, "1/3");
} else {
panic!("Expected Simple element");
}
if let WeightedSithElement::Simple(val) = &elements3[2] {
assert_eq!(val, "1/2");
} else {
panic!("Expected Simple element");
}
match &elements3[3] {
WeightedSithElement::Complex(map) => {
assert_eq!(map.len(), 1);
let values = map.get("1/2").expect("Key 1/2 should exist");
assert_eq!(values.len(), 2);
if let WeightedSithElement::Simple(val) = &values[0] {
assert_eq!(val, "1");
} else {
panic!("Expected Simple element in complex map");
}
}
_ => panic!("Expected Complex element"),
}
let json_str4 = r#"[[{"1/3":["1/2", "1/2", "1/2"]}, "1/2", {"1/2": ["1", "1"]}], ["1/2", {"1/2": ["1", "1"]}]]"#;
let elements4: Vec<Vec<WeightedSithElement>> = serde_json::from_str(json_str4)?;
assert_eq!(elements4.len(), 2);
assert_eq!(elements4[0].len(), 3);
assert_eq!(elements4[1].len(), 2);
match &elements4[0][0] {
WeightedSithElement::Complex(map) => {
assert_eq!(map.len(), 1);
let values = map.get("1/3").expect("Key 1/3 should exist");
assert_eq!(values.len(), 3);
if let WeightedSithElement::Simple(val) = &values[0] {
assert_eq!(val, "1/2");
} else {
panic!("Expected Simple element in complex map");
}
}
_ => panic!("Expected Complex element"),
}
if let WeightedSithElement::Simple(val) = &elements4[0][1] {
assert_eq!(val, "1/2");
} else {
panic!("Expected Simple element");
}
match &elements4[1][1] {
WeightedSithElement::Complex(map) => {
assert_eq!(map.len(), 1);
let values = map.get("1/2").expect("Key 1/2 should exist");
assert_eq!(values.len(), 2);
if let WeightedSithElement::Simple(val) = &values[0] {
assert_eq!(val, "1");
} else {
panic!("Expected Simple element in complex map");
}
}
_ => panic!("Expected Complex element"),
}
let json_str5 = r#"["1/2", ["1/3", "1/3", "1/3"], "1/4"]"#;
let elements5: Vec<WeightedSithElement> = serde_json::from_str(json_str5)?;
assert_eq!(elements5.len(), 3);
match &elements5[1] {
WeightedSithElement::Array(arr) => {
assert_eq!(arr.len(), 3);
if let WeightedSithElement::Simple(val) = &arr[0] {
assert_eq!(val, "1/3");
} else {
panic!("Expected Simple element in array");
}
}
_ => panic!("Expected Array element"),
}
if let WeightedSithElement::Simple(val) = &elements1[0] {
let weight = Tholder::weight(val)?;
assert_eq!(weight, Rational32::new(1, 2));
}
Ok(())
}
#[test]
fn test_deserialize_json_bytes() -> Result<(), Box<dyn std::error::Error>> {
let json_bytes = br#"["1/2", "1/2", "1/4", "1/4", "1/4"]"#;
let elements: Vec<WeightedSithElement> = serde_json::from_slice(json_bytes)?;
assert_eq!(elements.len(), 5);
if let WeightedSithElement::Simple(val) = &elements[0] {
assert_eq!(val, "1/2");
let weight = Tholder::weight(val)?;
assert_eq!(weight, Rational32::new(1, 2));
} else {
panic!("Expected Simple element");
}
let complex_json_bytes =
br#"[{"1/3":["1/2", "1/2", "1/2"]}, "1/3", "1/2", {"1/2": ["1", "1"]}]"#;
let complex_elements: Vec<WeightedSithElement> =
serde_json::from_slice(complex_json_bytes)?;
assert_eq!(complex_elements.len(), 4);
match &complex_elements[0] {
WeightedSithElement::Complex(map) => {
assert!(map.contains_key("1/3"));
let values = map.get("1/3").unwrap();
assert_eq!(values.len(), 3);
}
_ => panic!("Expected Complex element"),
}
Ok(())
}
#[test]
fn test_process_weighted_sith() -> Result<(), Box<dyn std::error::Error>> {
let json_str = r#"["1/2", "1/2", "1/4", "1/4", "1/4"]"#;
let elements: Vec<WeightedSithElement> = serde_json::from_str(json_str)?;
let mut total_weight = Rational32::new(0, 1);
for element in &elements {
if let WeightedSithElement::Simple(val) = element {
total_weight = total_weight + Tholder::weight(val)?;
}
}
assert!(total_weight > Rational32::new(1, 1));
assert_eq!(total_weight, Rational32::new(7, 4));
Ok(())
}
fn test_tholder_weighted() -> Result<(), Box<dyn std::error::Error>> {
let json_str = r#"["1/2", "1/2", "1/4", "1/4", "1/4"]"#;
let weights: Vec<WeightedSithElement> = serde_json::from_str(json_str)?;
let tholder = Tholder::new(None, None, Some(TholderSith::Weights(weights)))?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 5);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 1);
assert_eq!(clauses[0].len(), 5);
if let WeightSpec::Simple(fraction) = clauses[0][0] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec for first element");
}
if let WeightSpec::Simple(fraction) = clauses[0][1] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec for second element");
}
if let WeightSpec::Simple(fraction) = clauses[0][2] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for third element");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"4AAFA1s2c1s2c1s4c1s4c1s4");
if let TholderSith::Weights(weights) = tholder.sith() {
assert_eq!(weights.len(), 5);
if let WeightedSithElement::Simple(s) = &weights[0] {
assert_eq!(s, "1/2");
} else {
panic!("Expected Simple weight element");
}
} else {
panic!("Expected Weights sith type");
}
let expected_json = r#"["1/2","1/2","1/4","1/4","1/4"]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[0, 2, 4])); assert!(tholder.satisfy(&[0, 1])); assert!(tholder.satisfy(&[1, 3, 4])); assert!(tholder.satisfy(&[0, 1, 2, 3, 4])); assert!(tholder.satisfy(&[3, 2, 0])); assert!(tholder.satisfy(&[0, 0, 1, 2, 1]));
assert!(!tholder.satisfy(&[0, 2])); assert!(!tholder.satisfy(&[2, 3, 4]));
Ok(())
}
#[test]
fn test_tholder_weighted_simple() -> Result<(), Box<dyn std::error::Error>> {
let json_str = r#"["1/2", "1/2", "1/4", "1/4", "1/4"]"#;
let weights: Vec<WeightedSithElement> = serde_json::from_str(json_str)?;
let tholder = Tholder::new(None, None, Some(TholderSith::Weights(weights)))?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 5);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 1); assert_eq!(clauses[0].len(), 5);
if let WeightSpec::Simple(fraction) = clauses[0][0] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec for first element");
}
if let WeightSpec::Simple(fraction) = clauses[0][1] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec for second element");
}
if let WeightSpec::Simple(fraction) = clauses[0][2] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for third element");
}
if let WeightSpec::Simple(fraction) = clauses[0][3] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for fourth element");
}
if let WeightSpec::Simple(fraction) = clauses[0][4] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for fifth element");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"4AAFA1s2c1s2c1s4c1s4c1s4");
if let TholderSith::Weights(weights) = tholder.sith() {
assert_eq!(weights.len(), 5);
if let WeightedSithElement::Simple(s) = &weights[0] {
assert_eq!(s, "1/2");
} else {
panic!("Expected Simple weight element");
}
if let WeightedSithElement::Simple(s) = &weights[4] {
assert_eq!(s, "1/4");
} else {
panic!("Expected Simple weight element");
}
} else {
panic!("Expected Weights sith type");
}
let expected_json = r#"["1/2","1/2","1/4","1/4","1/4"]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[0, 2, 4])); assert!(tholder.satisfy(&[0, 1])); assert!(tholder.satisfy(&[1, 3, 4])); assert!(tholder.satisfy(&[0, 1, 2, 3, 4])); assert!(tholder.satisfy(&[3, 2, 0])); assert!(tholder.satisfy(&[0, 0, 1, 2, 1]));
assert!(!tholder.satisfy(&[0, 2])); assert!(!tholder.satisfy(&[2, 3, 4]));
Ok(())
}
#[test]
fn test_tholder_weighted_with_zero() -> Result<(), Box<dyn std::error::Error>> {
let json_str = r#"["1/2", "1/2", "1/4", "1/4", "1/4", "0"]"#;
let weights: Vec<WeightedSithElement> = serde_json::from_str(json_str)?;
let tholder = Tholder::new(None, None, Some(TholderSith::Weights(weights)))?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 6);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 1); assert_eq!(clauses[0].len(), 6);
if let WeightSpec::Simple(fraction) = clauses[0][5] {
assert_eq!(fraction, Rational32::new(0, 1));
} else {
panic!("Expected Simple weight spec for last element");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"6AAGAAA1s2c1s2c1s4c1s4c1s4c0");
if let TholderSith::Weights(weights) = tholder.sith() {
assert_eq!(weights.len(), 6);
if let WeightedSithElement::Simple(s) = &weights[5] {
assert_eq!(s, "0");
} else {
panic!("Expected Simple weight element");
}
} else {
panic!("Expected Weights sith type");
}
let expected_json = r#"["1/2","1/2","1/4","1/4","1/4","0"]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[0, 2, 4])); assert!(tholder.satisfy(&[0, 1])); assert!(tholder.satisfy(&[1, 3, 4])); assert!(tholder.satisfy(&[0, 1, 2, 3, 4])); assert!(tholder.satisfy(&[3, 2, 0])); assert!(tholder.satisfy(&[0, 0, 1, 2, 1]));
assert!(!tholder.satisfy(&[0, 2, 5])); assert!(!tholder.satisfy(&[2, 3, 4, 5]));
Ok(())
}
#[test]
fn test_tholder_weighted_nested_single_clause() -> Result<(), Box<dyn std::error::Error>> {
let json_str = r#"[["1/2", "1/2", "1/4", "1/4", "1/4"]]"#;
let nested_weights: Vec<Vec<WeightedSithElement>> = serde_json::from_str(json_str)?;
let tholder = Tholder::new(None, None, Some(TholderSith::Json(json_str.to_string())))?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 5);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 1); assert_eq!(clauses[0].len(), 5);
if let WeightSpec::Simple(fraction) = clauses[0][0] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec for first element");
}
if let WeightSpec::Simple(fraction) = clauses[0][1] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec for second element");
}
if let WeightSpec::Simple(fraction) = clauses[0][2] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for third element");
}
if let WeightSpec::Simple(fraction) = clauses[0][3] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for fourth element");
}
if let WeightSpec::Simple(fraction) = clauses[0][4] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for fifth element");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"4AAFA1s2c1s2c1s4c1s4c1s4");
if let TholderSith::Weights(weights) = tholder.sith() {
assert_eq!(weights.len(), 5);
if let WeightedSithElement::Simple(s) = &weights[0] {
assert_eq!(s, "1/2");
} else {
panic!("Expected Simple weight element");
}
} else {
panic!("Expected Weights sith type");
}
let expected_json = r#"["1/2","1/2","1/4","1/4","1/4"]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[1, 2, 3])); assert!(tholder.satisfy(&[0, 1, 2])); assert!(tholder.satisfy(&[1, 3, 4])); assert!(tholder.satisfy(&[0, 1, 2, 3, 4])); assert!(tholder.satisfy(&[3, 2, 0])); assert!(tholder.satisfy(&[0, 0, 1, 2, 1, 4, 4]));
assert!(!tholder.satisfy(&[0, 2])); assert!(!tholder.satisfy(&[2, 3, 4]));
Ok(())
}
#[test]
fn test_tholder_weighted_multi_clause() -> Result<(), Box<dyn std::error::Error>> {
let json_str = r#"[["1/2", "1/2", "1/4", "1/4", "1/4"], ["1", "1"]]"#;
let nested_weights: Vec<Vec<WeightedSithElement>> = serde_json::from_str(json_str)?;
let mut all_clauses = Vec::new();
for clause_weights in nested_weights {
let mut clause_specs = Vec::new();
for weight_element in clause_weights {
if let WeightedSithElement::Simple(weight_str) = weight_element {
let weight = Tholder::weight(&weight_str)?;
clause_specs.push(WeightSpec::Simple(weight));
}
}
all_clauses.push(clause_specs);
}
let tholder = Tholder::new(Some(TholderThold::Weighted(all_clauses)), None, None)?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 7);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 2); assert_eq!(clauses[0].len(), 5); assert_eq!(clauses[1].len(), 2);
if let WeightSpec::Simple(fraction) = clauses[0][0] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[1][0] {
assert_eq!(fraction, Rational32::new(1, 1));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[1][1] {
assert_eq!(fraction, Rational32::new(1, 1));
} else {
panic!("Expected Simple weight spec");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"4AAGA1s2c1s2c1s4c1s4c1s4a1c1");
if let TholderSith::Json(json_str) = tholder.sith() {
let expected_nested = r#"[["1/2","1/2","1/4","1/4","1/4"],["1","1"]]"#;
assert_eq!(json_str, expected_nested);
} else {
panic!("Expected Json sith type for multi-clause");
}
let expected_json = r#"[["1/2","1/2","1/4","1/4","1/4"],["1","1"]]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[1, 2, 3, 5])); assert!(tholder.satisfy(&[0, 1, 6]));
assert!(!tholder.satisfy(&[0, 1])); assert!(!tholder.satisfy(&[5, 6])); assert!(!tholder.satisfy(&[2, 3, 4])); assert!(!tholder.satisfy(&[]));
Ok(())
}
#[test]
fn test_tholder_json_string_multi_clause() -> Result<(), Box<dyn std::error::Error>> {
let json_str = r#"[["1/2", "1/2", "1/4", "1/4", "1/4"], ["1", "1"]]"#;
let tholder = Tholder::new(None, None, Some(TholderSith::Json(json_str.to_string())))?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 7);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 2); assert_eq!(clauses[0].len(), 5); assert_eq!(clauses[1].len(), 2);
if let WeightSpec::Simple(fraction) = clauses[0][0] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[0][1] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[0][2] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[1][0] {
assert_eq!(fraction, Rational32::new(1, 1));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[1][1] {
assert_eq!(fraction, Rational32::new(1, 1));
} else {
panic!("Expected Simple weight spec");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"4AAGA1s2c1s2c1s4c1s4c1s4a1c1");
if let TholderSith::Json(json_str) = tholder.sith() {
let expected_nested = r#"[["1/2","1/2","1/4","1/4","1/4"],["1","1"]]"#;
assert_eq!(json_str, expected_nested);
} else {
panic!("Expected Json sith type for multi-clause");
}
let expected_json = r#"[["1/2","1/2","1/4","1/4","1/4"],["1","1"]]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[1, 2, 3, 5])); assert!(tholder.satisfy(&[0, 1, 6]));
assert!(!tholder.satisfy(&[0, 1])); assert!(!tholder.satisfy(&[5, 6])); assert!(!tholder.satisfy(&[2, 3, 4])); assert!(!tholder.satisfy(&[]));
Ok(())
}
#[test]
fn test_tholder_json_string_single_clause_nested() -> Result<(), Box<dyn std::error::Error>> {
let json_str = r#"[["1/2", "1/2", "1/4", "1/4", "1/4"]]"#;
let tholder = Tholder::new(None, None, Some(TholderSith::Json(json_str.to_string())))?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 5);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 1); assert_eq!(clauses[0].len(), 5);
if let WeightSpec::Simple(fraction) = clauses[0][0] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec for first element");
}
if let WeightSpec::Simple(fraction) = clauses[0][1] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec for second element");
}
if let WeightSpec::Simple(fraction) = clauses[0][2] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for third element");
}
if let WeightSpec::Simple(fraction) = clauses[0][3] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for fourth element");
}
if let WeightSpec::Simple(fraction) = clauses[0][4] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for fifth element");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"4AAFA1s2c1s2c1s4c1s4c1s4");
if let TholderSith::Weights(weights) = tholder.sith() {
assert_eq!(weights.len(), 5);
if let WeightedSithElement::Simple(s) = &weights[0] {
assert_eq!(s, "1/2");
} else {
panic!("Expected Simple weight element");
}
} else {
panic!("Expected Weights sith type");
}
let expected_json = r#"["1/2","1/2","1/4","1/4","1/4"]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[1, 2, 3])); assert!(tholder.satisfy(&[0, 1, 2])); assert!(tholder.satisfy(&[1, 3, 4])); assert!(tholder.satisfy(&[0, 1, 2, 3, 4])); assert!(tholder.satisfy(&[3, 2, 0])); assert!(tholder.satisfy(&[0, 0, 1, 2, 1, 4, 4]));
assert!(!tholder.satisfy(&[0, 2])); assert!(!tholder.satisfy(&[2, 3, 4]));
Ok(())
}
#[test]
fn test_tholder_json_string_simple_array() -> Result<(), Box<dyn std::error::Error>> {
let json_str = r#"["1/2", "1/2", "1/4", "1/4", "1/4", "0"]"#;
let tholder = Tholder::new(None, None, Some(TholderSith::Json(json_str.to_string())))?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 6);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 1); assert_eq!(clauses[0].len(), 6);
if let WeightSpec::Simple(fraction) = clauses[0][0] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec for first element");
}
if let WeightSpec::Simple(fraction) = clauses[0][1] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec for second element");
}
if let WeightSpec::Simple(fraction) = clauses[0][2] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for third element");
}
if let WeightSpec::Simple(fraction) = clauses[0][3] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for fourth element");
}
if let WeightSpec::Simple(fraction) = clauses[0][4] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec for fifth element");
}
if let WeightSpec::Simple(fraction) = clauses[0][5] {
assert_eq!(fraction, Rational32::new(0, 1));
} else {
panic!("Expected Simple weight spec for last element");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"6AAGAAA1s2c1s2c1s4c1s4c1s4c0");
if let TholderSith::Weights(weights) = tholder.sith() {
assert_eq!(weights.len(), 6);
if let WeightedSithElement::Simple(s) = &weights[0] {
assert_eq!(s, "1/2");
} else {
panic!("Expected Simple weight element");
}
if let WeightedSithElement::Simple(s) = &weights[5] {
assert_eq!(s, "0");
} else {
panic!("Expected Simple weight element");
}
} else {
panic!("Expected Weights sith type");
}
let expected_json = r#"["1/2","1/2","1/4","1/4","1/4","0"]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[0, 2, 4])); assert!(tholder.satisfy(&[0, 1])); assert!(tholder.satisfy(&[1, 3, 4])); assert!(tholder.satisfy(&[0, 1, 2, 3, 4])); assert!(tholder.satisfy(&[3, 2, 0])); assert!(tholder.satisfy(&[0, 0, 1, 2, 1]));
assert!(!tholder.satisfy(&[0, 2, 5])); assert!(!tholder.satisfy(&[2, 3, 4, 5]));
Ok(())
}
#[test]
fn test_tholder_from_limen_multi_clause() -> Result<(), Box<dyn std::error::Error>> {
let limen = b"4AAGA1s2c1s2c1s4c1s4c1s4a1c1".to_vec();
let tholder = Tholder::new(None, Some(limen), None)?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 7);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 2); assert_eq!(clauses[0].len(), 5); assert_eq!(clauses[1].len(), 2);
if let WeightSpec::Simple(fraction) = clauses[0][0] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[0][1] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[0][2] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[0][3] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[0][4] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[1][0] {
assert_eq!(fraction, Rational32::new(1, 1));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[1][1] {
assert_eq!(fraction, Rational32::new(1, 1));
} else {
panic!("Expected Simple weight spec");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"4AAGA1s2c1s2c1s4c1s4c1s4a1c1");
if let TholderSith::Json(json_str) = tholder.sith() {
let expected_nested = r#"[["1/2","1/2","1/4","1/4","1/4"],["1","1"]]"#;
assert_eq!(json_str, expected_nested);
} else {
panic!("Expected Json sith type for multi-clause");
}
let expected_json = r#"[["1/2","1/2","1/4","1/4","1/4"],["1","1"]]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[1, 2, 3, 5])); assert!(tholder.satisfy(&[0, 1, 6]));
assert!(!tholder.satisfy(&[0, 1])); assert!(!tholder.satisfy(&[5, 6])); assert!(!tholder.satisfy(&[2, 3, 4])); assert!(!tholder.satisfy(&[]));
Ok(())
}
#[test]
fn test_tholder_from_thold_multi_clause() -> Result<(), Box<dyn std::error::Error>> {
let thold = TholderThold::Weighted(vec![
vec![
WeightSpec::Simple(Rational32::new(1, 2)),
WeightSpec::Simple(Rational32::new(1, 2)),
WeightSpec::Simple(Rational32::new(1, 4)),
WeightSpec::Simple(Rational32::new(1, 4)),
WeightSpec::Simple(Rational32::new(1, 4)),
],
vec![
WeightSpec::Simple(Rational32::new(1, 1)),
WeightSpec::Simple(Rational32::new(1, 1)),
],
]);
let tholder = Tholder::new(Some(thold), None, None)?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 7);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 2); assert_eq!(clauses[0].len(), 5); assert_eq!(clauses[1].len(), 2);
if let WeightSpec::Simple(fraction) = clauses[0][0] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[0][1] {
assert_eq!(fraction, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[0][2] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[0][3] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[0][4] {
assert_eq!(fraction, Rational32::new(1, 4));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[1][0] {
assert_eq!(fraction, Rational32::new(1, 1));
} else {
panic!("Expected Simple weight spec");
}
if let WeightSpec::Simple(fraction) = clauses[1][1] {
assert_eq!(fraction, Rational32::new(1, 1));
} else {
panic!("Expected Simple weight spec");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"4AAGA1s2c1s2c1s4c1s4c1s4a1c1");
if let TholderSith::Json(json_str) = tholder.sith() {
let expected_nested = r#"[["1/2","1/2","1/4","1/4","1/4"],["1","1"]]"#;
assert_eq!(json_str, expected_nested);
} else {
panic!("Expected Json sith type for multi-clause");
}
let expected_json = r#"[["1/2","1/2","1/4","1/4","1/4"],["1","1"]]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[1, 2, 3, 5])); assert!(tholder.satisfy(&[0, 1, 6]));
assert!(!tholder.satisfy(&[0, 1])); assert!(!tholder.satisfy(&[5, 6])); assert!(!tholder.satisfy(&[2, 3, 4])); assert!(!tholder.satisfy(&[]));
Ok(())
}
#[test]
fn test_tholder_nested_weighted_single_clause() -> Result<(), Box<dyn std::error::Error>> {
let json_str = r#"[{"1/3":["1/2", "1/2", "1/2"]}, "1/3", "1/2", {"1/2": ["1", "1"]}]"#;
let tholder = Tholder::new(None, None, Some(TholderSith::Json(json_str.to_string())))?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 7);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 1); assert_eq!(clauses[0].len(), 4);
if let WeightSpec::WeightedMap(key_weight, nested_weights) = &clauses[0][0] {
assert_eq!(*key_weight, Rational32::new(1, 3));
assert_eq!(nested_weights.len(), 3);
assert_eq!(nested_weights[0], Rational32::new(1, 2));
assert_eq!(nested_weights[1], Rational32::new(1, 2));
assert_eq!(nested_weights[2], Rational32::new(1, 2));
} else {
panic!("Expected WeightedMap for first element");
}
if let WeightSpec::Simple(weight) = clauses[0][1] {
assert_eq!(weight, Rational32::new(1, 3));
} else {
panic!("Expected Simple weight for second element");
}
if let WeightSpec::Simple(weight) = clauses[0][2] {
assert_eq!(weight, Rational32::new(1, 2));
} else {
panic!("Expected Simple weight for third element");
}
if let WeightSpec::WeightedMap(key_weight, nested_weights) = &clauses[0][3] {
assert_eq!(*key_weight, Rational32::new(1, 2));
assert_eq!(nested_weights.len(), 2);
assert_eq!(nested_weights[0], Rational32::new(1, 1));
assert_eq!(nested_weights[1], Rational32::new(1, 1));
} else {
panic!("Expected WeightedMap for fourth element");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"4AAIA1s3k1s2v1s2v1s2c1s3c1s2c1s2k1v1");
if let TholderSith::Weights(weights) = tholder.sith() {
assert_eq!(weights.len(), 4);
if let WeightedSithElement::Complex(map) = &weights[0] {
assert!(map.contains_key("1/3"));
let values = map.get("1/3").unwrap();
assert_eq!(values.len(), 3);
} else {
panic!("Expected Complex element for first weight");
}
if let WeightedSithElement::Simple(s) = &weights[1] {
assert_eq!(s, "1/3");
} else {
panic!("Expected Simple element for second weight");
}
if let WeightedSithElement::Simple(s) = &weights[2] {
assert_eq!(s, "1/2");
} else {
panic!("Expected Simple element for third weight");
}
if let WeightedSithElement::Complex(map) = &weights[3] {
assert!(map.contains_key("1/2"));
let values = map.get("1/2").unwrap();
assert_eq!(values.len(), 2);
} else {
panic!("Expected Complex element for fourth weight");
}
} else {
panic!("Expected Weights sith type");
}
let expected_json = r#"[{"1/3":["1/2","1/2","1/2"]},"1/3","1/2",{"1/2":["1","1"]}]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[0, 2, 3, 6])); assert!(tholder.satisfy(&[3, 4, 5])); assert!(tholder.satisfy(&[1, 2, 3, 4])); assert!(tholder.satisfy(&[4, 6])); assert!(tholder.satisfy(&[4, 2, 0, 3])); assert!(tholder.satisfy(&[0, 0, 1, 2, 1, 5, 6, 3]));
assert!(!tholder.satisfy(&[0, 2, 5])); assert!(!tholder.satisfy(&[2, 3, 4]));
Ok(())
}
#[test]
fn test_tholder_nested_weighted_from_limen() -> Result<(), Box<dyn std::error::Error>> {
let limen = b"4AAIA1s3k1s2v1s2v1s2c1s3c1s2c1s2k1v1".to_vec();
let tholder = Tholder::new(None, Some(limen), None)?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 7);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 1);
assert_eq!(clauses[0].len(), 4);
if let WeightSpec::WeightedMap(key_weight, nested_weights) = &clauses[0][0] {
assert_eq!(*key_weight, Rational32::new(1, 3));
assert_eq!(nested_weights.len(), 3);
} else {
panic!("Expected WeightedMap for first element");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(tholder.limen(), b"4AAIA1s3k1s2v1s2v1s2c1s3c1s2c1s2k1v1");
let expected_json = r#"[{"1/3":["1/2","1/2","1/2"]},"1/3","1/2",{"1/2":["1","1"]}]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
Ok(())
}
#[test]
fn test_tholder_nested_weighted_from_thold() -> Result<(), Box<dyn std::error::Error>> {
let thold = TholderThold::Weighted(vec![vec![
WeightSpec::WeightedMap(
Rational32::new(1, 3),
vec![
Rational32::new(1, 2),
Rational32::new(1, 2),
Rational32::new(1, 2),
],
),
WeightSpec::Simple(Rational32::new(1, 3)),
WeightSpec::Simple(Rational32::new(1, 2)),
WeightSpec::WeightedMap(
Rational32::new(1, 2),
vec![Rational32::new(1, 1), Rational32::new(1, 1)],
),
]]);
let tholder = Tholder::new(Some(thold), None, None)?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 7);
assert_eq!(tholder.limen(), b"4AAIA1s3k1s2v1s2v1s2c1s3c1s2c1s2k1v1");
let expected_json = r#"[{"1/3":["1/2","1/2","1/2"]},"1/3","1/2",{"1/2":["1","1"]}]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
Ok(())
}
#[test]
fn test_tholder_nested_weighted_multi_clause() -> Result<(), Box<dyn std::error::Error>> {
let json_str = r#"[[{"1/3":["1/2", "1/2", "1/2"]}, "1/2", {"1/2": ["1", "1"]}], ["1/2", {"1/2": ["1", "1"]}]]"#;
let tholder = Tholder::new(None, None, Some(TholderSith::Json(json_str.to_string())))?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 9);
if let TholderThold::Weighted(clauses) = tholder.thold() {
assert_eq!(clauses.len(), 2); assert_eq!(clauses[0].len(), 3); assert_eq!(clauses[1].len(), 2);
if let WeightSpec::WeightedMap(key_weight, nested_weights) = &clauses[0][0] {
assert_eq!(*key_weight, Rational32::new(1, 3));
assert_eq!(nested_weights.len(), 3);
} else {
panic!("Expected WeightedMap for first clause first element");
}
if let WeightSpec::WeightedMap(key_weight, nested_weights) = &clauses[1][1] {
assert_eq!(*key_weight, Rational32::new(1, 2));
assert_eq!(nested_weights.len(), 2);
} else {
panic!("Expected WeightedMap for second clause second element");
}
} else {
panic!("Expected Weighted threshold type");
}
assert_eq!(
tholder.limen(),
b"4AAKA1s3k1s2v1s2v1s2c1s2c1s2k1v1a1s2c1s2k1v1"
);
if let TholderSith::Json(json_str) = tholder.sith() {
let expected_nested = r#"[[{"1/3":["1/2","1/2","1/2"]},"1/2",{"1/2":["1","1"]}],["1/2",{"1/2":["1","1"]}]]"#;
assert_eq!(json_str, expected_nested);
} else {
panic!("Expected Json sith type for multi-clause");
}
let expected_json =
r#"[[{"1/3":["1/2","1/2","1/2"]},"1/2",{"1/2":["1","1"]}],["1/2",{"1/2":["1","1"]}]]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
assert!(tholder.satisfy(&[0, 2, 3, 5, 6, 7])); assert!(tholder.satisfy(&[3, 4, 5, 6, 8])); assert!(tholder.satisfy(&[1, 2, 3, 4, 6, 7])); assert!(tholder.satisfy(&[4, 2, 0, 3, 8, 6])); assert!(tholder.satisfy(&[0, 0, 1, 2, 1, 8, 3, 5, 6, 3]));
assert!(!tholder.satisfy(&[0, 2, 5])); assert!(!tholder.satisfy(&[6, 7, 8]));
Ok(())
}
#[test]
fn test_tholder_nested_weighted_multi_clause_from_limen(
) -> Result<(), Box<dyn std::error::Error>> {
let limen = b"4AAKA1s3k1s2v1s2v1s2c1s2c1s2k1v1a1s2c1s2k1v1".to_vec();
let tholder = Tholder::new(None, Some(limen), None)?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 9);
assert_eq!(
tholder.limen(),
b"4AAKA1s3k1s2v1s2v1s2c1s2c1s2k1v1a1s2c1s2k1v1"
);
let expected_json =
r#"[[{"1/3":["1/2","1/2","1/2"]},"1/2",{"1/2":["1","1"]}],["1/2",{"1/2":["1","1"]}]]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
Ok(())
}
#[test]
fn test_tholder_nested_weighted_multi_clause_from_thold(
) -> Result<(), Box<dyn std::error::Error>> {
let thold = TholderThold::Weighted(vec![
vec![
WeightSpec::WeightedMap(
Rational32::new(1, 3),
vec![
Rational32::new(1, 2),
Rational32::new(1, 2),
Rational32::new(1, 2),
],
),
WeightSpec::Simple(Rational32::new(1, 2)),
WeightSpec::WeightedMap(
Rational32::new(1, 2),
vec![Rational32::new(1, 1), Rational32::new(1, 1)],
),
],
vec![
WeightSpec::Simple(Rational32::new(1, 2)),
WeightSpec::WeightedMap(
Rational32::new(1, 2),
vec![Rational32::new(1, 1), Rational32::new(1, 1)],
),
],
]);
let tholder = Tholder::new(Some(thold), None, None)?;
assert!(tholder.weighted());
assert_eq!(tholder.size(), 9);
assert_eq!(
tholder.limen(),
b"4AAKA1s3k1s2v1s2v1s2c1s2c1s2k1v1a1s2c1s2k1v1"
);
let expected_json =
r#"[[{"1/3":["1/2","1/2","1/2"]},"1/2",{"1/2":["1","1"]}],["1/2",{"1/2":["1","1"]}]]"#;
assert_eq!(tholder.json(), expected_json);
assert_eq!(tholder.num(), None);
Ok(())
}
#[test]
fn test_tholder_nested_weighted_validation_errors() -> Result<(), Box<dyn std::error::Error>> {
let json_str1 = r#"[{"1/3":["1/3", "1/3", "1/4"]}, "1/3", "1/2", {"1/2": ["1", "1"]}]"#;
let result1 = Tholder::new(None, None, Some(TholderSith::Json(json_str1.to_string())));
assert!(result1.is_err());
let json_str2 = r#"[{"1/3":["1/2", "1/2", "1/2"]}, "1/3", "1/2", {"1/2": ["2/3", "1/4"]}]"#;
let result2 = Tholder::new(None, None, Some(TholderSith::Json(json_str2.to_string())));
assert!(result2.is_err());
let json_str3 = r#"[{"1/5":["1/2", "1/2", "1/2"]}, "1/4", "1/5", {"1/5": ["1", "1"]}]"#;
let result3 = Tholder::new(None, None, Some(TholderSith::Json(json_str3.to_string())));
assert!(result3.is_err());
let json_str4 = r#"[[{"1/3":["1/2", "1/2", "1/2"]}, "1/2", {"1/2": ["1", "1"]}], ["1/2", {"1/3": ["1", "1"]}]]"#;
let result4 = Tholder::new(None, None, Some(TholderSith::Json(json_str4.to_string())));
assert!(result4.is_err());
let json_str5 = r#"[[{"1/3":["1/3", "1/4", "1/3"]}, "1/2", {"1/2": ["1", "1"]}], ["1/2", {"1/2": ["1/2", "1/2"]}]]"#;
let result5 = Tholder::new(None, None, Some(TholderSith::Json(json_str5.to_string())));
assert!(result5.is_err());
Ok(())
}
}