use super::builder::{Model, Value, VarId};
#[allow(unused_imports)]
use crate::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompletionStrategy {
Default,
Witness,
Minimal,
}
#[derive(Debug, Clone)]
pub struct CompletionConfig {
pub strategy: CompletionStrategy,
pub default_int: i64,
pub default_bool: bool,
}
impl Default for CompletionConfig {
fn default() -> Self {
Self {
strategy: CompletionStrategy::Default,
default_int: 0,
default_bool: false,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct CompletionStats {
pub vars_completed: u64,
pub defaults_used: u64,
pub witnesses_used: u64,
}
#[derive(Debug)]
pub struct ModelCompleter {
config: CompletionConfig,
witnesses: FxHashMap<VarId, Value>,
stats: CompletionStats,
}
impl ModelCompleter {
pub fn new(config: CompletionConfig) -> Self {
Self {
config,
witnesses: FxHashMap::default(),
stats: CompletionStats::default(),
}
}
pub fn default_config() -> Self {
Self::new(CompletionConfig::default())
}
pub fn add_witness(&mut self, var: VarId, value: Value) {
self.witnesses.insert(var, value);
}
pub fn complete(&mut self, partial_model: &Model) -> Model {
let mut completed = partial_model.clone();
let missing_vars = self.find_missing_vars(&completed);
for var in missing_vars {
let value = self.complete_variable(var);
completed.assign_theory(var, value);
self.stats.vars_completed += 1;
}
completed
}
fn find_missing_vars(&self, model: &Model) -> Vec<VarId> {
let mut missing = Vec::new();
for &var in self.witnesses.keys() {
if model.get_theory(var).is_none() {
missing.push(var);
}
}
missing
}
fn complete_variable(&mut self, var: VarId) -> Value {
match self.config.strategy {
CompletionStrategy::Witness => {
if let Some(witness) = self.witnesses.get(&var) {
self.stats.witnesses_used += 1;
witness.clone()
} else {
self.stats.defaults_used += 1;
self.default_value()
}
}
CompletionStrategy::Default => {
self.stats.defaults_used += 1;
self.default_value()
}
CompletionStrategy::Minimal => {
if let Some(witness) = self.witnesses.get(&var) {
self.stats.witnesses_used += 1;
witness.clone()
} else {
self.stats.defaults_used += 1;
self.minimal_value()
}
}
}
}
fn default_value(&self) -> Value {
Value::Int(self.config.default_int)
}
fn minimal_value(&self) -> Value {
Value::Int(0)
}
pub fn stats(&self) -> &CompletionStats {
&self.stats
}
pub fn reset(&mut self) {
self.witnesses.clear();
self.stats = CompletionStats::default();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_completer_creation() {
let completer = ModelCompleter::default_config();
assert_eq!(completer.stats().vars_completed, 0);
}
#[test]
fn test_add_witness() {
let mut completer = ModelCompleter::default_config();
completer.add_witness(0, Value::Int(42));
assert!(completer.witnesses.contains_key(&0));
}
#[test]
fn test_complete_with_witnesses() {
let config = CompletionConfig {
strategy: CompletionStrategy::Witness,
..Default::default()
};
let mut completer = ModelCompleter::new(config);
completer.add_witness(0, Value::Int(10));
completer.add_witness(1, Value::Bool(true));
let partial = Model::new();
let _completed = completer.complete(&partial);
assert!(completer.stats().witnesses_used > 0);
}
#[test]
fn test_default_completion() {
let config = CompletionConfig {
strategy: CompletionStrategy::Default,
default_int: 100,
..Default::default()
};
let completer = ModelCompleter::new(config);
let default_val = completer.default_value();
assert_eq!(default_val, Value::Int(100));
}
}