runtime_api_demo/
runtime_api_demo.rs

1//! Example demonstrating the Runtime Constraint API (Phase 1 & 2)
2//!
3//! This example shows how to build constraints programmatically at runtime
4//! using the new ultra-short API without requiring compile-time knowledge.
5//!
6//! # Phase 1: Core Expression System
7//! - ExprBuilder for mathematical expressions (x.add(y).eq(z))
8//! - VarIdExt trait for direct variable methods
9//! - ModelExt trait for posting constraints
10//!
11//! # Phase 2: Constraint Builder  
12//! - Builder struct for fluent constraint building
13//! - Model::c() ultra-short syntax (m.c(x).eq(5))
14//! - Global constraint shortcuts (alldiff, alleq, elem, count)
15
16use cspsolver::prelude::*;
17
18fn main() {
19    println!("🚀 Runtime Constraint API Demo - Phase 1 & 2");
20    println!("===============================================\n");
21
22    // Phase 1 Examples
23    example_1_basic_runtime_building();
24    example_2_dynamic_from_data();
25    example_3_expression_chaining();
26    example_4_constraint_composition();
27    
28    // Phase 2 Examples  
29    example_5_model_c_syntax();
30    example_6_builder_fluent_interface();
31    example_7_global_constraints();
32    example_8_mixed_phase_usage();
33}
34
35/// Example 1: Basic runtime constraint building
36fn example_1_basic_runtime_building() {
37    println!("📝 Example 1: Basic Runtime Constraint Building");
38    
39    let mut m = Model::default();
40    let x = m.int(0, 10);
41    let y = m.int(0, 10);
42    let z = m.int(0, 20);
43
44    // Pure runtime constraint building - no compile-time syntax
45    m.post(x.add(y).eq(z));      // x + y == z
46    m.post(x.gt(int(5)));        // x > int(5)
47    m.post(y.le(int(8)));        // y <= int(8)
48
49    if let Ok(solution) = m.solve() {
50        println!("✓ Solution found:");
51        println!("  x = {:?}", solution[x]);
52        println!("  y = {:?}", solution[y]);  
53        println!("  z = {:?}", solution[z]);
54    } else {
55        println!("❌ No solution found");
56    }
57    println!();
58}
59
60/// Example 2: Dynamic constraint building from runtime data
61fn example_2_dynamic_from_data() {
62    println!("📝 Example 2: Dynamic Constraints from Data");
63    
64    let mut m = Model::default();
65    let x = m.int(0, 10);
66    let y = m.int(0, 10);
67    
68    // Simulate loading constraint rules from database/config
69    let rules = vec![
70        ("x", "gt", 3),   // x > 3
71        ("y", "le", 7),   // y <= 7
72        ("x", "ne", 5),   // x != 5
73    ];
74    
75    println!("  Building constraints from data: {:?}", rules);
76    
77    for (var_name, op, value) in rules {
78        let var_id = match var_name {
79            "x" => x,
80            "y" => y,
81            _ => continue,
82        };
83        
84        let constraint = match op {
85            "gt" => var_id.gt(int(value)),
86            "le" => var_id.le(int(value)), 
87            "eq" => var_id.eq(int(value)),
88            "ne" => var_id.ne(int(value)),
89            "ge" => var_id.ge(int(value)),
90            "lt" => var_id.lt(int(value)),
91            _ => {
92                println!("  ⚠️  Unknown operator: {}", op);
93                continue;
94            }
95        };
96        
97        m.post(constraint);
98        println!("  ✓ Posted: {} {} {}", var_name, op, value);
99    }
100    
101    if let Ok(solution) = m.solve() {
102        println!("✓ Solution found:");
103        println!("  x = {:?}", solution[x]);
104        println!("  y = {:?}", solution[y]);
105    } else {
106        println!("❌ No solution found");
107    }
108    println!();
109}
110
111/// Example 3: Complex expression chaining
112fn example_3_expression_chaining() {
113    println!("📝 Example 3: Expression Chaining");
114    
115    let mut m = Model::default();
116    let a = m.int(1, 10);  // Expanded range
117    let b = m.int(1, 10);  // Expanded range
118    let result = m.int(0, 100);  // Expanded range
119    
120    // Build simple expression for now
121    m.post(a.eq(int(5)));      // a = int(5)  
122    m.post(b.eq(int(5)));      // b = int(5)
123    m.post(result.eq(int(10))); // result = int(10)
124    
125    if let Ok(solution) = m.solve() {
126        println!("✓ Found simple values:");
127        println!("  a = {:?}", solution[a]);
128        println!("  b = {:?}", solution[b]);
129        println!("  result = {:?}", solution[result]);
130    } else {
131        println!("❌ No solution found");
132    }
133    println!();
134}
135
136/// Example 4: Constraint composition with boolean logic
137fn example_4_constraint_composition() {
138    println!("📝 Example 4: Constraint Composition");
139    
140    let mut m = Model::default();
141    let x = m.int(0, 20);
142    let y = m.int(0, 20);
143    
144    // Create individual constraints
145    let c1 = x.gt(int(5));      // x > int(5)
146    let c2 = x.lt(int(15));     // x < int(15)  
147    let c3 = y.ge(int(10));     // y >= int(10)
148    
149    // Compose them: (x > 5 AND x < 15) AND y >= 10
150    let range_constraint = c1.and(c2);
151    let combined = range_constraint.and(c3);
152    
153    m.post(combined);
154    
155    if let Ok(solution) = m.solve() {
156        println!("✓ Solution with composed constraints:");
157        println!("  x = {:?} (should be 6-14)", solution[x]);
158        println!("  y = {:?} (should be >= 10)", solution[y]);
159    } else {
160        println!("❌ No solution found");
161    }
162    println!();
163}
164
165// =================== PHASE 2 EXAMPLES ===================
166
167/// Example 5: Model::c() ultra-short syntax
168fn example_5_model_c_syntax() {
169    println!("📝 Example 5: Model::c() Ultra-Short Syntax (Phase 2)");
170    
171    let mut m = Model::default();
172    let x = m.int(1, 10);
173    let y = m.int(1, 10);
174    let sum = m.int(2, 20);
175    
176    // Ultra-short Model::c() syntax - auto-posts constraints
177    m.c(x).gt(int(3));                    // x > int(3)
178    m.c(y).le(int(8));                    // y <= int(8)
179    m.c(x).add(y).eq(sum);               // x + y == sum
180    m.c(sum).eq(int(12));                // sum == int(12)
181    
182    if let Ok(solution) = m.solve() {
183        println!("✓ Solution found:");
184        println!("  x = {:?}", solution[x]);
185        println!("  y = {:?}", solution[y]);
186        println!("  sum = {:?}", solution[sum]);
187    } else {
188        println!("❌ No solution found");
189    }
190    println!();
191}
192
193/// Example 6: Builder fluent interface
194fn example_6_builder_fluent_interface() {
195    println!("📝 Example 6: Builder Fluent Interface (Phase 2)");
196    
197    let mut m = Model::default();
198    let a = m.int(0, 20);
199    let b = m.int(0, 20);
200    let result = m.int(0, 100);
201    
202    // Complex expression building with fluent interface
203    m.c(a).mul(int(3)).add(int(5)).le(int(50));         // a * int(3) + int(5) <= int(50)
204    m.c(b).div(int(2)).sub(int(1)).ge(int(2));          // b / int(2) - int(1) >= int(2)
205    m.c(a).add(b).mul(int(2)).eq(result);              // (a + b) * int(2) == result
206    m.c(result).ne(int(20));                           // result != int(20)
207    
208    if let Ok(solution) = m.solve() {
209        println!("✓ Complex fluent constraints satisfied:");
210        println!("  a = {:?}", solution[a]);
211        println!("  b = {:?}", solution[b]);
212        println!("  result = {:?}", solution[result]);
213    } else {
214        println!("❌ No solution found");
215    }
216    println!();
217}
218
219/// Example 7: Global constraint shortcuts
220fn example_7_global_constraints() {
221    println!("📝 Example 7: Global Constraint Shortcuts (Phase 2)");
222    
223    let mut m = Model::default();
224    let digits = (0..4).map(|_| m.int(1, 4)).collect::<Vec<_>>();
225    
226    // Ultra-short global constraints
227    m.alldiff(&digits);              // All digits must be different
228    m.c(digits[0]).gt(digits[1]);    // First > Second
229    m.c(digits[2]).add(digits[3]).eq(int(5)); // Third + Fourth == int(5)
230    
231    if let Ok(solution) = m.solve() {
232        println!("✓ Global constraint solution:");
233        for (i, &digit) in digits.iter().enumerate() {
234            println!("  digit[{}] = {:?}", i, solution[digit]);
235        }
236    } else {
237        println!("❌ No solution found");
238    }
239    println!();
240}
241
242/// Example 8: Mixed Phase 1 & 2 usage
243fn example_8_mixed_phase_usage() {
244    println!("📝 Example 8: Mixed Phase 1 & 2 Usage");
245    
246    let mut m = Model::default();
247    let x = m.int(1, 10);
248    let y = m.int(1, 10);
249    let z = m.int(1, 10);
250    
251    // Phase 1: Manual constraint creation and posting
252    let constraint1 = x.add(y).gt(int(5));
253    m.post(constraint1);
254    
255    // Phase 2: Auto-posting builder syntax
256    m.c(y).mul(int(2)).le(z.add(int(3)));
257    
258    // Global constraints (Phase 2)
259    m.alldiff(&[x, y, z]);
260    
261    // Phase 1: Complex constraint composition  
262    let c1 = x.lt(int(8));
263    let c2 = y.ge(int(2));
264    let combined = c1.and(c2);
265    m.post(combined);
266    
267    if let Ok(solution) = m.solve() {
268        println!("✓ Mixed API solution:");
269        println!("  x = {:?}", solution[x]);
270        println!("  y = {:?}", solution[y]);
271        println!("  z = {:?}", solution[z]);
272    } else {
273        println!("❌ No solution found");
274    }
275    println!();
276}