1use std::{
23 fmt::{Debug, Display},
24 hash::Hash,
25};
26
27pub mod fraction;
28pub mod properties;
29
30pub trait Atomic: Debug + Display + Hash + Clone + Eq + for<'a> From<&'a str> + Ord {
39 fn name(&self) -> &str;
41}
42
43pub trait IsDeclared<T> {
50 fn is_declared(&self, obj: &T) -> bool;
52}
53
54#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
60pub struct Parameter(String);
61impl Parameter {
62 pub fn new(name: impl ToString) -> Self {
64 Parameter(name.to_string())
65 }
66}
67
68impl From<&str> for Parameter {
69 fn from(s: &str) -> Self {
70 Parameter::new(s)
71 }
72}
73
74impl Atomic for Parameter {
75 fn name(&self) -> &str {
76 &self.0
77 }
78}
79
80#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
85pub struct Variable(String);
86impl Variable {
87 pub fn new(name: impl ToString) -> Self {
89 Variable(name.to_string())
90 }
91}
92
93impl From<&str> for Variable {
94 fn from(value: &str) -> Self {
95 Variable::new(value)
96 }
97}
98
99impl Atomic for Variable {
100 fn name(&self) -> &str {
101 &self.0
102 }
103}
104
105#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
109pub struct Location(String);
110
111impl Location {
112 pub fn new(name: impl ToString) -> Self {
114 Location(name.to_string())
115 }
116}
117
118impl From<&str> for Location {
119 fn from(value: &str) -> Self {
120 Location::new(value)
121 }
122}
123
124impl Atomic for Location {
125 fn name(&self) -> &str {
126 &self.0
127 }
128}
129
130#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
159pub enum BooleanExpression<T: Atomic> {
160 ComparisonExpression(
162 Box<IntegerExpression<T>>,
163 ComparisonOp,
164 Box<IntegerExpression<T>>,
165 ),
166 BinaryExpression(
168 Box<BooleanExpression<T>>,
169 BooleanConnective,
170 Box<BooleanExpression<T>>,
171 ),
172 Not(Box<BooleanExpression<T>>),
174 True,
176 False,
178}
179
180#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
197pub enum IntegerExpression<T: Atomic> {
198 Atom(T),
200 Const(u32),
202 Param(Parameter),
204 BinaryExpr(
206 Box<IntegerExpression<T>>,
207 IntegerOp,
208 Box<IntegerExpression<T>>,
209 ),
210 Neg(Box<IntegerExpression<T>>),
212}
213
214#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
216pub enum ComparisonOp {
217 Gt,
219 Geq,
221 Eq,
223 Neq,
225 Leq,
227 Lt,
229}
230
231#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
233pub enum BooleanConnective {
234 And,
236 Or,
238}
239
240#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
242pub enum IntegerOp {
243 Add,
245 Sub,
247 Mul,
249 Div,
251}
252
253impl Display for Parameter {
254 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255 write!(f, "{}", self.0)
256 }
257}
258
259impl Display for Location {
260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 write!(f, "{}", self.0)
262 }
263}
264
265impl Display for Variable {
266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 write!(f, "{}", self.0)
268 }
269}
270
271impl<T: Atomic> Display for BooleanExpression<T> {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 match self {
274 BooleanExpression::ComparisonExpression(lhs, op, rhs) => {
275 write!(f, "{lhs} {op} {rhs}")
276 }
277 BooleanExpression::BinaryExpression(lhs, op, rhs) => {
278 write!(f, "({lhs} {op} {rhs})")
279 }
280 BooleanExpression::True => write!(f, "true"),
281 BooleanExpression::False => write!(f, "false"),
282 BooleanExpression::Not(b) => write!(f, "!{b}"),
283 }
284 }
285}
286
287impl<T: Atomic> Display for IntegerExpression<T> {
288 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
289 match self {
290 IntegerExpression::Atom(a) => write!(f, "{a}"),
291 IntegerExpression::Const(c) => write!(f, "{c}"),
292 IntegerExpression::Param(p) => write!(f, "{p}"),
293 IntegerExpression::BinaryExpr(lhs, op, rhs) => write!(f, "({lhs} {op} {rhs})"),
294 IntegerExpression::Neg(ex) => write!(f, "-{ex}"),
295 }
296 }
297}
298
299impl Display for ComparisonOp {
300 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
301 match self {
302 ComparisonOp::Gt => write!(f, ">"),
303 ComparisonOp::Geq => write!(f, ">="),
304 ComparisonOp::Eq => write!(f, "=="),
305 ComparisonOp::Neq => write!(f, "!="),
306 ComparisonOp::Leq => write!(f, "<="),
307 ComparisonOp::Lt => write!(f, "<"),
308 }
309 }
310}
311
312impl Display for BooleanConnective {
313 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314 match self {
315 BooleanConnective::And => write!(f, "&&"),
316 BooleanConnective::Or => write!(f, "||"),
317 }
318 }
319}
320
321impl Display for IntegerOp {
322 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323 match self {
324 IntegerOp::Add => write!(f, "+"),
325 IntegerOp::Sub => write!(f, "-"),
326 IntegerOp::Mul => write!(f, "*"),
327 IntegerOp::Div => write!(f, "/"),
328 }
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335
336 #[test]
337 fn test_parameter_new() {
338 let param = Parameter::new("n");
339 assert_eq!(param.0, "n");
340 }
341
342 #[test]
343 fn test_variable_new() {
344 let var = Variable::new("x");
345 assert_eq!(var.0, "x");
346 }
347
348 #[test]
349 fn test_location_new() {
350 let loc = Location::new("start");
351 assert_eq!(loc.0, "start");
352 }
353
354 #[test]
355 fn test_constraint_display() {
356 let lhs = IntegerExpression::Atom(Variable::new("x"));
357 let rhs = IntegerExpression::Const(5);
358 let constraint = BooleanExpression::ComparisonExpression(
359 Box::new(lhs),
360 ComparisonOp::Geq,
361 Box::new(rhs),
362 );
363 assert_eq!(constraint.to_string(), "x >= 5");
364 }
365
366 #[test]
367 fn test_integer_expression_display() {
368 let lhs = IntegerExpression::Atom(Variable::new("x"));
369 let rhs = IntegerExpression::Const(5);
370 let expr = IntegerExpression::BinaryExpr(Box::new(lhs), IntegerOp::Add, Box::new(rhs));
371 assert_eq!(expr.to_string(), "(x + 5)");
372 }
373
374 #[test]
375 fn test_comparison_op_display() {
376 assert_eq!(ComparisonOp::Gt.to_string(), ">");
377 assert_eq!(ComparisonOp::Geq.to_string(), ">=");
378 assert_eq!(ComparisonOp::Eq.to_string(), "==");
379 assert_eq!(ComparisonOp::Neq.to_string(), "!=");
380 assert_eq!(ComparisonOp::Leq.to_string(), "<=");
381 assert_eq!(ComparisonOp::Lt.to_string(), "<");
382 }
383
384 #[test]
385 fn test_boolean_connective_display() {
386 assert_eq!(BooleanConnective::And.to_string(), "&&");
387 assert_eq!(BooleanConnective::Or.to_string(), "||");
388 }
389
390 #[test]
391 fn test_arithmetic_op_display() {
392 assert_eq!(IntegerOp::Add.to_string(), "+");
393 assert_eq!(IntegerOp::Sub.to_string(), "-");
394 assert_eq!(IntegerOp::Mul.to_string(), "*");
395 assert_eq!(IntegerOp::Div.to_string(), "/");
396 }
397
398 #[test]
399 fn test_negated_expression_display() {
400 let expr = -IntegerExpression::Atom(Variable::new("x"));
401 assert_eq!(expr.to_string(), "-x");
402 }
403
404 #[test]
405 fn test_binary_boolean_expression_display() {
406 let lhs = BooleanExpression::ComparisonExpression(
407 Box::new(IntegerExpression::Atom(Variable::new("x"))),
408 ComparisonOp::Gt,
409 Box::new(IntegerExpression::Const(0)),
410 );
411 let rhs = BooleanExpression::ComparisonExpression(
412 Box::new(IntegerExpression::Atom(Variable::new("y"))),
413 ComparisonOp::Lt,
414 Box::new(IntegerExpression::Const(10)),
415 );
416 let expr = lhs & rhs;
417 assert_eq!(expr.to_string(), "(x > 0 && y < 10)");
418 }
419
420 #[test]
421 fn test_true_boolean_expression_display() {
422 let expr: BooleanExpression<Location> = BooleanExpression::True;
423 assert_eq!(expr.to_string(), "true");
424 }
425
426 #[test]
427 fn test_false_boolean_expression_display() {
428 let expr: BooleanExpression<Location> = BooleanExpression::False;
429 assert_eq!(expr.to_string(), "false");
430 }
431
432 #[test]
433 fn test_not_boolean_expression_display() {
434 let expr: BooleanExpression<Location> = !BooleanExpression::True;
435 assert_eq!(expr.to_string(), "!true");
436 }
437}