use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum PredicateProperty {
Symmetric,
Transitive,
Reflexive,
Irreflexive,
Antisymmetric,
Functional,
InverseFunctional,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ValueRange {
pub min: Option<f64>,
pub max: Option<f64>,
pub inclusive_min: bool,
pub inclusive_max: bool,
}
impl ValueRange {
pub fn new() -> Self {
Self {
min: None,
max: None,
inclusive_min: true,
inclusive_max: true,
}
}
pub fn with_min(mut self, min: f64, inclusive: bool) -> Self {
self.min = Some(min);
self.inclusive_min = inclusive;
self
}
pub fn with_max(mut self, max: f64, inclusive: bool) -> Self {
self.max = Some(max);
self.inclusive_max = inclusive;
self
}
pub fn contains(&self, value: f64) -> bool {
if let Some(min) = self.min {
if self.inclusive_min {
if value < min {
return false;
}
} else if value <= min {
return false;
}
}
if let Some(max) = self.max {
if self.inclusive_max {
if value > max {
return false;
}
} else if value >= max {
return false;
}
}
true
}
}
impl Default for ValueRange {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct FunctionalDependency {
pub determinants: Vec<usize>,
pub dependents: Vec<usize>,
}
impl FunctionalDependency {
pub fn new(determinants: Vec<usize>, dependents: Vec<usize>) -> Self {
Self {
determinants,
dependents,
}
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct PredicateConstraints {
pub properties: Vec<PredicateProperty>,
pub value_ranges: Vec<Option<ValueRange>>,
pub functional_dependencies: Vec<FunctionalDependency>,
}
impl PredicateConstraints {
pub fn new() -> Self {
Self::default()
}
pub fn with_property(mut self, property: PredicateProperty) -> Self {
self.properties.push(property);
self
}
pub fn with_value_range(mut self, arg_index: usize, range: ValueRange) -> Self {
while self.value_ranges.len() <= arg_index {
self.value_ranges.push(None);
}
self.value_ranges[arg_index] = Some(range);
self
}
pub fn with_functional_dependency(mut self, dependency: FunctionalDependency) -> Self {
self.functional_dependencies.push(dependency);
self
}
pub fn has_property(&self, property: &PredicateProperty) -> bool {
self.properties.contains(property)
}
pub fn is_symmetric(&self) -> bool {
self.has_property(&PredicateProperty::Symmetric)
}
pub fn is_transitive(&self) -> bool {
self.has_property(&PredicateProperty::Transitive)
}
pub fn is_reflexive(&self) -> bool {
self.has_property(&PredicateProperty::Reflexive)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_range() {
let range = ValueRange::new().with_min(0.0, true).with_max(1.0, true);
assert!(range.contains(0.0));
assert!(range.contains(0.5));
assert!(range.contains(1.0));
assert!(!range.contains(-0.1));
assert!(!range.contains(1.1));
}
#[test]
fn test_value_range_exclusive() {
let range = ValueRange::new().with_min(0.0, false).with_max(1.0, false);
assert!(!range.contains(0.0));
assert!(range.contains(0.5));
assert!(!range.contains(1.0));
}
#[test]
fn test_predicate_properties() {
let constraints = PredicateConstraints::new()
.with_property(PredicateProperty::Symmetric)
.with_property(PredicateProperty::Transitive);
assert!(constraints.is_symmetric());
assert!(constraints.is_transitive());
assert!(!constraints.is_reflexive());
}
#[test]
fn test_functional_dependency() {
let fd = FunctionalDependency::new(vec![0], vec![1, 2]);
assert_eq!(fd.determinants, vec![0]);
assert_eq!(fd.dependents, vec![1, 2]);
}
}