use crate::prelude::*;
use std::collections::HashMap;
use std::fmt;
#[derive(Clone)]
pub struct BySymmetricPropStmt {
pub forall_fact: ForallFact,
pub proof: Vec<Stmt>,
pub line_file: LineFile,
}
impl fmt::Display for BySymmetricPropStmt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {}:\n{}{}\n{}",
BY,
SYMMETRIC_PROP,
add_four_spaces_at_beginning(PROVE.to_string(), 1),
COLON,
to_string_and_add_four_spaces_at_beginning_of_each_line(
&self.forall_fact.to_string(),
2
)
)?;
if !self.proof.is_empty() {
write!(
f,
"\n{}",
vec_to_string_add_four_spaces_at_beginning_of_each_line(&self.proof, 1)
)?;
}
Ok(())
}
}
impl BySymmetricPropStmt {
pub fn new(forall_fact: ForallFact, proof: Vec<Stmt>, line_file: LineFile) -> Self {
BySymmetricPropStmt {
forall_fact,
proof,
line_file,
}
}
pub fn symmetric_prop_registration(&self) -> Result<(String, Vec<usize>), String> {
symmetric_prop_shape_from_forall(&self.forall_fact)
}
}
fn symmetric_prop_shape_from_forall(
forall_fact: &ForallFact,
) -> Result<(String, Vec<usize>), String> {
let params = forall_fact
.params_def_with_type
.collect_param_names_with_types();
if params.len() < 2 {
return Err("by symmetric_prop: forall must declare at least two parameters".to_string());
}
for (_, param_type) in params.iter() {
match param_type {
ParamType::Set(_) => {}
_ => {
return Err("by symmetric_prop: each forall parameter type must be set".to_string());
}
}
}
if forall_fact.dom_facts.len() != 1 {
return Err("by symmetric_prop: forall dom must contain exactly one fact".to_string());
}
if forall_fact.then_facts.len() != 1 {
return Err("by symmetric_prop: forall then must contain exactly one fact".to_string());
}
let n = params.len();
let dom_f = normal_atomic_from_dom_fact_sym(&forall_fact.dom_facts[0])?;
let then_f = normal_atomic_from_then_fact_sym(&forall_fact.then_facts[0])?;
let prop_name = dom_f.predicate.to_string();
if then_f.predicate.to_string() != prop_name {
return Err("by symmetric_prop: dom and then must use the same prop".to_string());
}
if dom_f.body.len() != n || then_f.body.len() != n {
return Err(format!(
"by symmetric_prop: dom and then must each have {} arguments",
n
));
}
let dom_names = forall_param_names_in_order_sym(dom_f)?;
let then_names = forall_param_names_in_order_sym(then_f)?;
let mut param_sorted: Vec<String> = params.iter().map(|(name, _)| name.clone()).collect();
param_sorted.sort();
let mut dom_sorted = dom_names.clone();
dom_sorted.sort();
if dom_sorted != param_sorted {
return Err(
"by symmetric_prop: dom fact must use each forall parameter exactly once".to_string(),
);
}
let mut then_sorted = then_names.clone();
then_sorted.sort();
if then_sorted != param_sorted {
return Err(
"by symmetric_prop: then fact must use each forall parameter exactly once".to_string(),
);
}
let mut name_to_dom_ix: HashMap<String, usize> = HashMap::new();
for (i, name) in dom_names.iter().enumerate() {
if name_to_dom_ix.insert(name.clone(), i).is_some() {
return Err("by symmetric_prop: duplicate parameter in dom arguments".to_string());
}
}
let mut gather = Vec::with_capacity(n);
for name in &then_names {
let Some(&i) = name_to_dom_ix.get(name) else {
return Err("by symmetric_prop: then argument is not a forall parameter".to_string());
};
gather.push(i);
}
if gather.iter().enumerate().all(|(k, &g)| g == k) {
return Err("by symmetric_prop: dom and then argument order are identical".to_string());
}
Ok((prop_name, gather))
}
fn forall_param_names_in_order_sym(fact: &NormalAtomicFact) -> Result<Vec<String>, String> {
let mut v = Vec::new();
for obj in fact.body.iter() {
match obj {
Obj::Atom(AtomObj::Forall(p)) => v.push(p.name.clone()),
_ => {
return Err(
"by symmetric_prop: each argument must be a forall parameter".to_string(),
);
}
}
}
Ok(v)
}
fn normal_atomic_from_dom_fact_sym(fact: &Fact) -> Result<&NormalAtomicFact, String> {
match fact {
Fact::AtomicFact(AtomicFact::NormalAtomicFact(f)) => Ok(f),
_ => {
Err("by symmetric_prop: dom fact must be a positive user-defined prop fact".to_string())
}
}
}
fn normal_atomic_from_then_fact_sym(
fact: &ExistOrAndChainAtomicFact,
) -> Result<&NormalAtomicFact, String> {
match fact {
ExistOrAndChainAtomicFact::AtomicFact(AtomicFact::NormalAtomicFact(f)) => Ok(f),
_ => Err(
"by symmetric_prop: then fact must be a positive user-defined prop fact".to_string(),
),
}
}