cspsolver/
constraint_macros.rs

1//! Mathematical constraint posting macros
2//!
3//! This module provides the `post!` and `postall!` macros for creating constraints
4//! using natural mathematical notation.
5//!
6//! # Basic Usage
7//!
8//! The primary way to use these macros is for mathematical constraints:
9//!
10//! ```rust
11//! use cspsolver::prelude::*;
12//! 
13//! let mut m = Model::default();
14//! let x = m.int(1, 10);
15//! let y = m.int(1, 10);
16//! let five = m.int(5, 5);
17//! 
18//! // Mathematical constraint syntax:
19//! post!(m, x < y);
20//! post!(m, y > five);
21//! post!(m, x + y <= five);
22//! 
23//! // Multiple constraints at once:
24//! postall!(m, x < y, y != five);
25//! ```
26//!
27//! For boolean logic operations, use the model's boolean methods instead:
28//! - `model.bool_and(&[a, b])` for AND
29//! - `model.bool_or(&[a, b])` for OR  
30//! - `model.bool_not(a)` for NOT
31//!
32//! The function-style syntax is preferred because it's cleaner and doesn't require
33//! parentheses due to Rust macro parsing limitations.
34
35use crate::vars::{VarId, Val};
36use crate::model::Model;
37use crate::math_syntax::{MathExpr, TypedConstant};
38
39/// Represents a constraint reference that can be used later
40#[derive(Debug, Clone, Copy)]
41pub struct ConstraintRef {
42    /// Internal constraint ID (for future constraint management)
43    id: usize,
44}
45
46impl ConstraintRef {
47    /// Create a new constraint reference
48    pub fn new(id: usize) -> Self {
49        Self { id }
50    }
51    
52    /// Get the constraint ID
53    pub fn id(&self) -> usize {
54        self.id
55    }
56}
57
58
59#[macro_export]
60macro_rules! post {
61    // Handle simple variable comparisons: x < y, x <= y, etc.
62    ($model:expr, $left:ident < $right:ident) => {{
63        $model.props.less_than($left, $right);
64        $crate::constraint_macros::ConstraintRef::new(0)
65    }};
66    
67    ($model:expr, $left:ident <= $right:ident) => {{
68        $model.props.less_than_or_equals($left, $right);
69        $crate::constraint_macros::ConstraintRef::new(0)
70    }};
71    
72    ($model:expr, $left:ident > $right:ident) => {{
73        $model.props.greater_than($left, $right);
74        $crate::constraint_macros::ConstraintRef::new(0)
75    }};
76    
77    ($model:expr, $left:ident >= $right:ident) => {{
78        $model.props.greater_than_or_equals($left, $right);
79        $crate::constraint_macros::ConstraintRef::new(0)
80    }};
81    
82    ($model:expr, $left:ident == $right:ident) => {{
83        $model.props.equals($left, $right);
84        $crate::constraint_macros::ConstraintRef::new(0)
85    }};
86    
87    ($model:expr, $left:ident != $right:ident) => {{
88        $model.props.not_equals($left, $right);
89        $crate::constraint_macros::ConstraintRef::new(0)
90    }};
91    
92    // Handle variable vs bare literal: x < 5, y >= 3.14
93    ($model:expr, $left:ident < $right:literal) => {{
94        $model.props.less_than($left, $crate::vars::Val::from($right));
95        $crate::constraint_macros::ConstraintRef::new(0)
96    }};
97    
98    ($model:expr, $left:ident <= $right:literal) => {{
99        $model.props.less_than_or_equals($left, $crate::vars::Val::from($right));
100        $crate::constraint_macros::ConstraintRef::new(0)
101    }};
102    
103    ($model:expr, $left:ident > $right:literal) => {{
104        $model.props.greater_than($left, $crate::vars::Val::from($right));
105        $crate::constraint_macros::ConstraintRef::new(0)
106    }};
107    
108    ($model:expr, $left:ident >= $right:literal) => {{
109        $model.props.greater_than_or_equals($left, $crate::vars::Val::from($right));
110        $crate::constraint_macros::ConstraintRef::new(0)
111    }};
112    
113    ($model:expr, $left:ident == $right:literal) => {{
114        $model.props.equals($left, $crate::vars::Val::from($right));
115        $crate::constraint_macros::ConstraintRef::new(0)
116    }};
117    
118    ($model:expr, $left:ident != $right:literal) => {{
119        $model.props.not_equals($left, $crate::vars::Val::from($right));
120        $crate::constraint_macros::ConstraintRef::new(0)
121    }};
122    
123    // Handle variable vs expression in parentheses: x < (y + 1)
124    ($model:expr, $left:ident < ($right:expr)) => {{
125        $model.props.less_than($left, $crate::vars::Val::from($right));
126        $crate::constraint_macros::ConstraintRef::new(0)
127    }};
128    
129    ($model:expr, $left:ident <= ($right:expr)) => {{
130        $model.props.less_than_or_equals($left, $crate::vars::Val::from($right));
131        $crate::constraint_macros::ConstraintRef::new(0)
132    }};
133    
134    ($model:expr, $left:ident > ($right:expr)) => {{
135        $model.props.greater_than($left, $crate::vars::Val::from($right));
136        $crate::constraint_macros::ConstraintRef::new(0)
137    }};
138    
139    ($model:expr, $left:ident >= ($right:expr)) => {{
140        $model.props.greater_than_or_equals($left, $crate::vars::Val::from($right));
141        $crate::constraint_macros::ConstraintRef::new(0)
142    }};
143    
144    ($model:expr, $left:ident == ($right:expr)) => {{
145        $model.props.equals($left, $crate::vars::Val::from($right));
146        $crate::constraint_macros::ConstraintRef::new(0)
147    }};
148    
149    ($model:expr, $left:ident != ($right:expr)) => {{
150        $model.props.not_equals($left, $crate::vars::Val::from($right));
151        $crate::constraint_macros::ConstraintRef::new(0)
152    }};
153    
154    // Handle variable vs constant: x < int(5), y >= float(3.14)
155    ($model:expr, $left:ident < int($right:expr)) => {{
156        $model.props.less_than($left, $crate::prelude::int($right));
157        $crate::constraint_macros::ConstraintRef::new(0)
158    }};
159    
160    ($model:expr, $left:ident <= int($right:expr)) => {{
161        $model.props.less_than_or_equals($left, $crate::prelude::int($right));
162        $crate::constraint_macros::ConstraintRef::new(0)
163    }};
164    
165    ($model:expr, $left:ident > int($right:expr)) => {{
166        $model.props.greater_than($left, $crate::prelude::int($right));
167        $crate::constraint_macros::ConstraintRef::new(0)
168    }};
169    
170    ($model:expr, $left:ident >= int($right:expr)) => {{
171        $model.props.greater_than_or_equals($left, $crate::prelude::int($right));
172        $crate::constraint_macros::ConstraintRef::new(0)
173    }};
174    
175    ($model:expr, $left:ident == int($right:expr)) => {{
176        $model.props.equals($left, $crate::prelude::int($right));
177        $crate::constraint_macros::ConstraintRef::new(0)
178    }};
179    
180    ($model:expr, $left:ident != int($right:expr)) => {{
181        $model.props.not_equals($left, $crate::prelude::int($right));
182        $crate::constraint_macros::ConstraintRef::new(0)
183    }};
184    
185    // Handle float constants
186    ($model:expr, $left:ident < float($right:expr)) => {{
187        $model.props.less_than($left, $crate::prelude::float($right));
188        $crate::constraint_macros::ConstraintRef::new(0)
189    }};
190    
191    ($model:expr, $left:ident <= float($right:expr)) => {{
192        $model.props.less_than_or_equals($left, $crate::prelude::float($right));
193        $crate::constraint_macros::ConstraintRef::new(0)
194    }};
195    
196    ($model:expr, $left:ident > float($right:expr)) => {{
197        $model.props.greater_than($left, $crate::prelude::float($right));
198        $crate::constraint_macros::ConstraintRef::new(0)
199    }};
200    
201    ($model:expr, $left:ident >= float($right:expr)) => {{
202        $model.props.greater_than_or_equals($left, $crate::prelude::float($right));
203        $crate::constraint_macros::ConstraintRef::new(0)
204    }};
205    
206    ($model:expr, $left:ident == float($right:expr)) => {{
207        $model.props.equals($left, $crate::prelude::float($right));
208        $crate::constraint_macros::ConstraintRef::new(0)
209    }};
210    
211    ($model:expr, $left:ident != float($right:expr)) => {{
212        $model.props.not_equals($left, $crate::prelude::float($right));
213        $crate::constraint_macros::ConstraintRef::new(0)
214    }};
215    
216    // Handle mathematical functions: abs(x), min([x,y]), max([x,y])
217    // Absolute value: abs(x) <op> <expr>
218    ($model:expr, abs($var:ident) < $target:ident) => {{
219        let _abs_var = $model.abs($var);
220        $model.props.less_than(_abs_var, $target);
221        $crate::constraint_macros::ConstraintRef::new(0)
222    }};
223    
224    ($model:expr, abs($var:ident) <= $target:ident) => {{
225        let _abs_var = $model.abs($var);
226        $model.props.less_than_or_equals(_abs_var, $target);
227        $crate::constraint_macros::ConstraintRef::new(0)
228    }};
229    
230    ($model:expr, abs($var:ident) > $target:ident) => {{
231        let _abs_var = $model.abs($var);
232        $model.props.greater_than(_abs_var, $target);
233        $crate::constraint_macros::ConstraintRef::new(0)
234    }};
235    
236    ($model:expr, abs($var:ident) >= $target:ident) => {{
237        let _abs_var = $model.abs($var);
238        $model.props.greater_than_or_equals(_abs_var, $target);
239        $crate::constraint_macros::ConstraintRef::new(0)
240    }};
241    
242    ($model:expr, abs($var:ident) == $target:ident) => {{
243        let _abs_var = $model.abs($var);
244        $model.props.equals(_abs_var, $target);
245        $crate::constraint_macros::ConstraintRef::new(0)
246    }};
247    
248    ($model:expr, abs($var:ident) != $target:ident) => {{
249        let _abs_var = $model.abs($var);
250        $model.props.not_equals(_abs_var, $target);
251        $crate::constraint_macros::ConstraintRef::new(0)
252    }};
253    
254    // Absolute value with constants: abs(x) >= int(1)
255    ($model:expr, abs($var:ident) < int($target:expr)) => {{
256        let _abs_var = $model.abs($var);
257        $model.props.less_than(_abs_var, $crate::prelude::int($target));
258        $crate::constraint_macros::ConstraintRef::new(0)
259    }};
260    
261    ($model:expr, abs($var:ident) <= int($target:expr)) => {{
262        let _abs_var = $model.abs($var);
263        $model.props.less_than_or_equals(_abs_var, $crate::prelude::int($target));
264        $crate::constraint_macros::ConstraintRef::new(0)
265    }};
266    
267    ($model:expr, abs($var:ident) > int($target:expr)) => {{
268        let _abs_var = $model.abs($var);
269        $model.props.greater_than(_abs_var, $crate::prelude::int($target));
270        $crate::constraint_macros::ConstraintRef::new(0)
271    }};
272    
273    ($model:expr, abs($var:ident) >= int($target:expr)) => {{
274        let _abs_var = $model.abs($var);
275        $model.props.greater_than_or_equals(_abs_var, $crate::prelude::int($target));
276        $crate::constraint_macros::ConstraintRef::new(0)
277    }};
278    
279    ($model:expr, abs($var:ident) == int($target:expr)) => {{
280        let _abs_var = $model.abs($var);
281        $model.props.equals(_abs_var, $crate::prelude::int($target));
282        $crate::constraint_macros::ConstraintRef::new(0)
283    }};
284    
285    ($model:expr, abs($var:ident) != int($target:expr)) => {{
286        let _abs_var = $model.abs($var);
287        $model.props.not_equals(_abs_var, $crate::prelude::int($target));
288        $crate::constraint_macros::ConstraintRef::new(0)
289    }};
290    
291    // Absolute value with float constants: abs(x) >= float(1.5)
292    ($model:expr, abs($var:ident) < float($target:expr)) => {{
293        let _abs_var = $model.abs($var);
294        $model.props.less_than(_abs_var, $crate::prelude::float($target));
295        $crate::constraint_macros::ConstraintRef::new(0)
296    }};
297    
298    ($model:expr, abs($var:ident) <= float($target:expr)) => {{
299        let _abs_var = $model.abs($var);
300        $model.props.less_than_or_equals(_abs_var, $crate::prelude::float($target));
301        $crate::constraint_macros::ConstraintRef::new(0)
302    }};
303    
304    ($model:expr, abs($var:ident) > float($target:expr)) => {{
305        let _abs_var = $model.abs($var);
306        $model.props.greater_than(_abs_var, $crate::prelude::float($target));
307        $crate::constraint_macros::ConstraintRef::new(0)
308    }};
309    
310    ($model:expr, abs($var:ident) >= float($target:expr)) => {{
311        let _abs_var = $model.abs($var);
312        $model.props.greater_than_or_equals(_abs_var, $crate::prelude::float($target));
313        $crate::constraint_macros::ConstraintRef::new(0)
314    }};
315    
316    ($model:expr, abs($var:ident) == float($target:expr)) => {{
317        let _abs_var = $model.abs($var);
318        $model.props.equals(_abs_var, $crate::prelude::float($target));
319        $crate::constraint_macros::ConstraintRef::new(0)
320    }};
321    
322    ($model:expr, abs($var:ident) != float($target:expr)) => {{
323        let _abs_var = $model.abs($var);
324        $model.props.not_equals(_abs_var, $crate::prelude::float($target));
325        $crate::constraint_macros::ConstraintRef::new(0)
326    }};
327    
328    // Min function: min([x, y]) <op> <expr>
329    ($model:expr, min([$($vars:ident),+ $(,)?]) < $target:ident) => {{
330        let _min_var = $model.min(&[$($vars),+]);
331        $model.props.less_than(_min_var, $target);
332        $crate::constraint_macros::ConstraintRef::new(0)
333    }};
334    
335    ($model:expr, min([$($vars:ident),+ $(,)?]) <= $target:ident) => {{
336        let _min_var = $model.min(&[$($vars),+]);
337        $model.props.less_than_or_equals(_min_var, $target);
338        $crate::constraint_macros::ConstraintRef::new(0)
339    }};
340    
341    ($model:expr, min([$($vars:ident),+ $(,)?]) > $target:ident) => {{
342        let _min_var = $model.min(&[$($vars),+]);
343        $model.props.greater_than(_min_var, $target);
344        $crate::constraint_macros::ConstraintRef::new(0)
345    }};
346    
347    ($model:expr, min([$($vars:ident),+ $(,)?]) >= $target:ident) => {{
348        let _min_var = $model.min(&[$($vars),+]);
349        $model.props.greater_than_or_equals(_min_var, $target);
350        $crate::constraint_macros::ConstraintRef::new(0)
351    }};
352    
353    ($model:expr, min([$($vars:ident),+ $(,)?]) == $target:ident) => {{
354        let _min_var = $model.min(&[$($vars),+]);
355        $model.props.equals(_min_var, $target);
356        $crate::constraint_macros::ConstraintRef::new(0)
357    }};
358    
359    ($model:expr, min([$($vars:ident),+ $(,)?]) != $target:ident) => {{
360        let _min_var = $model.min(&[$($vars),+]);
361        $model.props.not_equals(_min_var, $target);
362        $crate::constraint_macros::ConstraintRef::new(0)
363    }};
364    
365    // Min function with constants: min([x, y]) <= int(5)
366    ($model:expr, min([$($vars:ident),+ $(,)?]) < int($target:expr)) => {{
367        let _min_var = $model.min(&[$($vars),+]);
368        $model.props.less_than(_min_var, $crate::prelude::int($target));
369        $crate::constraint_macros::ConstraintRef::new(0)
370    }};
371    
372    ($model:expr, min([$($vars:ident),+ $(,)?]) <= int($target:expr)) => {{
373        let _min_var = $model.min(&[$($vars),+]);
374        $model.props.less_than_or_equals(_min_var, $crate::prelude::int($target));
375        $crate::constraint_macros::ConstraintRef::new(0)
376    }};
377    
378    ($model:expr, min([$($vars:ident),+ $(,)?]) > int($target:expr)) => {{
379        let _min_var = $model.min(&[$($vars),+]);
380        $model.props.greater_than(_min_var, $crate::prelude::int($target));
381        $crate::constraint_macros::ConstraintRef::new(0)
382    }};
383    
384    ($model:expr, min([$($vars:ident),+ $(,)?]) >= int($target:expr)) => {{
385        let _min_var = $model.min(&[$($vars),+]);
386        $model.props.greater_than_or_equals(_min_var, $crate::prelude::int($target));
387        $crate::constraint_macros::ConstraintRef::new(0)
388    }};
389    
390    ($model:expr, min([$($vars:ident),+ $(,)?]) == int($target:expr)) => {{
391        let _min_var = $model.min(&[$($vars),+]);
392        $model.props.equals(_min_var, $crate::prelude::int($target));
393        $crate::constraint_macros::ConstraintRef::new(0)
394    }};
395    
396    ($model:expr, min([$($vars:ident),+ $(,)?]) != int($target:expr)) => {{
397        let _min_var = $model.min(&[$($vars),+]);
398        $model.props.not_equals(_min_var, $crate::prelude::int($target));
399        $crate::constraint_macros::ConstraintRef::new(0)
400    }};
401    
402    // Min function with float constants: min([x, y]) <= float(5.0)
403    ($model:expr, min([$($vars:ident),+ $(,)?]) < float($target:expr)) => {{
404        let _min_var = $model.min(&[$($vars),+]);
405        $model.props.less_than(_min_var, $crate::prelude::float($target));
406        $crate::constraint_macros::ConstraintRef::new(0)
407    }};
408    
409    ($model:expr, min([$($vars:ident),+ $(,)?]) <= float($target:expr)) => {{
410        let _min_var = $model.min(&[$($vars),+]);
411        $model.props.less_than_or_equals(_min_var, $crate::prelude::float($target));
412        $crate::constraint_macros::ConstraintRef::new(0)
413    }};
414    
415    ($model:expr, min([$($vars:ident),+ $(,)?]) > float($target:expr)) => {{
416        let _min_var = $model.min(&[$($vars),+]);
417        $model.props.greater_than(_min_var, $crate::prelude::float($target));
418        $crate::constraint_macros::ConstraintRef::new(0)
419    }};
420    
421    ($model:expr, min([$($vars:ident),+ $(,)?]) >= float($target:expr)) => {{
422        let _min_var = $model.min(&[$($vars),+]);
423        $model.props.greater_than_or_equals(_min_var, $crate::prelude::float($target));
424        $crate::constraint_macros::ConstraintRef::new(0)
425    }};
426    
427    ($model:expr, min([$($vars:ident),+ $(,)?]) == float($target:expr)) => {{
428        let _min_var = $model.min(&[$($vars),+]);
429        $model.props.equals(_min_var, $crate::prelude::float($target));
430        $crate::constraint_macros::ConstraintRef::new(0)
431    }};
432    
433    ($model:expr, min([$($vars:ident),+ $(,)?]) != float($target:expr)) => {{
434        let _min_var = $model.min(&[$($vars),+]);
435        $model.props.not_equals(_min_var, $crate::prelude::float($target));
436        $crate::constraint_macros::ConstraintRef::new(0)
437    }};
438    
439    // Max function: max([x, y]) <op> <expr>
440    ($model:expr, max([$($vars:ident),+ $(,)?]) < $target:ident) => {{
441        let _max_var = $model.max(&[$($vars),+]);
442        $model.props.less_than(_max_var, $target);
443        $crate::constraint_macros::ConstraintRef::new(0)
444    }};
445    
446    ($model:expr, max([$($vars:ident),+ $(,)?]) <= $target:ident) => {{
447        let _max_var = $model.max(&[$($vars),+]);
448        $model.props.less_than_or_equals(_max_var, $target);
449        $crate::constraint_macros::ConstraintRef::new(0)
450    }};
451    
452    ($model:expr, max([$($vars:ident),+ $(,)?]) > $target:ident) => {{
453        let _max_var = $model.max(&[$($vars),+]);
454        $model.props.greater_than(_max_var, $target);
455        $crate::constraint_macros::ConstraintRef::new(0)
456    }};
457    
458    ($model:expr, max([$($vars:ident),+ $(,)?]) >= $target:ident) => {{
459        let _max_var = $model.max(&[$($vars),+]);
460        $model.props.greater_than_or_equals(_max_var, $target);
461        $crate::constraint_macros::ConstraintRef::new(0)
462    }};
463    
464    ($model:expr, max([$($vars:ident),+ $(,)?]) == $target:ident) => {{
465        let _max_var = $model.max(&[$($vars),+]);
466        $model.props.equals(_max_var, $target);
467        $crate::constraint_macros::ConstraintRef::new(0)
468    }};
469    
470    ($model:expr, max([$($vars:ident),+ $(,)?]) != $target:ident) => {{
471        let _max_var = $model.max(&[$($vars),+]);
472        $model.props.not_equals(_max_var, $target);
473        $crate::constraint_macros::ConstraintRef::new(0)
474    }};
475    
476    // Max function with constants: max([x, y]) >= int(10)
477    ($model:expr, max([$($vars:ident),+ $(,)?]) < int($target:expr)) => {{
478        let _max_var = $model.max(&[$($vars),+]);
479        $model.props.less_than(_max_var, $crate::prelude::int($target));
480        $crate::constraint_macros::ConstraintRef::new(0)
481    }};
482    
483    ($model:expr, max([$($vars:ident),+ $(,)?]) <= int($target:expr)) => {{
484        let _max_var = $model.max(&[$($vars),+]);
485        $model.props.less_than_or_equals(_max_var, $crate::prelude::int($target));
486        $crate::constraint_macros::ConstraintRef::new(0)
487    }};
488    
489    ($model:expr, max([$($vars:ident),+ $(,)?]) > int($target:expr)) => {{
490        let _max_var = $model.max(&[$($vars),+]);
491        $model.props.greater_than(_max_var, $crate::prelude::int($target));
492        $crate::constraint_macros::ConstraintRef::new(0)
493    }};
494    
495    ($model:expr, max([$($vars:ident),+ $(,)?]) >= int($target:expr)) => {{
496        let _max_var = $model.max(&[$($vars),+]);
497        $model.props.greater_than_or_equals(_max_var, $crate::prelude::int($target));
498        $crate::constraint_macros::ConstraintRef::new(0)
499    }};
500    
501    ($model:expr, max([$($vars:ident),+ $(,)?]) == int($target:expr)) => {{
502        let _max_var = $model.max(&[$($vars),+]);
503        $model.props.equals(_max_var, $crate::prelude::int($target));
504        $crate::constraint_macros::ConstraintRef::new(0)
505    }};
506    
507    ($model:expr, max([$($vars:ident),+ $(,)?]) != int($target:expr)) => {{
508        let _max_var = $model.max(&[$($vars),+]);
509        $model.props.not_equals(_max_var, $crate::prelude::int($target));
510        $crate::constraint_macros::ConstraintRef::new(0)
511    }};
512    
513    // Max function with float constants: max([x, y]) >= float(10.0)
514    ($model:expr, max([$($vars:ident),+ $(,)?]) < float($target:expr)) => {{
515        let _max_var = $model.max(&[$($vars),+]);
516        $model.props.less_than(_max_var, $crate::prelude::float($target));
517        $crate::constraint_macros::ConstraintRef::new(0)
518    }};
519    
520    ($model:expr, max([$($vars:ident),+ $(,)?]) <= float($target:expr)) => {{
521        let _max_var = $model.max(&[$($vars),+]);
522        $model.props.less_than_or_equals(_max_var, $crate::prelude::float($target));
523        $crate::constraint_macros::ConstraintRef::new(0)
524    }};
525    
526    ($model:expr, max([$($vars:ident),+ $(,)?]) > float($target:expr)) => {{
527        let _max_var = $model.max(&[$($vars),+]);
528        $model.props.greater_than(_max_var, $crate::prelude::float($target));
529        $crate::constraint_macros::ConstraintRef::new(0)
530    }};
531    
532    ($model:expr, max([$($vars:ident),+ $(,)?]) >= float($target:expr)) => {{
533        let _max_var = $model.max(&[$($vars),+]);
534        $model.props.greater_than_or_equals(_max_var, $crate::prelude::float($target));
535        $crate::constraint_macros::ConstraintRef::new(0)
536    }};
537    
538    ($model:expr, max([$($vars:ident),+ $(,)?]) == float($target:expr)) => {{
539        let _max_var = $model.max(&[$($vars),+]);
540        $model.props.equals(_max_var, $crate::prelude::float($target));
541        $crate::constraint_macros::ConstraintRef::new(0)
542    }};
543    
544    ($model:expr, max([$($vars:ident),+ $(,)?]) != float($target:expr)) => {{
545        let _max_var = $model.max(&[$($vars),+]);
546        $model.props.not_equals(_max_var, $crate::prelude::float($target));
547        $crate::constraint_macros::ConstraintRef::new(0)
548    }};
549    
550    // Min function with array expressions: min(array) <op> <expr>
551    ($model:expr, min($array:expr) < $target:ident) => {{
552        let _min_var = $model.min(&$array);
553        $model.props.less_than(_min_var, $target);
554        $crate::constraint_macros::ConstraintRef::new(0)
555    }};
556    
557    ($model:expr, min($array:expr) <= $target:ident) => {{
558        let _min_var = $model.min(&$array);
559        $model.props.less_than_or_equals(_min_var, $target);
560        $crate::constraint_macros::ConstraintRef::new(0)
561    }};
562    
563    ($model:expr, min($array:expr) > $target:ident) => {{
564        let _min_var = $model.min(&$array);
565        $model.props.greater_than(_min_var, $target);
566        $crate::constraint_macros::ConstraintRef::new(0)
567    }};
568    
569    ($model:expr, min($array:expr) >= $target:ident) => {{
570        let _min_var = $model.min(&$array);
571        $model.props.greater_than_or_equals(_min_var, $target);
572        $crate::constraint_macros::ConstraintRef::new(0)
573    }};
574    
575    ($model:expr, min($array:expr) == $target:ident) => {{
576        let _min_var = $model.min(&$array);
577        $model.props.equals(_min_var, $target);
578        $crate::constraint_macros::ConstraintRef::new(0)
579    }};
580    
581    ($model:expr, min($array:expr) != $target:ident) => {{
582        let _min_var = $model.min(&$array);
583        $model.props.not_equals(_min_var, $target);
584        $crate::constraint_macros::ConstraintRef::new(0)
585    }};
586    
587    // Min function with array expressions and constants: min(array) <= int(5)
588    ($model:expr, min($array:expr) < int($target:expr)) => {{
589        let _min_var = $model.min(&$array);
590        $model.props.less_than(_min_var, $crate::prelude::int($target));
591        $crate::constraint_macros::ConstraintRef::new(0)
592    }};
593    
594    ($model:expr, min($array:expr) <= int($target:expr)) => {{
595        let _min_var = $model.min(&$array);
596        $model.props.less_than_or_equals(_min_var, $crate::prelude::int($target));
597        $crate::constraint_macros::ConstraintRef::new(0)
598    }};
599    
600    ($model:expr, min($array:expr) > int($target:expr)) => {{
601        let _min_var = $model.min(&$array);
602        $model.props.greater_than(_min_var, $crate::prelude::int($target));
603        $crate::constraint_macros::ConstraintRef::new(0)
604    }};
605    
606    ($model:expr, min($array:expr) >= int($target:expr)) => {{
607        let _min_var = $model.min(&$array);
608        $model.props.greater_than_or_equals(_min_var, $crate::prelude::int($target));
609        $crate::constraint_macros::ConstraintRef::new(0)
610    }};
611    
612    ($model:expr, min($array:expr) == int($target:expr)) => {{
613        let _min_var = $model.min(&$array);
614        $model.props.equals(_min_var, $crate::prelude::int($target));
615        $crate::constraint_macros::ConstraintRef::new(0)
616    }};
617    
618    ($model:expr, min($array:expr) != int($target:expr)) => {{
619        let _min_var = $model.min(&$array);
620        $model.props.not_equals(_min_var, $crate::prelude::int($target));
621        $crate::constraint_macros::ConstraintRef::new(0)
622    }};
623    
624    // Max function with array expressions: max(array) <op> <expr>
625    ($model:expr, max($array:expr) < $target:ident) => {{
626        let _max_var = $model.max(&$array);
627        $model.props.less_than(_max_var, $target);
628        $crate::constraint_macros::ConstraintRef::new(0)
629    }};
630    
631    ($model:expr, max($array:expr) <= $target:ident) => {{
632        let _max_var = $model.max(&$array);
633        $model.props.less_than_or_equals(_max_var, $target);
634        $crate::constraint_macros::ConstraintRef::new(0)
635    }};
636    
637    ($model:expr, max($array:expr) > $target:ident) => {{
638        let _max_var = $model.max(&$array);
639        $model.props.greater_than(_max_var, $target);
640        $crate::constraint_macros::ConstraintRef::new(0)
641    }};
642    
643    ($model:expr, max($array:expr) >= $target:ident) => {{
644        let _max_var = $model.max(&$array);
645        $model.props.greater_than_or_equals(_max_var, $target);
646        $crate::constraint_macros::ConstraintRef::new(0)
647    }};
648    
649    ($model:expr, max($array:expr) == $target:ident) => {{
650        let _max_var = $model.max(&$array);
651        $model.props.equals(_max_var, $target);
652        $crate::constraint_macros::ConstraintRef::new(0)
653    }};
654    
655    ($model:expr, max($array:expr) != $target:ident) => {{
656        let _max_var = $model.max(&$array);
657        $model.props.not_equals(_max_var, $target);
658        $crate::constraint_macros::ConstraintRef::new(0)
659    }};
660    
661    // Max function with array expressions and constants: max(array) >= int(10)
662    ($model:expr, max($array:expr) < int($target:expr)) => {{
663        let _max_var = $model.max(&$array);
664        $model.props.less_than(_max_var, $crate::prelude::int($target));
665        $crate::constraint_macros::ConstraintRef::new(0)
666    }};
667    
668    ($model:expr, max($array:expr) <= int($target:expr)) => {{
669        let _max_var = $model.max(&$array);
670        $model.props.less_than_or_equals(_max_var, $crate::prelude::int($target));
671        $crate::constraint_macros::ConstraintRef::new(0)
672    }};
673    
674    ($model:expr, max($array:expr) > int($target:expr)) => {{
675        let _max_var = $model.max(&$array);
676        $model.props.greater_than(_max_var, $crate::prelude::int($target));
677        $crate::constraint_macros::ConstraintRef::new(0)
678    }};
679    
680    ($model:expr, max($array:expr) >= int($target:expr)) => {{
681        let _max_var = $model.max(&$array);
682        $model.props.greater_than_or_equals(_max_var, $crate::prelude::int($target));
683        $crate::constraint_macros::ConstraintRef::new(0)
684    }};
685    
686    ($model:expr, max($array:expr) == int($target:expr)) => {{
687        let _max_var = $model.max(&$array);
688        $model.props.equals(_max_var, $crate::prelude::int($target));
689        $crate::constraint_macros::ConstraintRef::new(0)
690    }};
691    
692    ($model:expr, max($array:expr) != int($target:expr)) => {{
693        let _max_var = $model.max(&$array);
694        $model.props.not_equals(_max_var, $crate::prelude::int($target));
695        $crate::constraint_macros::ConstraintRef::new(0)
696    }};
697    
698    // Sum function: sum([x, y, z]) <op> <expr>
699    ($model:expr, sum([$($vars:ident),+ $(,)?]) < $target:ident) => {{
700        let _sum_var = $model.sum(&[$($vars),+]);
701        $model.props.less_than(_sum_var, $target);
702        $crate::constraint_macros::ConstraintRef::new(0)
703    }};
704    
705    ($model:expr, sum([$($vars:ident),+ $(,)?]) <= $target:ident) => {{
706        let _sum_var = $model.sum(&[$($vars),+]);
707        $model.props.less_than_or_equals(_sum_var, $target);
708        $crate::constraint_macros::ConstraintRef::new(0)
709    }};
710    
711    ($model:expr, sum([$($vars:ident),+ $(,)?]) > $target:ident) => {{
712        let _sum_var = $model.sum(&[$($vars),+]);
713        $model.props.greater_than(_sum_var, $target);
714        $crate::constraint_macros::ConstraintRef::new(0)
715    }};
716    
717    ($model:expr, sum([$($vars:ident),+ $(,)?]) >= $target:ident) => {{
718        let _sum_var = $model.sum(&[$($vars),+]);
719        $model.props.greater_than_or_equals(_sum_var, $target);
720        $crate::constraint_macros::ConstraintRef::new(0)
721    }};
722    
723    ($model:expr, sum([$($vars:ident),+ $(,)?]) == $target:ident) => {{
724        let _sum_var = $model.sum(&[$($vars),+]);
725        $model.props.equals(_sum_var, $target);
726        $crate::constraint_macros::ConstraintRef::new(0)
727    }};
728    
729    ($model:expr, sum([$($vars:ident),+ $(,)?]) != $target:ident) => {{
730        let _sum_var = $model.sum(&[$($vars),+]);
731        $model.props.not_equals(_sum_var, $target);
732        $crate::constraint_macros::ConstraintRef::new(0)
733    }};
734    
735    // Sum function with constants: sum([x, y, z]) <= int(10)
736    ($model:expr, sum([$($vars:ident),+ $(,)?]) < int($target:expr)) => {{
737        let _sum_var = $model.sum(&[$($vars),+]);
738        $model.props.less_than(_sum_var, $crate::prelude::int($target));
739        $crate::constraint_macros::ConstraintRef::new(0)
740    }};
741    
742    ($model:expr, sum([$($vars:ident),+ $(,)?]) <= int($target:expr)) => {{
743        let _sum_var = $model.sum(&[$($vars),+]);
744        $model.props.less_than_or_equals(_sum_var, $crate::prelude::int($target));
745        $crate::constraint_macros::ConstraintRef::new(0)
746    }};
747    
748    ($model:expr, sum([$($vars:ident),+ $(,)?]) > int($target:expr)) => {{
749        let _sum_var = $model.sum(&[$($vars),+]);
750        $model.props.greater_than(_sum_var, $crate::prelude::int($target));
751        $crate::constraint_macros::ConstraintRef::new(0)
752    }};
753    
754    ($model:expr, sum([$($vars:ident),+ $(,)?]) >= int($target:expr)) => {{
755        let _sum_var = $model.sum(&[$($vars),+]);
756        $model.props.greater_than_or_equals(_sum_var, $crate::prelude::int($target));
757        $crate::constraint_macros::ConstraintRef::new(0)
758    }};
759    
760    ($model:expr, sum([$($vars:ident),+ $(,)?]) == int($target:expr)) => {{
761        let _sum_var = $model.sum(&[$($vars),+]);
762        $model.props.equals(_sum_var, $crate::prelude::int($target));
763        $crate::constraint_macros::ConstraintRef::new(0)
764    }};
765    
766    ($model:expr, sum([$($vars:ident),+ $(,)?]) != int($target:expr)) => {{
767        let _sum_var = $model.sum(&[$($vars),+]);
768        $model.props.not_equals(_sum_var, $crate::prelude::int($target));
769        $crate::constraint_macros::ConstraintRef::new(0)
770    }};
771    
772    // Sum function with array expressions: sum(array) <op> <expr>
773    ($model:expr, sum($array:expr) < $target:ident) => {{
774        let _sum_var = $model.sum(&$array);
775        $model.props.less_than(_sum_var, $target);
776        $crate::constraint_macros::ConstraintRef::new(0)
777    }};
778    
779    ($model:expr, sum($array:expr) <= $target:ident) => {{
780        let _sum_var = $model.sum(&$array);
781        $model.props.less_than_or_equals(_sum_var, $target);
782        $crate::constraint_macros::ConstraintRef::new(0)
783    }};
784    
785    ($model:expr, sum($array:expr) > $target:ident) => {{
786        let _sum_var = $model.sum(&$array);
787        $model.props.greater_than(_sum_var, $target);
788        $crate::constraint_macros::ConstraintRef::new(0)
789    }};
790    
791    ($model:expr, sum($array:expr) >= $target:ident) => {{
792        let _sum_var = $model.sum(&$array);
793        $model.props.greater_than_or_equals(_sum_var, $target);
794        $crate::constraint_macros::ConstraintRef::new(0)
795    }};
796    
797    ($model:expr, sum($array:expr) == $target:ident) => {{
798        let _sum_var = $model.sum(&$array);
799        $model.props.equals(_sum_var, $target);
800        $crate::constraint_macros::ConstraintRef::new(0)
801    }};
802    
803    ($model:expr, sum($array:expr) != $target:ident) => {{
804        let _sum_var = $model.sum(&$array);
805        $model.props.not_equals(_sum_var, $target);
806        $crate::constraint_macros::ConstraintRef::new(0)
807    }};
808    
809    // Sum function with array expressions and constants: sum(array) <= int(10)
810    ($model:expr, sum($array:expr) < int($target:expr)) => {{
811        let _sum_var = $model.sum(&$array);
812        $model.props.less_than(_sum_var, $crate::prelude::int($target));
813        $crate::constraint_macros::ConstraintRef::new(0)
814    }};
815    
816    ($model:expr, sum($array:expr) <= int($target:expr)) => {{
817        let _sum_var = $model.sum(&$array);
818        $model.props.less_than_or_equals(_sum_var, $crate::prelude::int($target));
819        $crate::constraint_macros::ConstraintRef::new(0)
820    }};
821    
822    ($model:expr, sum($array:expr) > int($target:expr)) => {{
823        let _sum_var = $model.sum(&$array);
824        $model.props.greater_than(_sum_var, $crate::prelude::int($target));
825        $crate::constraint_macros::ConstraintRef::new(0)
826    }};
827    
828    ($model:expr, sum($array:expr) >= int($target:expr)) => {{
829        let _sum_var = $model.sum(&$array);
830        $model.props.greater_than_or_equals(_sum_var, $crate::prelude::int($target));
831        $crate::constraint_macros::ConstraintRef::new(0)
832    }};
833    
834    ($model:expr, sum($array:expr) == int($target:expr)) => {{
835        let _sum_var = $model.sum(&$array);
836        $model.props.equals(_sum_var, $crate::prelude::int($target));
837        $crate::constraint_macros::ConstraintRef::new(0)
838    }};
839    
840    ($model:expr, sum($array:expr) != int($target:expr)) => {{
841        let _sum_var = $model.sum(&$array);
842        $model.props.not_equals(_sum_var, $crate::prelude::int($target));
843        $crate::constraint_macros::ConstraintRef::new(0)
844    }};
845    
846    // Logical operators (traditional style)
847    ($model:expr, and($c1:expr, $c2:expr)) => {{
848        let _and_result = $model.bool_and(&[$c1, $c2]);
849        $crate::constraint_macros::ConstraintRef::new(0)
850    }};
851    
852    ($model:expr, or($c1:expr, $c2:expr)) => {{
853        let _or_result = $model.bool_or(&[$c1, $c2]);
854        $crate::constraint_macros::ConstraintRef::new(0)
855    }};
856    
857    ($model:expr, not($var:ident)) => {{
858        let _not_result = $model.bool_not($var);
859        $crate::constraint_macros::ConstraintRef::new(0)
860    }};
861
862    // Handle logical operators: & (AND), | (OR)
863    // NOTE: Parentheses are REQUIRED due to Rust macro parsing rules
864    // The `&` and `|` tokens cannot follow `expr` fragments directly
865    // So we use ($left:expr) & ($right:expr) instead of $left:expr & $right:expr
866    ($model:expr, ($left:expr) & ($right:expr)) => {{
867        // AND operation - both constraints must be true
868        // Post both constraints separately
869        let _left_ref = $left;
870        let _right_ref = $right;
871        // Return the second constraint's reference (arbitrary choice since both must hold)
872        _right_ref
873    }};
874    
875    ($model:expr, ($left:expr) | ($right:expr)) => {{
876        // OR operation - at least one constraint must be true
877        // This would require disjunctive constraint support
878        let _left_ref = $left;
879        let _right_ref = $right;
880        $crate::constraint_macros::ConstraintRef::new(0)
881    }};
882    
883    // Handle function-style logical operators (preferred syntax)
884    ($model:expr, and($left:expr, $right:expr)) => {{
885        // AND operation - both constraints must be true
886        // Post both constraints separately
887        let _left_ref = $left;
888        let _right_ref = $right;
889        // Return the second constraint's reference (arbitrary choice since both must hold)
890        _right_ref
891    }};
892    
893    ($model:expr, or($left:expr, $right:expr)) => {{
894        // OR operation - at least one constraint must be true
895        // This would require disjunctive constraint support
896        // For now, this is a placeholder - true OR support needs special implementation
897        let _left_ref = $left;
898        let _right_ref = $right;
899        $crate::constraint_macros::ConstraintRef::new(0)
900    }};
901    
902    ($model:expr, not($constraint:expr)) => {{
903        // NOT operation - negation of a constraint
904        // This would require constraint negation support
905        // For now, this is a placeholder - true NOT support needs special implementation
906        let _constraint_ref = $constraint;
907        $crate::constraint_macros::ConstraintRef::new(0)
908    }};
909    
910    // Global constraints: alldiff([x, y, z])
911    ($model:expr, alldiff([$($vars:ident),+ $(,)?])) => {{
912        $model.props.all_different(vec![$($vars),+]);
913        $crate::constraint_macros::ConstraintRef::new(0)
914    }};
915    
916    // Global constraints: alldiff with array expressions
917    ($model:expr, alldiff($array:expr)) => {{
918        $model.props.all_different($array.to_vec());
919        $crate::constraint_macros::ConstraintRef::new(0)
920    }};
921    
922    // Enhanced modulo operations: x % y == int(0), x % y != int(0)
923    
924    // Modulo with literal divisor and variable remainder: x % 5 == y
925    ($model:expr, $left:ident % $divisor:literal == $remainder:ident) => {{
926        let _mod_var = $model.modulo($left, $crate::prelude::int($divisor));
927        $model.props.equals(_mod_var, $remainder);
928        $crate::constraint_macros::ConstraintRef::new(0)
929    }};
930    
931    ($model:expr, $left:ident % $divisor:literal != $remainder:ident) => {{
932        let _mod_var = $model.modulo($left, $crate::prelude::int($divisor));
933        $model.props.not_equals(_mod_var, $remainder);
934        $crate::constraint_macros::ConstraintRef::new(0)
935    }};
936    
937    // Modulo with variables and all comparison operators: x % y <op> z
938    ($model:expr, $left:ident % $divisor:ident < $target:ident) => {{
939        let _mod_var = $model.modulo($left, $divisor);
940        $model.props.less_than(_mod_var, $target);
941        $crate::constraint_macros::ConstraintRef::new(0)
942    }};
943    
944    ($model:expr, $left:ident % $divisor:ident <= $target:ident) => {{
945        let _mod_var = $model.modulo($left, $divisor);
946        $model.props.less_than_or_equals(_mod_var, $target);
947        $crate::constraint_macros::ConstraintRef::new(0)
948    }};
949    
950    ($model:expr, $left:ident % $divisor:ident > $target:ident) => {{
951        let _mod_var = $model.modulo($left, $divisor);
952        $model.props.greater_than(_mod_var, $target);
953        $crate::constraint_macros::ConstraintRef::new(0)
954    }};
955    
956    ($model:expr, $left:ident % $divisor:ident >= $target:ident) => {{
957        let _mod_var = $model.modulo($left, $divisor);
958        $model.props.greater_than_or_equals(_mod_var, $target);
959        $crate::constraint_macros::ConstraintRef::new(0)
960    }};
961    
962    ($model:expr, $left:ident % $divisor:ident == $target:ident) => {{
963        let _mod_var = $model.modulo($left, $divisor);
964        $model.props.equals(_mod_var, $target);
965        $crate::constraint_macros::ConstraintRef::new(0)
966    }};
967    
968    ($model:expr, $left:ident % $divisor:ident != $target:ident) => {{
969        let _mod_var = $model.modulo($left, $divisor);
970        $model.props.not_equals(_mod_var, $target);
971        $crate::constraint_macros::ConstraintRef::new(0)
972    }};
973    
974    // Modulo with int() constants on divisor: x % int(5) <op> int(0) 
975    ($model:expr, $left:ident % int($divisor:expr) < int($target:expr)) => {{
976        let _mod_var = $model.modulo($left, $crate::prelude::int($divisor));
977        $model.props.less_than(_mod_var, $crate::prelude::int($target));
978        $crate::constraint_macros::ConstraintRef::new(0)
979    }};
980    
981    ($model:expr, $left:ident % int($divisor:expr) <= int($target:expr)) => {{
982        let _mod_var = $model.modulo($left, $crate::prelude::int($divisor));
983        $model.props.less_than_or_equals(_mod_var, $crate::prelude::int($target));
984        $crate::constraint_macros::ConstraintRef::new(0)
985    }};
986    
987    ($model:expr, $left:ident % int($divisor:expr) > int($target:expr)) => {{
988        let _mod_var = $model.modulo($left, $crate::prelude::int($divisor));
989        $model.props.greater_than(_mod_var, $crate::prelude::int($target));
990        $crate::constraint_macros::ConstraintRef::new(0)
991    }};
992    
993    ($model:expr, $left:ident % int($divisor:expr) >= int($target:expr)) => {{
994        let _mod_var = $model.modulo($left, $crate::prelude::int($divisor));
995        $model.props.greater_than_or_equals(_mod_var, $crate::prelude::int($target));
996        $crate::constraint_macros::ConstraintRef::new(0)
997    }};
998    
999    ($model:expr, $left:ident % int($divisor:expr) == int($target:expr)) => {{
1000        let _mod_var = $model.modulo($left, $crate::prelude::int($divisor));
1001        $model.props.equals(_mod_var, $crate::prelude::int($target));
1002        $crate::constraint_macros::ConstraintRef::new(0)
1003    }};
1004    
1005    ($model:expr, $left:ident % int($divisor:expr) != int($target:expr)) => {{
1006        let _mod_var = $model.modulo($left, $crate::prelude::int($divisor));
1007        $model.props.not_equals(_mod_var, $crate::prelude::int($target));
1008        $crate::constraint_macros::ConstraintRef::new(0)
1009    }};
1010    
1011    // Handle arithmetic operations: x + y < z, x - y >= int(0), etc.
1012    // Addition: x + y <op> <expr>
1013    ($model:expr, $left:ident + $right:ident < $target:ident) => {{
1014        let _sum_var = $model.add($left, $right);
1015        $model.props.less_than(_sum_var, $target);
1016        $crate::constraint_macros::ConstraintRef::new(0)
1017    }};
1018    
1019    ($model:expr, $left:ident + $right:ident <= $target:ident) => {{
1020        let _sum_var = $model.add($left, $right);
1021        $model.props.less_than_or_equals(_sum_var, $target);
1022        $crate::constraint_macros::ConstraintRef::new(0)
1023    }};
1024    
1025    ($model:expr, $left:ident + $right:ident > $target:ident) => {{
1026        let _sum_var = $model.add($left, $right);
1027        $model.props.greater_than(_sum_var, $target);
1028        $crate::constraint_macros::ConstraintRef::new(0)
1029    }};
1030    
1031    ($model:expr, $left:ident + $right:ident >= $target:ident) => {{
1032        let _sum_var = $model.add($left, $right);
1033        $model.props.greater_than_or_equals(_sum_var, $target);
1034        $crate::constraint_macros::ConstraintRef::new(0)
1035    }};
1036    
1037    ($model:expr, $left:ident + $right:ident == $target:ident) => {{
1038        let _sum_var = $model.add($left, $right);
1039        $model.props.equals(_sum_var, $target);
1040        $crate::constraint_macros::ConstraintRef::new(0)
1041    }};
1042    
1043    ($model:expr, $left:ident + $right:ident != $target:ident) => {{
1044        let _sum_var = $model.add($left, $right);
1045        $model.props.not_equals(_sum_var, $target);
1046        $crate::constraint_macros::ConstraintRef::new(0)
1047    }};
1048    
1049    // Addition with constants: x + y < int(10)
1050    ($model:expr, $left:ident + $right:ident < int($target:expr)) => {{
1051        let _sum_var = $model.add($left, $right);
1052        $model.props.less_than(_sum_var, $crate::prelude::int($target));
1053        $crate::constraint_macros::ConstraintRef::new(0)
1054    }};
1055    
1056    ($model:expr, $left:ident + $right:ident <= int($target:expr)) => {{
1057        let _sum_var = $model.add($left, $right);
1058        $model.props.less_than_or_equals(_sum_var, $crate::prelude::int($target));
1059        $crate::constraint_macros::ConstraintRef::new(0)
1060    }};
1061    
1062    ($model:expr, $left:ident + $right:ident > int($target:expr)) => {{
1063        let _sum_var = $model.add($left, $right);
1064        $model.props.greater_than(_sum_var, $crate::prelude::int($target));
1065        $crate::constraint_macros::ConstraintRef::new(0)
1066    }};
1067    
1068    ($model:expr, $left:ident + $right:ident >= int($target:expr)) => {{
1069        let _sum_var = $model.add($left, $right);
1070        $model.props.greater_than_or_equals(_sum_var, $crate::prelude::int($target));
1071        $crate::constraint_macros::ConstraintRef::new(0)
1072    }};
1073    
1074    ($model:expr, $left:ident + $right:ident == int($target:expr)) => {{
1075        let _sum_var = $model.add($left, $right);
1076        $model.props.equals(_sum_var, $crate::prelude::int($target));
1077        $crate::constraint_macros::ConstraintRef::new(0)
1078    }};
1079    
1080    ($model:expr, $left:ident + $right:ident != int($target:expr)) => {{
1081        let _sum_var = $model.add($left, $right);
1082        $model.props.not_equals(_sum_var, $crate::prelude::int($target));
1083        $crate::constraint_macros::ConstraintRef::new(0)
1084    }};
1085    
1086    // Subtraction: x - y <op> <expr>
1087    ($model:expr, $left:ident - $right:ident < $target:ident) => {{
1088        let _diff_var = $model.sub($left, $right);
1089        $model.props.less_than(_diff_var, $target);
1090        $crate::constraint_macros::ConstraintRef::new(0)
1091    }};
1092    
1093    ($model:expr, $left:ident - $right:ident <= $target:ident) => {{
1094        let _diff_var = $model.sub($left, $right);
1095        $model.props.less_than_or_equals(_diff_var, $target);
1096        $crate::constraint_macros::ConstraintRef::new(0)
1097    }};
1098    
1099    ($model:expr, $left:ident - $right:ident > $target:ident) => {{
1100        let _diff_var = $model.sub($left, $right);
1101        $model.props.greater_than(_diff_var, $target);
1102        $crate::constraint_macros::ConstraintRef::new(0)
1103    }};
1104    
1105    ($model:expr, $left:ident - $right:ident >= $target:ident) => {{
1106        let _diff_var = $model.sub($left, $right);
1107        $model.props.greater_than_or_equals(_diff_var, $target);
1108        $crate::constraint_macros::ConstraintRef::new(0)
1109    }};
1110    
1111    ($model:expr, $left:ident - $right:ident == $target:ident) => {{
1112        let _diff_var = $model.sub($left, $right);
1113        $model.props.equals(_diff_var, $target);
1114        $crate::constraint_macros::ConstraintRef::new(0)
1115    }};
1116    
1117    ($model:expr, $left:ident - $right:ident != $target:ident) => {{
1118        let _diff_var = $model.sub($left, $right);
1119        $model.props.not_equals(_diff_var, $target);
1120        $crate::constraint_macros::ConstraintRef::new(0)
1121    }};
1122    
1123    // Subtraction with constants: x - y >= int(0)
1124    ($model:expr, $left:ident - $right:ident < int($target:expr)) => {{
1125        let _diff_var = $model.sub($left, $right);
1126        $model.props.less_than(_diff_var, $crate::prelude::int($target));
1127        $crate::constraint_macros::ConstraintRef::new(0)
1128    }};
1129    
1130    ($model:expr, $left:ident - $right:ident <= int($target:expr)) => {{
1131        let _diff_var = $model.sub($left, $right);
1132        $model.props.less_than_or_equals(_diff_var, $crate::prelude::int($target));
1133        $crate::constraint_macros::ConstraintRef::new(0)
1134    }};
1135    
1136    ($model:expr, $left:ident - $right:ident > int($target:expr)) => {{
1137        let _diff_var = $model.sub($left, $right);
1138        $model.props.greater_than(_diff_var, $crate::prelude::int($target));
1139        $crate::constraint_macros::ConstraintRef::new(0)
1140    }};
1141    
1142    ($model:expr, $left:ident - $right:ident >= int($target:expr)) => {{
1143        let _diff_var = $model.sub($left, $right);
1144        $model.props.greater_than_or_equals(_diff_var, $crate::prelude::int($target));
1145        $crate::constraint_macros::ConstraintRef::new(0)
1146    }};
1147    
1148    ($model:expr, $left:ident - $right:ident == int($target:expr)) => {{
1149        let _diff_var = $model.sub($left, $right);
1150        $model.props.equals(_diff_var, $crate::prelude::int($target));
1151        $crate::constraint_macros::ConstraintRef::new(0)
1152    }};
1153    
1154    ($model:expr, $left:ident - $right:ident != int($target:expr)) => {{
1155        let _diff_var = $model.sub($left, $right);
1156        $model.props.not_equals(_diff_var, $crate::prelude::int($target));
1157        $crate::constraint_macros::ConstraintRef::new(0)
1158    }};
1159    
1160    // Multiplication: x * y <op> <expr>
1161    ($model:expr, $left:ident * $right:ident < $target:ident) => {{
1162        let _prod_var = $model.mul($left, $right);
1163        $model.props.less_than(_prod_var, $target);
1164        $crate::constraint_macros::ConstraintRef::new(0)
1165    }};
1166    
1167    ($model:expr, $left:ident * $right:ident <= $target:ident) => {{
1168        let _prod_var = $model.mul($left, $right);
1169        $model.props.less_than_or_equals(_prod_var, $target);
1170        $crate::constraint_macros::ConstraintRef::new(0)
1171    }};
1172    
1173    ($model:expr, $left:ident * $right:ident > $target:ident) => {{
1174        let _prod_var = $model.mul($left, $right);
1175        $model.props.greater_than(_prod_var, $target);
1176        $crate::constraint_macros::ConstraintRef::new(0)
1177    }};
1178    
1179    ($model:expr, $left:ident * $right:ident >= $target:ident) => {{
1180        let _prod_var = $model.mul($left, $right);
1181        $model.props.greater_than_or_equals(_prod_var, $target);
1182        $crate::constraint_macros::ConstraintRef::new(0)
1183    }};
1184    
1185    ($model:expr, $left:ident * $right:ident == $target:ident) => {{
1186        let _prod_var = $model.mul($left, $right);
1187        $model.props.equals(_prod_var, $target);
1188        $crate::constraint_macros::ConstraintRef::new(0)
1189    }};
1190    
1191    ($model:expr, $left:ident * $right:ident != $target:ident) => {{
1192        let _prod_var = $model.mul($left, $right);
1193        $model.props.not_equals(_prod_var, $target);
1194        $crate::constraint_macros::ConstraintRef::new(0)
1195    }};
1196    
1197    // Multiplication with constants: x * y <= int(10)
1198    ($model:expr, $left:ident * $right:ident < int($target:expr)) => {{
1199        let _prod_var = $model.mul($left, $right);
1200        $model.props.less_than(_prod_var, $crate::prelude::int($target));
1201        $crate::constraint_macros::ConstraintRef::new(0)
1202    }};
1203    
1204    ($model:expr, $left:ident * $right:ident <= int($target:expr)) => {{
1205        let _prod_var = $model.mul($left, $right);
1206        $model.props.less_than_or_equals(_prod_var, $crate::prelude::int($target));
1207        $crate::constraint_macros::ConstraintRef::new(0)
1208    }};
1209    
1210    ($model:expr, $left:ident * $right:ident > int($target:expr)) => {{
1211        let _prod_var = $model.mul($left, $right);
1212        $model.props.greater_than(_prod_var, $crate::prelude::int($target));
1213        $crate::constraint_macros::ConstraintRef::new(0)
1214    }};
1215    
1216    ($model:expr, $left:ident * $right:ident >= int($target:expr)) => {{
1217        let _prod_var = $model.mul($left, $right);
1218        $model.props.greater_than_or_equals(_prod_var, $crate::prelude::int($target));
1219        $crate::constraint_macros::ConstraintRef::new(0)
1220    }};
1221    
1222    ($model:expr, $left:ident * $right:ident == int($target:expr)) => {{
1223        let _prod_var = $model.mul($left, $right);
1224        $model.props.equals(_prod_var, $crate::prelude::int($target));
1225        $crate::constraint_macros::ConstraintRef::new(0)
1226    }};
1227    
1228    ($model:expr, $left:ident * $right:ident != int($target:expr)) => {{
1229        let _prod_var = $model.mul($left, $right);
1230        $model.props.not_equals(_prod_var, $crate::prelude::int($target));
1231        $crate::constraint_macros::ConstraintRef::new(0)
1232    }};
1233
1234    // Multiplication with constant values: x * int(5) == y, x * float(3.14) <= z
1235    ($model:expr, $target:ident == $left:ident * int($value:expr)) => {{
1236        let _prod_var = $model.mul($left, $crate::prelude::int($value));
1237        $model.props.equals($target, _prod_var);
1238        $crate::constraint_macros::ConstraintRef::new(0)
1239    }};
1240
1241    ($model:expr, $target:ident == $left:ident * float($value:expr)) => {{
1242        let _prod_var = $model.mul($left, $crate::prelude::float($value));
1243        $model.props.equals($target, _prod_var);
1244        $crate::constraint_macros::ConstraintRef::new(0)
1245    }};
1246
1247    ($model:expr, $target:ident <= $left:ident * int($value:expr)) => {{
1248        let _prod_var = $model.mul($left, $crate::prelude::int($value));
1249        $model.props.less_than_or_equals($target, _prod_var);
1250        $crate::constraint_macros::ConstraintRef::new(0)
1251    }};
1252
1253    ($model:expr, $target:ident <= $left:ident * float($value:expr)) => {{
1254        let _prod_var = $model.mul($left, $crate::prelude::float($value));
1255        $model.props.less_than_or_equals($target, _prod_var);
1256        $crate::constraint_macros::ConstraintRef::new(0)
1257    }};
1258
1259    ($model:expr, $target:ident >= $left:ident * int($value:expr)) => {{
1260        let _prod_var = $model.mul($left, $crate::prelude::int($value));
1261        $model.props.greater_than_or_equals($target, _prod_var);
1262        $crate::constraint_macros::ConstraintRef::new(0)
1263    }};
1264
1265    ($model:expr, $target:ident >= $left:ident * float($value:expr)) => {{
1266        let _prod_var = $model.mul($left, $crate::prelude::float($value));
1267        $model.props.greater_than_or_equals($target, _prod_var);
1268        $crate::constraint_macros::ConstraintRef::new(0)
1269    }};
1270
1271    ($model:expr, $target:ident < $left:ident * int($value:expr)) => {{
1272        let _prod_var = $model.mul($left, $crate::prelude::int($value));
1273        $model.props.less_than($target, _prod_var);
1274        $crate::constraint_macros::ConstraintRef::new(0)
1275    }};
1276
1277    ($model:expr, $target:ident < $left:ident * float($value:expr)) => {{
1278        let _prod_var = $model.mul($left, $crate::prelude::float($value));
1279        $model.props.less_than($target, _prod_var);
1280        $crate::constraint_macros::ConstraintRef::new(0)
1281    }};
1282
1283    ($model:expr, $target:ident > $left:ident * int($value:expr)) => {{
1284        let _prod_var = $model.mul($left, $crate::prelude::int($value));
1285        $model.props.greater_than($target, _prod_var);
1286        $crate::constraint_macros::ConstraintRef::new(0)
1287    }};
1288
1289    ($model:expr, $target:ident > $left:ident * float($value:expr)) => {{
1290        let _prod_var = $model.mul($left, $crate::prelude::float($value));
1291        $model.props.greater_than($target, _prod_var);
1292        $crate::constraint_macros::ConstraintRef::new(0)
1293    }};
1294
1295    ($model:expr, $target:ident != $left:ident * int($value:expr)) => {{
1296        let _prod_var = $model.mul($left, $crate::prelude::int($value));
1297        $model.props.not_equals($target, _prod_var);
1298        $crate::constraint_macros::ConstraintRef::new(0)
1299    }};
1300
1301    ($model:expr, $target:ident != $left:ident * float($value:expr)) => {{
1302        let _prod_var = $model.mul($left, $crate::prelude::float($value));
1303        $model.props.not_equals($target, _prod_var);
1304        $crate::constraint_macros::ConstraintRef::new(0)
1305    }};
1306    
1307    // Division: x / y <op> <expr>
1308    ($model:expr, $left:ident / $right:ident < $target:ident) => {{
1309        let _quot_var = $model.div($left, $right);
1310        $model.props.less_than(_quot_var, $target);
1311        $crate::constraint_macros::ConstraintRef::new(0)
1312    }};
1313    
1314    ($model:expr, $left:ident / $right:ident <= $target:ident) => {{
1315        let _quot_var = $model.div($left, $right);
1316        $model.props.less_than_or_equals(_quot_var, $target);
1317        $crate::constraint_macros::ConstraintRef::new(0)
1318    }};
1319    
1320    ($model:expr, $left:ident / $right:ident > $target:ident) => {{
1321        let _quot_var = $model.div($left, $right);
1322        $model.props.greater_than(_quot_var, $target);
1323        $crate::constraint_macros::ConstraintRef::new(0)
1324    }};
1325    
1326    ($model:expr, $left:ident / $right:ident >= $target:ident) => {{
1327        let _quot_var = $model.div($left, $right);
1328        $model.props.greater_than_or_equals(_quot_var, $target);
1329        $crate::constraint_macros::ConstraintRef::new(0)
1330    }};
1331    
1332    ($model:expr, $left:ident / $right:ident == $target:ident) => {{
1333        let _quot_var = $model.div($left, $right);
1334        $model.props.equals(_quot_var, $target);
1335        $crate::constraint_macros::ConstraintRef::new(0)
1336    }};
1337    
1338    ($model:expr, $left:ident / $right:ident != $target:ident) => {{
1339        let _quot_var = $model.div($left, $right);
1340        $model.props.not_equals(_quot_var, $target);
1341        $crate::constraint_macros::ConstraintRef::new(0)
1342    }};
1343    
1344    // Division with constants: x / y <= int(5)
1345    ($model:expr, $left:ident / $right:ident < int($target:expr)) => {{
1346        let _quot_var = $model.div($left, $right);
1347        $model.props.less_than(_quot_var, $crate::prelude::int($target));
1348        $crate::constraint_macros::ConstraintRef::new(0)
1349    }};
1350    
1351    ($model:expr, $left:ident / $right:ident <= int($target:expr)) => {{
1352        let _quot_var = $model.div($left, $right);
1353        $model.props.less_than_or_equals(_quot_var, $crate::prelude::int($target));
1354        $crate::constraint_macros::ConstraintRef::new(0)
1355    }};
1356    
1357    ($model:expr, $left:ident / $right:ident > int($target:expr)) => {{
1358        let _quot_var = $model.div($left, $right);
1359        $model.props.greater_than(_quot_var, $crate::prelude::int($target));
1360        $crate::constraint_macros::ConstraintRef::new(0)
1361    }};
1362    
1363    ($model:expr, $left:ident / $right:ident >= int($target:expr)) => {{
1364        let _quot_var = $model.div($left, $right);
1365        $model.props.greater_than_or_equals(_quot_var, $crate::prelude::int($target));
1366        $crate::constraint_macros::ConstraintRef::new(0)
1367    }};
1368    
1369    ($model:expr, $left:ident / $right:ident == int($target:expr)) => {{
1370        let _quot_var = $model.div($left, $right);
1371        $model.props.equals(_quot_var, $crate::prelude::int($target));
1372        $crate::constraint_macros::ConstraintRef::new(0)
1373    }};
1374    
1375    ($model:expr, $left:ident / $right:ident != int($target:expr)) => {{
1376        let _quot_var = $model.div($left, $right);
1377        $model.props.not_equals(_quot_var, $crate::prelude::int($target));
1378        $crate::constraint_macros::ConstraintRef::new(0)
1379    }};
1380    
1381    // Handle modulo operations: x % 3 == 1
1382    ($model:expr, $left:ident % $divisor:literal == $remainder:literal) => {{
1383        let _mod_var = $model.modulo($left, $crate::prelude::int($divisor));
1384        $model.props.equals(_mod_var, $crate::prelude::int($remainder));
1385        $crate::constraint_macros::ConstraintRef::new(0)
1386    }};
1387    
1388    // Enhanced modulo operations: x % y == int(0), x % y != int(0)
1389    ($model:expr, $left:ident % $right:ident == int($remainder:expr)) => {{
1390        let _mod_var = $model.modulo($left, $right);
1391        $model.props.equals(_mod_var, $crate::prelude::int($remainder));
1392        $crate::constraint_macros::ConstraintRef::new(0)
1393    }};
1394    
1395    ($model:expr, $left:ident % $right:ident != int($remainder:expr)) => {{
1396        let _mod_var = $model.modulo($left, $right);
1397        $model.props.not_equals(_mod_var, $crate::prelude::int($remainder));
1398        $crate::constraint_macros::ConstraintRef::new(0)
1399    }};
1400}
1401
1402
1403#[macro_export]
1404macro_rules! postall {
1405    // Use simple comma-separated arguments
1406    ($model:expr, $($rest:tt)*) => {{
1407        $crate::postall_helper!($model, $($rest)*);
1408    }};
1409}
1410
1411/// Helper macro to handle constraint expressions recursively
1412#[macro_export]
1413macro_rules! postall_helper {
1414    // Base case: empty
1415    ($model:expr,) => {};
1416    
1417    // Single constraint at the end
1418    ($model:expr, $var:ident < $target:ident) => {
1419        $crate::post!($model, $var < $target);
1420    };
1421    
1422    ($model:expr, $var:ident <= $target:ident) => {
1423        $crate::post!($model, $var <= $target);
1424    };
1425    
1426    ($model:expr, $var:ident > $target:ident) => {
1427        $crate::post!($model, $var > $target);
1428    };
1429    
1430    ($model:expr, $var:ident >= $target:ident) => {
1431        $crate::post!($model, $var >= $target);
1432    };
1433    
1434    ($model:expr, $var:ident == $target:ident) => {
1435        $crate::post!($model, $var == $target);
1436    };
1437    
1438    ($model:expr, $var:ident != $target:ident) => {
1439        $crate::post!($model, $var != $target);
1440    };
1441    
1442    // With constants
1443    ($model:expr, $var:ident < int($target:expr)) => {
1444        $crate::post!($model, $var < int($target));
1445    };
1446    
1447    ($model:expr, $var:ident <= int($target:expr)) => {
1448        $crate::post!($model, $var <= int($target));
1449    };
1450    
1451    ($model:expr, $var:ident > int($target:expr)) => {
1452        $crate::post!($model, $var > int($target));
1453    };
1454    
1455    ($model:expr, $var:ident >= int($target:expr)) => {
1456        $crate::post!($model, $var >= int($target));
1457    };
1458    
1459    ($model:expr, $var:ident == int($target:expr)) => {
1460        $crate::post!($model, $var == int($target));
1461    };
1462    
1463    ($model:expr, $var:ident != int($target:expr)) => {
1464        $crate::post!($model, $var != int($target));
1465    };
1466    
1467    // Arithmetic operations
1468    ($model:expr, $left:ident + $right:ident <= $target:ident) => {
1469        $crate::post!($model, $left + $right <= $target);
1470    };
1471    
1472    ($model:expr, $left:ident + $right:ident == $target:ident) => {
1473        $crate::post!($model, $left + $right == $target);
1474    };
1475    
1476    ($model:expr, $left:ident + $right:ident <= int($target:expr)) => {
1477        $crate::post!($model, $left + $right <= int($target));
1478    };
1479    
1480    ($model:expr, $left:ident + $right:ident == int($target:expr)) => {
1481        $crate::post!($model, $left + $right == int($target));
1482    };
1483    
1484    // Mathematical functions
1485    ($model:expr, abs($var:ident) >= int($target:expr)) => {
1486        $crate::post!($model, abs($var) >= int($target));
1487    };
1488    
1489    ($model:expr, abs($var:ident) <= int($target:expr)) => {
1490        $crate::post!($model, abs($var) <= int($target));
1491    };
1492    
1493    // Global constraints
1494    ($model:expr, alldiff([$($vars:ident),+ $(,)?])) => {
1495        $crate::post!($model, alldiff([$($vars),+]));
1496    };
1497    
1498    // Global constraints with array expressions
1499    ($model:expr, alldiff($array:expr)) => {
1500        $crate::post!($model, alldiff($array));
1501    };
1502    
1503    // Logical operators
1504    ($model:expr, and($c1:expr, $c2:expr)) => {
1505        $crate::post!($model, and($c1, $c2));
1506    };
1507    
1508    ($model:expr, or($c1:expr, $c2:expr)) => {
1509        $crate::post!($model, or($c1, $c2));
1510    };
1511    
1512    ($model:expr, not($c:expr)) => {
1513        $crate::post!($model, not($c));
1514    };
1515    
1516    // Multiple constraints: handle first one then recurse
1517    ($model:expr, $var:ident < $target:ident, $($rest:tt)*) => {
1518        $crate::post!($model, $var < $target);
1519        $crate::postall_helper!($model, $($rest)*);
1520    };
1521    
1522    ($model:expr, $var:ident <= $target:ident, $($rest:tt)*) => {
1523        $crate::post!($model, $var <= $target);
1524        $crate::postall_helper!($model, $($rest)*);
1525    };
1526    
1527    ($model:expr, $var:ident > $target:ident, $($rest:tt)*) => {
1528        $crate::post!($model, $var > $target);
1529        $crate::postall_helper!($model, $($rest)*);
1530    };
1531    
1532    ($model:expr, $var:ident >= $target:ident, $($rest:tt)*) => {
1533        $crate::post!($model, $var >= $target);
1534        $crate::postall_helper!($model, $($rest)*);
1535    };
1536    
1537    ($model:expr, $var:ident == $target:ident, $($rest:tt)*) => {
1538        $crate::post!($model, $var == $target);
1539        $crate::postall_helper!($model, $($rest)*);
1540    };
1541    
1542    ($model:expr, $var:ident != $target:ident, $($rest:tt)*) => {
1543        $crate::post!($model, $var != $target);
1544        $crate::postall_helper!($model, $($rest)*);
1545    };
1546    
1547    // With constants (multiple)
1548    ($model:expr, $var:ident < int($target:expr), $($rest:tt)*) => {
1549        $crate::post!($model, $var < int($target));
1550        $crate::postall_helper!($model, $($rest)*);
1551    };
1552    
1553    ($model:expr, $var:ident <= int($target:expr), $($rest:tt)*) => {
1554        $crate::post!($model, $var <= int($target));
1555        $crate::postall_helper!($model, $($rest)*);
1556    };
1557    
1558    ($model:expr, $var:ident > int($target:expr), $($rest:tt)*) => {
1559        $crate::post!($model, $var > int($target));
1560        $crate::postall_helper!($model, $($rest)*);
1561    };
1562    
1563    ($model:expr, $var:ident >= int($target:expr), $($rest:tt)*) => {
1564        $crate::post!($model, $var >= int($target));
1565        $crate::postall_helper!($model, $($rest)*);
1566    };
1567    
1568    ($model:expr, $var:ident == int($target:expr), $($rest:tt)*) => {
1569        $crate::post!($model, $var == int($target));
1570        $crate::postall_helper!($model, $($rest)*);
1571    };
1572    
1573    ($model:expr, $var:ident != int($target:expr), $($rest:tt)*) => {
1574        $crate::post!($model, $var != int($target));
1575        $crate::postall_helper!($model, $($rest)*);
1576    };
1577    
1578    // Arithmetic operations (multiple)
1579    ($model:expr, $left:ident + $right:ident <= $target:ident, $($rest:tt)*) => {
1580        $crate::post!($model, $left + $right <= $target);
1581        $crate::postall_helper!($model, $($rest)*);
1582    };
1583    
1584    ($model:expr, $left:ident + $right:ident == $target:ident, $($rest:tt)*) => {
1585        $crate::post!($model, $left + $right == $target);
1586        $crate::postall_helper!($model, $($rest)*);
1587    };
1588    
1589    ($model:expr, $left:ident + $right:ident <= int($target:expr), $($rest:tt)*) => {
1590        $crate::post!($model, $left + $right <= int($target));
1591        $crate::postall_helper!($model, $($rest)*);
1592    };
1593    
1594    ($model:expr, $left:ident + $right:ident == int($target:expr), $($rest:tt)*) => {
1595        $crate::post!($model, $left + $right == int($target));
1596        $crate::postall_helper!($model, $($rest)*);
1597    };
1598    
1599    // Mathematical functions (multiple)
1600    ($model:expr, abs($var:ident) >= int($target:expr), $($rest:tt)*) => {
1601        $crate::post!($model, abs($var) >= int($target));
1602        $crate::postall_helper!($model, $($rest)*);
1603    };
1604    
1605    ($model:expr, abs($var:ident) <= int($target:expr), $($rest:tt)*) => {
1606        $crate::post!($model, abs($var) <= int($target));
1607        $crate::postall_helper!($model, $($rest)*);
1608    };
1609    
1610    // Global constraints (multiple)
1611    ($model:expr, alldiff([$($vars:ident),+ $(,)?]), $($rest:tt)*) => {
1612        $crate::post!($model, alldiff([$($vars),+]));
1613        $crate::postall_helper!($model, $($rest)*);
1614    };
1615    
1616    // Global constraints with array expressions (multiple)
1617    ($model:expr, alldiff($array:expr), $($rest:tt)*) => {
1618        $crate::post!($model, alldiff($array));
1619        $crate::postall_helper!($model, $($rest)*);
1620    };
1621    
1622    // Logical operators (multiple)
1623    ($model:expr, and($c1:expr, $c2:expr), $($rest:tt)*) => {
1624        $crate::post!($model, and($c1, $c2));
1625        $crate::postall_helper!($model, $($rest)*);
1626    };
1627    
1628    ($model:expr, or($c1:expr, $c2:expr), $($rest:tt)*) => {
1629        $crate::post!($model, or($c1, $c2));
1630        $crate::postall_helper!($model, $($rest)*);
1631    };
1632    
1633    ($model:expr, not($c:expr), $($rest:tt)*) => {
1634        $crate::post!($model, not($c));
1635        $crate::postall_helper!($model, $($rest)*);
1636    };
1637}
1638
1639#[cfg(test)]
1640mod tests {
1641    use crate::prelude::*;
1642    
1643    #[test]
1644    fn test_post_macro_basic() {
1645        let mut m = Model::default();
1646        let x = m.int(1, 10);
1647        let y = m.int(1, 10);
1648        
1649        // Test basic variable comparisons
1650        let _c1 = post!(m, x < y);
1651        let _c2 = post!(m, x <= y);
1652        let _c3 = post!(m, x > y);
1653        let _c4 = post!(m, x >= y);
1654        let _c5 = post!(m, x == y);
1655        let _c6 = post!(m, x != y);
1656        
1657        // Should compile without errors
1658        assert!(true);
1659    }
1660    
1661    #[test]
1662    fn test_post_macro_array_syntax() {
1663        let mut m = Model::default();
1664        let x = m.int(1, 10);
1665        let y = m.int(1, 10);
1666        let z = m.int(1, 10);
1667        
1668        // Test alldiff with arrays
1669        let vars = [x, y, z];
1670        let _c1 = post!(m, alldiff(vars));
1671        
1672        let vars_vec = vec![x, y, z];
1673        let _c2 = post!(m, alldiff(vars_vec));
1674        
1675        // Test min/max with arrays
1676        let _c3 = post!(m, min(vars) <= int(5));
1677        let _c4 = post!(m, max(vars_vec) >= int(8));
1678        
1679        // Should compile without errors
1680        assert!(true);
1681    }
1682    
1683    #[test]
1684    fn test_post_macro_constants() {
1685        let mut m = Model::default();
1686        let x = m.int(1, 10);
1687        let y = m.float(1.0, 10.0);
1688        
1689        // Test variable vs integer constants
1690        let _c1 = post!(m, x < int(5));
1691        let _c2 = post!(m, x >= int(1));
1692        let _c3 = post!(m, x == int(7));
1693        
1694        // Test variable vs float constants
1695        let _c4 = post!(m, y <= float(3.14));
1696        let _c5 = post!(m, y > float(1.0));
1697        let _c6 = post!(m, y != float(5.5));
1698        
1699        // Should compile without errors
1700        assert!(true);
1701    }
1702    
1703    #[test]
1704    fn test_post_macro_logical_operators() {
1705        let mut m = Model::default();
1706        let x = m.int(1, 10);
1707        let y = m.int(1, 10);
1708        
1709        // Test basic constraint references (dummy implementation)
1710        let c1 = post!(m, x < y);
1711        let c2 = post!(m, y > int(5));
1712        
1713        // Note: ConstraintRef boolean operations are not fully implemented yet
1714        // Testing basic boolean operations with variables instead
1715        let a = m.int(0, 1);
1716        let b = m.int(0, 1);
1717        
1718        post!(m, and(a, b));   // Boolean AND with variables
1719        post!(m, or(a, b));    // Boolean OR with variables  
1720        post!(m, not(a));      // Boolean NOT with variable
1721        
1722        println!("Constraint references: {:?}, {:?}", c1.id(), c2.id());
1723        
1724        // Should compile without errors
1725        assert!(true);
1726    }
1727    
1728    #[test]
1729    fn test_post_macro_modulo() {
1730        let mut m = Model::default();
1731        let x = m.int(1, 20);
1732        
1733        // Test simple modulo operations (literals only for now)
1734        let _c1 = post!(m, x % 3 == 1);  // x % 3 == 1
1735        
1736        // TODO: More complex patterns with int() helpers:
1737        // let _c2 = post!(m, x % int(5) != int(0));  // x % 5 != 0
1738        
1739        // Should compile without errors
1740        assert!(true);
1741    }
1742    
1743    #[test]
1744    fn test_post_macro_arithmetic() {
1745        let mut m = Model::default();
1746        let x = m.int(1, 10);
1747        let y = m.int(1, 10);
1748        let z = m.int(1, 20);
1749        
1750        // Test arithmetic operations with variables
1751        let _c1 = post!(m, x + y < z);
1752        let _c2 = post!(m, x - y >= z);
1753        let _c3 = post!(m, x * y <= z);
1754        let _c4 = post!(m, x / y == z);
1755        
1756        // Test arithmetic operations with constants
1757        let _c5 = post!(m, x + y <= int(15));
1758        let _c6 = post!(m, x - y >= int(0));
1759        let _c7 = post!(m, x * y == int(12));
1760        let _c8 = post!(m, x / y != int(0));
1761        
1762        // Should compile without errors
1763        assert!(true);
1764    }
1765    
1766    #[test]
1767    fn test_post_macro_mathematical_functions() {
1768        let mut m = Model::default();
1769        let x = m.int(-10, 10);
1770        let y = m.int(1, 10);
1771        let z = m.int(1, 10);
1772        
1773        // Test absolute value
1774        let _c1 = post!(m, abs(x) >= int(1));
1775        let _c2 = post!(m, abs(x) <= y);
1776        
1777        // Test min function
1778        let _c3 = post!(m, min([y, z]) == int(5));
1779        let _c4 = post!(m, min([y, z]) >= x);
1780        
1781        // Test max function  
1782        let _c5 = post!(m, max([y, z]) <= int(10));
1783        let _c6 = post!(m, max([y, z]) != x);
1784        
1785        // Should compile without errors
1786        assert!(true);
1787    }
1788    
1789    #[test]
1790    fn test_post_macro_alldiff() {
1791        let mut m = Model::default();
1792        let x = m.int(1, 10);
1793        let y = m.int(1, 10);
1794        let z = m.int(1, 10);
1795        let w = m.int(1, 10);
1796        
1797        // Test alldiff constraint
1798        let _c1 = post!(m, alldiff([x, y, z]));
1799        let _c2 = post!(m, alldiff([x, y, z, w]));
1800        
1801        // Should compile without errors
1802        assert!(true);
1803    }
1804    
1805    #[test]
1806    fn test_post_macro_enhanced_modulo() {
1807        let mut m = Model::default();
1808        let x = m.int(1, 20);
1809        let y = m.int(2, 5);
1810        
1811        // Test enhanced modulo with variables
1812        let _c1 = post!(m, x % y == int(0));  // x is divisible by y
1813        let _c2 = post!(m, x % y != int(0));  // x is not divisible by y
1814        
1815        // Original literal modulo still works
1816        let _c3 = post!(m, x % 3 == 1);
1817        
1818        // Should compile without errors
1819        assert!(true);
1820    }
1821    
1822    #[test]
1823    fn test_post_macro_complex_expressions() {
1824        let mut m = Model::default();
1825        let x = m.int(1, 10);
1826        let y = m.int(1, 10);
1827        let z = m.int(1, 10);
1828        
1829        // Test combining different constraint types
1830        let _c1 = post!(m, x + y <= int(15));
1831        let _c2 = post!(m, abs(x) >= int(1));  // Simpler abs usage
1832        let _c3 = post!(m, max([x, y]) == z);
1833        let _c4 = post!(m, x % y != int(0));
1834        let _c5 = post!(m, alldiff([x, y, z]));
1835        
1836        // Should compile without errors
1837        assert!(true);
1838    }
1839    
1840    #[test]
1841    fn test_post_macro_negation() {
1842        let mut m = Model::default();
1843        let x = m.int(1, 10);
1844        let y = m.int(1, 10);
1845        
1846        // TODO: Negation to implement:
1847        // let _c1 = post!(m, !(x < y));  // NOT(x < y) should be x >= y
1848        
1849        // For now, basic comparisons work:
1850        let _c2 = post!(m, x >= y);  // Direct equivalent
1851        
1852        // Should compile without errors
1853        assert!(true);
1854    }
1855    
1856    #[test]
1857    fn test_postall_macro() {
1858        let mut m = Model::default();
1859        let x = m.int(1, 10);
1860        let y = m.int(1, 10);
1861        let z = m.int(1, 15);
1862        
1863        // Create some constraint references for testing
1864        let c1 = post!(m, x < y);
1865        let c2 = post!(m, y > int(5));
1866        
1867        // Test boolean variables for logical operations
1868        let a = m.int(0, 1);
1869        let b = m.int(0, 1);
1870        
1871        // Test direct constraint posting with simple comma syntax
1872        postall!(m, 
1873            x < y,
1874            y > int(5),
1875            x + y <= z,
1876            alldiff([x, y, z]),
1877            and(a, b),
1878            or(a, b),
1879            not(a)
1880        );
1881        
1882        println!("Constraint references: {:?}, {:?}", c1.id(), c2.id());
1883        
1884        // Should compile and run without errors
1885        assert!(true);
1886    }
1887    
1888    #[test]
1889    fn test_sum_function_support() {
1890        let mut m = Model::default();
1891        let x = m.int(1, 10);
1892        let y = m.int(1, 10);
1893        let z = m.int(1, 10);
1894        let w = m.int(1, 30);
1895        let array = vec![x, y, z];
1896        
1897        // Test sum with variables
1898        let _c1 = post!(m, sum([x, y, z]) < w);
1899        let _c2 = post!(m, sum([x, y]) <= z);
1900        let _c3 = post!(m, sum([x, y, z]) > x);
1901        let _c4 = post!(m, sum([x, y]) >= y);
1902        let _c5 = post!(m, sum([x, y, z]) == w);
1903        let _c6 = post!(m, sum([x, y]) != z);
1904        
1905        // Test sum with int constants
1906        let _c7 = post!(m, sum([x, y, z]) <= int(25));
1907        let _c8 = post!(m, sum([x, y]) == int(15));
1908        let _c9 = post!(m, sum([x, y, z]) != int(30));
1909        
1910        // Test sum with arrays
1911        let _c10 = post!(m, sum(array) >= int(5));
1912        
1913        // Should compile without errors
1914        assert!(true);
1915    }
1916    
1917    #[test]
1918    fn test_float_constants_math_functions() {
1919        let mut m = Model::default();
1920        let x = m.float(0.0, 10.0);
1921        let y = m.float(0.0, 10.0);
1922        let z = m.float(0.0, 10.0);
1923        
1924        // Test abs with float constants
1925        let _c1 = post!(m, abs(x) < float(5.0));
1926        let _c2 = post!(m, abs(x) <= float(7.5));
1927        let _c3 = post!(m, abs(y) > float(2.0));
1928        let _c4 = post!(m, abs(y) >= float(3.14));
1929        let _c5 = post!(m, abs(z) == float(1.0));
1930        let _c6 = post!(m, abs(z) != float(0.0));
1931        
1932        // Test min with float constants
1933        let _c7 = post!(m, min([x, y]) <= float(8.0));
1934        let _c8 = post!(m, min([x, y, z]) == float(2.5));
1935        let _c9 = post!(m, min([x, y]) != float(10.0));
1936        
1937        // Test max with float constants
1938        let _c10 = post!(m, max([x, y]) >= float(1.0));
1939        let _c11 = post!(m, max([x, y, z]) < float(9.5));
1940        let _c12 = post!(m, max([x, y]) > float(0.5));
1941        
1942        // Should compile without errors
1943        assert!(true);
1944    }
1945    
1946    #[test]
1947    fn test_boolean_logic_functions() {
1948        let mut m = Model::default();
1949        let a = m.bool();
1950        let b = m.bool();
1951        let c = m.bool();
1952        
1953        // Test traditional boolean functions with variables
1954        post!(m, and(a, b));
1955        post!(m, or(a, b));
1956        post!(m, not(a));
1957        
1958        // Test with additional boolean variables
1959        post!(m, and(b, c));
1960        post!(m, or(a, c));
1961        post!(m, not(b));
1962        post!(m, not(c));
1963        
1964        // Should compile without errors
1965        assert!(true);
1966    }
1967    
1968    #[test]
1969    fn test_enhanced_modulo_operations() {
1970        let mut m = Model::default();
1971        let x = m.int(1, 20);
1972        let y = m.int(2, 10);
1973        let z = m.int(0, 5);
1974        
1975        // Test modulo with literal divisor and variable remainder
1976        let _c1 = post!(m, x % 5 == z);
1977        let _c2 = post!(m, x % 3 != z);
1978        
1979        // Test modulo with variables and all comparison operators
1980        let _c3 = post!(m, x % y < z);
1981        let _c4 = post!(m, x % y <= z);
1982        let _c5 = post!(m, x % y > z);
1983        let _c6 = post!(m, x % y >= z);
1984        let _c7 = post!(m, x % y == z);
1985        let _c8 = post!(m, x % y != z);
1986        
1987        // Test modulo with int() constants
1988        let _c9 = post!(m, x % int(7) < int(3));
1989        let _c10 = post!(m, x % int(4) <= int(2));
1990        let _c11 = post!(m, x % int(6) > int(1));
1991        let _c12 = post!(m, x % int(5) >= int(0));
1992        let _c13 = post!(m, x % int(8) == int(2));
1993        let _c14 = post!(m, x % int(9) != int(0));
1994        
1995        // Test original patterns still work
1996        let _c15 = post!(m, x % 3 == 1);  // literal modulo
1997        let _c16 = post!(m, x % y == int(0));  // enhanced variable modulo
1998        let _c17 = post!(m, x % y != int(0));  // enhanced variable modulo
1999        
2000        // Should compile without errors
2001        assert!(true);
2002    }
2003    
2004    #[test]
2005    fn test_logical_operations_enhancement() {
2006        let mut m = Model::default();
2007        let x = m.int(1, 10);
2008        let y = m.int(1, 10);
2009        let z = m.int(1, 10);
2010        
2011        // Create constraint references for testing
2012        let c1 = post!(m, x < y);
2013        let c2 = post!(m, y > int(5));
2014        let c3 = post!(m, z <= int(8));
2015        
2016        // Test boolean variables for logical operations instead
2017        let a = m.int(0, 1);
2018        let b = m.int(0, 1);
2019        let c = m.int(0, 1);
2020        
2021        // Test logical operations with boolean variables (working implementation)
2022        post!(m, and(a, b));   // function-style AND
2023        post!(m, or(a, c));    // function-style OR
2024        post!(m, not(a));      // function-style NOT
2025        
2026        println!("Constraint references: {:?}, {:?}, {:?}", c1.id(), c2.id(), c3.id());
2027        
2028        // Should compile without errors
2029        assert!(true);
2030    }
2031    
2032    #[test]
2033    fn test_constraint_reference_system() {
2034        let mut m = Model::default();
2035        let x = m.int(1, 10);
2036        let y = m.int(1, 10);
2037        
2038        // Test that constraint references are returned and can be used
2039        let c1 = post!(m, x < y);
2040        let c2 = post!(m, x <= y);
2041        let c3 = post!(m, x > y);
2042        let c4 = post!(m, x >= y);
2043        let c5 = post!(m, x == y);
2044        let c6 = post!(m, x != y);
2045        
2046        // Verify constraint references have valid IDs (non-zero for the fixed pattern)
2047        assert!(c1.id() == 0 || c1.id() > 0); // First constraint gets actual PropId, others still dummy
2048        assert_eq!(c2.id(), 0); // Still using dummy for non-fixed patterns
2049        assert_eq!(c3.id(), 0);
2050        assert_eq!(c4.id(), 0);
2051        assert_eq!(c5.id(), 0);
2052        assert_eq!(c6.id(), 0);
2053        
2054        // Should compile and run without errors
2055        assert!(true);
2056    }
2057    
2058    #[test]
2059    fn test_comprehensive_new_functionality() {
2060        let mut m = Model::default();
2061        let x = m.int(1, 10);
2062        let y = m.int(1, 10);
2063        let z = m.int(1, 10);
2064        let a = m.bool();
2065        let b = m.bool();
2066        let f1 = m.float(0.0, 10.0);
2067        let f2 = m.float(0.0, 10.0);
2068        let vars = vec![x, y, z];
2069        
2070        // Test a combination of all new features in one go
2071        postall!(m,
2072            // Simple constraints without float constants
2073            x != y,
2074            
2075            // Boolean logic functions
2076            and(a, b),
2077            or(a, b),
2078            not(a),
2079            
2080            // Original functionality still works
2081            x < y,
2082            alldiff([x, y, z]),
2083            abs(x) >= int(1)
2084        );
2085        
2086        // Test sum separately since it needs variable vector
2087        post!(m, sum(vars) <= int(25));
2088        
2089        // Should compile and run without errors
2090        assert!(true);
2091    }
2092}