From 66e1659c5b8cc67a80585ba6334c8554f3a55e42 Mon Sep 17 00:00:00 2001 From: Albin Chaboissier Date: Thu, 12 Feb 2026 20:08:13 +0100 Subject: [PATCH] Starting a CLP(Z) engine --- doz/.gitignore | 1 + doz/Cargo.lock | 7 + doz/Cargo.toml | 6 + doz/src/domain.rs | 254 +++++++++++++++++++++++++++++++++ doz/src/domain/simple_range.rs | 153 ++++++++++++++++++++ doz/src/domain/union.rs | 16 +++ doz/src/lib.rs | 1 + doz/src/main.rs | 16 +++ 8 files changed, 454 insertions(+) create mode 100644 doz/.gitignore create mode 100644 doz/Cargo.lock create mode 100644 doz/Cargo.toml create mode 100644 doz/src/domain.rs create mode 100644 doz/src/domain/simple_range.rs create mode 100644 doz/src/domain/union.rs create mode 100644 doz/src/lib.rs create mode 100644 doz/src/main.rs diff --git a/doz/.gitignore b/doz/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/doz/.gitignore @@ -0,0 +1 @@ +/target diff --git a/doz/Cargo.lock b/doz/Cargo.lock new file mode 100644 index 0000000..65f26b0 --- /dev/null +++ b/doz/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "doz" +version = "0.1.0" diff --git a/doz/Cargo.toml b/doz/Cargo.toml new file mode 100644 index 0000000..104d8da --- /dev/null +++ b/doz/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "doz" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/doz/src/domain.rs b/doz/src/domain.rs new file mode 100644 index 0000000..09aefe2 --- /dev/null +++ b/doz/src/domain.rs @@ -0,0 +1,254 @@ +pub mod simple_range; +pub mod union; + +use std::fmt::Display; +use std::ops::Range; +use std::ops::RangeFrom; +use std::ops::RangeTo; + +pub struct Domain +{ + pub ranges: Vec, +} + +/// Represents the set +/// $$\{x \in [\text{start}; \text{end}[ | x \equiv \text{congruent} \text{mod} \text{modulo}}$$ +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct DomainRange +{ + pub start: LowerBound, + pub end: HigherBound, + + pub modulo: u64, + pub congruent: i64, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct SimpleRange +{ + pub start: LowerBound, + pub end: HigherBound, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum LowerBound +{ + Infinity, + Bounded(i64), +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum HigherBound +{ + Infinity, + Bounded(i64), +} + +pub enum UnionResult +{ + Single(T), + Union(T, T), +} + +impl Domain +{ + pub fn empty(&self) -> Domain + { + Domain { ranges: vec![] } + } + + pub fn is_empty(&self) -> bool + { + self.ranges.is_empty() + } +} + +impl DomainRange +{ + pub fn range(from: LowerBound, to: HigherBound) -> DomainRange + { + DomainRange { + start: from, + end: to, + modulo: 1, + congruent: 0, + } + } + + pub fn is_simple(&self) -> Option + { + if self.modulo <= 1 + { + Some(SimpleRange { + start: self.start, + end: self.end, + }) + } + else + { + None + } + } +} + +impl From> for Domain +{ + fn from(value: Range) -> Self + { + Domain { + ranges: vec![value.into()], + } + } +} + +impl From> for Domain +{ + fn from(value: RangeTo) -> Self + { + Domain { + ranges: vec![value.into()], + } + } +} + +impl From> for Domain +{ + fn from(value: RangeFrom) -> Self + { + Domain { + ranges: vec![value.into()], + } + } +} + +impl From> for DomainRange +{ + fn from(value: Range) -> Self + { + DomainRange::range( + LowerBound::Bounded(value.start), + HigherBound::Bounded(value.end), + ) + } +} + +impl From> for DomainRange +{ + fn from(value: RangeTo) -> Self + { + DomainRange::range(LowerBound::Infinity, HigherBound::Bounded(value.end)) + } +} + +impl From> for DomainRange +{ + fn from(value: RangeFrom) -> Self + { + DomainRange::range(LowerBound::Bounded(value.start), HigherBound::Infinity) + } +} + +impl Display for LowerBound +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + match self + { + LowerBound::Infinity => write!(f, "-∞"), + LowerBound::Bounded(x) => write!(f, "{}", x), + } + } +} + +impl Display for HigherBound +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + match self + { + HigherBound::Infinity => write!(f, "+∞"), + HigherBound::Bounded(x) => write!(f, "{}", x), + } + } +} + +impl Display for DomainRange +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + write!(f, "{}..{}", self.start, self.end)?; + if self.modulo > 1 + { + write!(f, " ≡ {} [{}]", self.congruent, self.modulo)?; + } + Ok(()) + } +} + +impl Display for Domain +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + if self.is_empty() + { + write!(f, "Ø") + } + else + { + let len = self.ranges.len(); + for (i, r) in self.ranges.iter().enumerate() + { + write!(f, "{}", r)?; + if i != len - 1 + { + write!(f, " ∪ ")?; + } + } + Ok(()) + } + } +} + +impl PartialOrd for LowerBound +{ + fn partial_cmp(&self, other: &Self) -> Option + { + Some(self.cmp(other)) + } +} + +impl Ord for LowerBound +{ + fn cmp(&self, other: &Self) -> std::cmp::Ordering + { + match (self, other) + { + (LowerBound::Infinity, LowerBound::Infinity) => std::cmp::Ordering::Equal, + (LowerBound::Infinity, LowerBound::Bounded(_)) => std::cmp::Ordering::Less, + (LowerBound::Bounded(_), LowerBound::Infinity) => std::cmp::Ordering::Greater, + (LowerBound::Bounded(a), LowerBound::Bounded(b)) => a.cmp(b), + } + } +} + +impl PartialOrd for HigherBound +{ + fn partial_cmp(&self, other: &Self) -> Option + { + Some(self.cmp(other)) + } +} + +impl Ord for HigherBound +{ + fn cmp(&self, other: &Self) -> std::cmp::Ordering + { + match (self, other) + { + (HigherBound::Infinity, HigherBound::Infinity) => std::cmp::Ordering::Equal, + (HigherBound::Infinity, HigherBound::Bounded(_)) => std::cmp::Ordering::Greater, + (HigherBound::Bounded(_), HigherBound::Infinity) => std::cmp::Ordering::Less, + (HigherBound::Bounded(a), HigherBound::Bounded(b)) => a.cmp(b), + } + } +} diff --git a/doz/src/domain/simple_range.rs b/doz/src/domain/simple_range.rs new file mode 100644 index 0000000..e4190ce --- /dev/null +++ b/doz/src/domain/simple_range.rs @@ -0,0 +1,153 @@ +use std::ops::Range; +use std::ops::RangeFrom; +use std::ops::RangeFull; +use std::ops::RangeTo; + +use crate::domain::HigherBound; +use crate::domain::LowerBound; +use crate::domain::SimpleRange; +use crate::domain::UnionResult; + +impl SimpleRange +{ + pub fn union(&self, range: &SimpleRange) -> UnionResult + { + let (a, b) = (self.min_by_start(range), self.max_by_start(range)); + match (a.end, b.start) + { + (HigherBound::Infinity, LowerBound::Infinity) => + { + UnionResult::Single(SimpleRange::from(..)) + } + (HigherBound::Infinity, LowerBound::Bounded(_)) => UnionResult::Single(SimpleRange { + start: a.start, + end: HigherBound::Infinity, + }), + (HigherBound::Bounded(_), LowerBound::Infinity) => UnionResult::Single(SimpleRange { + start: LowerBound::Infinity, + end: b.end, + }), + (HigherBound::Bounded(c), LowerBound::Bounded(d)) if c < d => UnionResult::Union(a, b), + (HigherBound::Bounded(_), LowerBound::Bounded(_)) => UnionResult::Single(SimpleRange { + start: a.start, + end: b.end, + }), + } + } + + pub fn intersection(&self, range: &SimpleRange) -> Option + { + let (a, b) = (self.min_by_start(range), self.max_by_start(range)); + match (a.end, b.start) + { + (HigherBound::Infinity, LowerBound::Infinity) => Some(SimpleRange::from(..)), + (HigherBound::Infinity, LowerBound::Bounded(a)) => Some(SimpleRange::from(a..)), + (HigherBound::Bounded(a), LowerBound::Infinity) => Some(SimpleRange::from(..a)), + (HigherBound::Bounded(a), LowerBound::Bounded(b)) if a <= b => None, + (HigherBound::Bounded(a), LowerBound::Bounded(b)) => Some(SimpleRange::from(a..b)), + } + } + + pub fn without(&self, range: &SimpleRange) -> SimpleRange + { + match self.intersection(range) + { + Some(intersecting_part) => match self.union(&intersecting_part) + { + UnionResult::Single(res) => res, + UnionResult::Union(_, _) => unreachable!(), + }, + None => *self, + } + } + + pub fn symetrical_difference(&self, range: &SimpleRange) -> Option> + { + match self.intersection(range) + { + Some(intersecting_part) => + { + Some((self.without(&intersecting_part)).union(&range.without(&intersecting_part))) + } + None => Some(self.union(range)), + } + } + + pub fn min_by_start(&self, range: &SimpleRange) -> SimpleRange + { + if self.start < range.start + { + *self + } + else + { + *range + } + } + + pub fn max_by_start(&self, range: &SimpleRange) -> SimpleRange + { + if self.start < range.start + { + *range + } + else + { + *self + } + } + + pub fn min_by_end(&self, range: &SimpleRange) -> SimpleRange + { + if self.end < range.end { *self } else { *range } + } + + pub fn max_by_end(&self, range: &SimpleRange) -> SimpleRange + { + if self.end < range.end { *range } else { *self } + } +} + +impl From> for SimpleRange +{ + fn from(value: Range) -> Self + { + SimpleRange { + start: LowerBound::Bounded(value.start), + end: HigherBound::Bounded(value.end), + } + } +} + +impl From> for SimpleRange +{ + fn from(value: RangeTo) -> Self + { + SimpleRange { + start: LowerBound::Infinity, + end: HigherBound::Bounded(value.end), + } + } +} + +impl From> for SimpleRange +{ + fn from(value: RangeFrom) -> Self + { + SimpleRange { + start: LowerBound::Bounded(value.start), + end: HigherBound::Infinity, + } + } +} + +impl From for SimpleRange +{ + fn from(_: RangeFull) -> Self + { + SimpleRange { + start: LowerBound::Infinity, + end: HigherBound::Infinity, + } + } +} diff --git a/doz/src/domain/union.rs b/doz/src/domain/union.rs new file mode 100644 index 0000000..a8db2f3 --- /dev/null +++ b/doz/src/domain/union.rs @@ -0,0 +1,16 @@ +// Module for computing intersection of domains + +use crate::domain::Domain; +use crate::domain::DomainRange; + +impl DomainRange +{ + pub fn union(&self, domain: &DomainRange) -> Domain + { + if let Some(simple_a) = self.is_simple() + && let Some(simple_b) = self.is_simple() + { + simple_a.union(&simple_b) + } + } +} diff --git a/doz/src/lib.rs b/doz/src/lib.rs new file mode 100644 index 0000000..d7abca1 --- /dev/null +++ b/doz/src/lib.rs @@ -0,0 +1 @@ +pub mod domain; diff --git a/doz/src/main.rs b/doz/src/main.rs new file mode 100644 index 0000000..487e411 --- /dev/null +++ b/doz/src/main.rs @@ -0,0 +1,16 @@ +use doz::domain::Domain; +use doz::domain::DomainRange; + +fn main() +{ + let even = Domain { + ranges: vec![DomainRange { + start: doz::domain::LowerBound::Infinity, + end: doz::domain::HigherBound::Infinity, + modulo: 2, + congruent: 0, + }], + }; + + println!("{}", even); +}