1use std::{
23 fmt::{Debug, Display},
24 hash::Hash,
25};
26
27pub mod fraction;
28pub mod properties;
29
30pub trait And {
35 fn and(self, other: Self) -> Self;
37}
38
39#[macro_export]
44macro_rules! impl_bitand {
45 ($($ty:ty),+ $(,)?) => {
46 $(
47 impl std::ops::BitAnd for $ty {
48 type Output = Self;
49
50 fn bitand(self, rhs: Self) -> Self::Output {
51 self.and(rhs)
52 }
53 }
54 )+
55 };
56}
57
58pub trait Or {
63 fn or(self, other: Self) -> Self;
65}
66
67#[macro_export]
72macro_rules! impl_bitor {
73 ($($ty:ty),+ $(,)?) => {
74 $(
75 impl std::ops::BitOr for $ty {
76 type Output = Self;
77
78 fn bitor(self, rhs: Self) -> Self::Output {
79 self.or(rhs)
80 }
81 }
82 )+
83 };
84}
85
86pub trait Atomic: Debug + Display + Hash + Clone + Eq + for<'a> From<&'a str> + Ord {
95 fn name(&self) -> &str;
97}
98
99pub trait IsDeclared<T> {
106 fn is_declared(&self, obj: &T) -> bool;
108}
109
110#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
116pub struct Parameter(String);
117impl Parameter {
118 pub fn new(name: impl ToString) -> Self {
120 Parameter(name.to_string())
121 }
122}
123
124impl From<&str> for Parameter {
125 fn from(s: &str) -> Self {
126 Parameter::new(s)
127 }
128}
129
130impl Atomic for Parameter {
131 fn name(&self) -> &str {
132 &self.0
133 }
134}
135
136#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
141pub struct Variable(String);
142impl Variable {
143 pub fn new(name: impl ToString) -> Self {
145 Variable(name.to_string())
146 }
147}
148
149impl From<&str> for Variable {
150 fn from(value: &str) -> Self {
151 Variable::new(value)
152 }
153}
154
155impl Atomic for Variable {
156 fn name(&self) -> &str {
157 &self.0
158 }
159}
160
161#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
165pub struct Location(String);
166
167impl Location {
168 pub fn new(name: impl ToString) -> Self {
170 Location(name.to_string())
171 }
172}
173
174impl From<&str> for Location {
175 fn from(value: &str) -> Self {
176 Location::new(value)
177 }
178}
179
180impl Atomic for Location {
181 fn name(&self) -> &str {
182 &self.0
183 }
184}
185
186#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
215pub enum BooleanExpression<T: Atomic> {
216 ComparisonExpression(
218 Box<IntegerExpression<T>>,
219 ComparisonOp,
220 Box<IntegerExpression<T>>,
221 ),
222 BinaryExpression(
224 Box<BooleanExpression<T>>,
225 BooleanConnective,
226 Box<BooleanExpression<T>>,
227 ),
228 Not(Box<BooleanExpression<T>>),
230 True,
232 False,
234}
235
236#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
253pub enum IntegerExpression<T: Atomic> {
254 Atom(T),
256 Const(u32),
258 Param(Parameter),
260 BinaryExpr(
262 Box<IntegerExpression<T>>,
263 IntegerOp,
264 Box<IntegerExpression<T>>,
265 ),
266 Neg(Box<IntegerExpression<T>>),
268}
269
270#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
272pub enum ComparisonOp {
273 Gt,
275 Geq,
277 Eq,
279 Neq,
281 Leq,
283 Lt,
285}
286
287#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
289pub enum BooleanConnective {
290 And,
292 Or,
294}
295
296#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
298pub enum IntegerOp {
299 Add,
301 Sub,
303 Mul,
305 Div,
307}
308
309impl Display for Parameter {
310 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311 write!(f, "{}", self.0)
312 }
313}
314
315impl Display for Location {
316 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
317 write!(f, "{}", self.0)
318 }
319}
320
321impl Display for Variable {
322 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323 write!(f, "{}", self.0)
324 }
325}
326
327impl<T: Atomic> Display for BooleanExpression<T> {
328 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
329 match self {
330 BooleanExpression::ComparisonExpression(lhs, op, rhs) => {
331 write!(f, "{lhs} {op} {rhs}")
332 }
333 BooleanExpression::BinaryExpression(lhs, op, rhs) => {
334 write!(f, "({lhs} {op} {rhs})")
335 }
336 BooleanExpression::True => write!(f, "true"),
337 BooleanExpression::False => write!(f, "false"),
338 BooleanExpression::Not(b) => write!(f, "!{b}"),
339 }
340 }
341}
342
343impl<T: Atomic> Display for IntegerExpression<T> {
344 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
345 match self {
346 IntegerExpression::Atom(a) => write!(f, "{a}"),
347 IntegerExpression::Const(c) => write!(f, "{c}"),
348 IntegerExpression::Param(p) => write!(f, "{p}"),
349 IntegerExpression::BinaryExpr(lhs, op, rhs) => write!(f, "({lhs} {op} {rhs})"),
350 IntegerExpression::Neg(ex) => write!(f, "-{ex}"),
351 }
352 }
353}
354
355impl Display for ComparisonOp {
356 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
357 match self {
358 ComparisonOp::Gt => write!(f, ">"),
359 ComparisonOp::Geq => write!(f, ">="),
360 ComparisonOp::Eq => write!(f, "=="),
361 ComparisonOp::Neq => write!(f, "!="),
362 ComparisonOp::Leq => write!(f, "<="),
363 ComparisonOp::Lt => write!(f, "<"),
364 }
365 }
366}
367
368impl Display for BooleanConnective {
369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
370 match self {
371 BooleanConnective::And => write!(f, "&&"),
372 BooleanConnective::Or => write!(f, "||"),
373 }
374 }
375}
376
377impl Display for IntegerOp {
378 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
379 match self {
380 IntegerOp::Add => write!(f, "+"),
381 IntegerOp::Sub => write!(f, "-"),
382 IntegerOp::Mul => write!(f, "*"),
383 IntegerOp::Div => write!(f, "/"),
384 }
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[test]
393 fn test_parameter_new() {
394 let param = Parameter::new("n");
395 assert_eq!(param.0, "n");
396 }
397
398 #[test]
399 fn test_variable_new() {
400 let var = Variable::new("x");
401 assert_eq!(var.0, "x");
402 }
403
404 #[test]
405 fn test_location_new() {
406 let loc = Location::new("start");
407 assert_eq!(loc.0, "start");
408 }
409
410 #[test]
411 fn test_constraint_display() {
412 let lhs = IntegerExpression::Atom(Variable::new("x"));
413 let rhs = IntegerExpression::Const(5);
414 let constraint = BooleanExpression::ComparisonExpression(
415 Box::new(lhs),
416 ComparisonOp::Geq,
417 Box::new(rhs),
418 );
419 assert_eq!(constraint.to_string(), "x >= 5");
420 }
421
422 #[test]
423 fn test_integer_expression_display() {
424 let lhs = IntegerExpression::Atom(Variable::new("x"));
425 let rhs = IntegerExpression::Const(5);
426 let expr = IntegerExpression::BinaryExpr(Box::new(lhs), IntegerOp::Add, Box::new(rhs));
427 assert_eq!(expr.to_string(), "(x + 5)");
428 }
429
430 #[test]
431 fn test_comparison_op_display() {
432 assert_eq!(ComparisonOp::Gt.to_string(), ">");
433 assert_eq!(ComparisonOp::Geq.to_string(), ">=");
434 assert_eq!(ComparisonOp::Eq.to_string(), "==");
435 assert_eq!(ComparisonOp::Neq.to_string(), "!=");
436 assert_eq!(ComparisonOp::Leq.to_string(), "<=");
437 assert_eq!(ComparisonOp::Lt.to_string(), "<");
438 }
439
440 #[test]
441 fn test_boolean_connective_display() {
442 assert_eq!(BooleanConnective::And.to_string(), "&&");
443 assert_eq!(BooleanConnective::Or.to_string(), "||");
444 }
445
446 #[test]
447 fn test_arithmetic_op_display() {
448 assert_eq!(IntegerOp::Add.to_string(), "+");
449 assert_eq!(IntegerOp::Sub.to_string(), "-");
450 assert_eq!(IntegerOp::Mul.to_string(), "*");
451 assert_eq!(IntegerOp::Div.to_string(), "/");
452 }
453
454 #[test]
455 fn test_negated_expression_display() {
456 let expr = -IntegerExpression::Atom(Variable::new("x"));
457 assert_eq!(expr.to_string(), "-x");
458 }
459
460 #[test]
461 fn test_binary_boolean_expression_display() {
462 let lhs = BooleanExpression::ComparisonExpression(
463 Box::new(IntegerExpression::Atom(Variable::new("x"))),
464 ComparisonOp::Gt,
465 Box::new(IntegerExpression::Const(0)),
466 );
467 let rhs = BooleanExpression::ComparisonExpression(
468 Box::new(IntegerExpression::Atom(Variable::new("y"))),
469 ComparisonOp::Lt,
470 Box::new(IntegerExpression::Const(10)),
471 );
472 let expr = lhs & rhs;
473 assert_eq!(expr.to_string(), "(x > 0 && y < 10)");
474 }
475
476 #[test]
477 fn test_true_boolean_expression_display() {
478 let expr: BooleanExpression<Location> = BooleanExpression::True;
479 assert_eq!(expr.to_string(), "true");
480 }
481
482 #[test]
483 fn test_false_boolean_expression_display() {
484 let expr: BooleanExpression<Location> = BooleanExpression::False;
485 assert_eq!(expr.to_string(), "false");
486 }
487
488 #[test]
489 fn test_not_boolean_expression_display() {
490 let expr: BooleanExpression<Location> = !BooleanExpression::True;
491 assert_eq!(expr.to_string(), "!true");
492 }
493}