1
use std::collections::HashMap;
2
use std::env;
3
use std::process::exit;
4

            
5
use conjure_core::rules::eval_constant;
6
use conjure_core::solver::SolverFamily;
7
use conjure_oxide::{
8
    ast::*,
9
    get_rule_by_name, get_rules,
10
    rule_engine::{resolve_rule_sets, rewrite_model},
11
    solver::{adaptors, Solver},
12
    utils::testing::save_stats_json,
13
    Metadata, Model, Rule,
14
};
15
use uniplate::{Biplate, Uniplate};
16

            
17
fn var_name_from_atom(a: &Atom) -> Name {
18
    <Atom as Biplate<Name>>::universe_bi(a)[0].clone()
19
}
20
#[test]
21
fn rules_present() {
22
    let rules = get_rules();
23
    assert!(!rules.is_empty());
24
}
25

            
26
#[test]
27
fn sum_of_constants() {
28
    let valid_sum_expression = Expression::Sum(
29
        Metadata::new(),
30
        vec![
31
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1))),
32
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(2))),
33
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(3))),
34
        ],
35
    );
36

            
37
    let invalid_sum_expression = Expression::Sum(
38
        Metadata::new(),
39
        vec![
40
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1))),
41
            Expression::Atomic(
42
                Metadata::new(),
43
                Atom::Reference(Name::UserName(String::from("a"))),
44
            ),
45
        ],
46
    );
47

            
48
    assert_eq!(evaluate_sum_of_constants(&valid_sum_expression), Some(6));
49

            
50
    assert_eq!(evaluate_sum_of_constants(&invalid_sum_expression), None);
51
}
52

            
53
fn evaluate_sum_of_constants(expr: &Expression) -> Option<i32> {
54
    match expr {
55
        Expression::Sum(_metadata, expressions) => {
56
            let mut sum = 0;
57
            for e in expressions {
58
                match e {
59
                    Expression::Atomic(_, Atom::Literal(Literal::Int(value))) => {
60
                        sum += value;
61
                    }
62
                    _ => return None,
63
                }
64
            }
65
            Some(sum)
66
        }
67
        _ => None,
68
    }
69
}
70

            
71
#[test]
72
fn recursive_sum_of_constants() {
73
    let complex_expression = Expression::Eq(
74
        Metadata::new(),
75
        Box::new(Expression::Sum(
76
            Metadata::new(),
77
            vec![
78
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1))),
79
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(2))),
80
                Expression::Sum(
81
                    Metadata::new(),
82
                    vec![
83
                        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1))),
84
                        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(2))),
85
                    ],
86
                ),
87
                Expression::Atomic(
88
                    Metadata::new(),
89
                    Atom::Reference(Name::UserName(String::from("a"))),
90
                ),
91
            ],
92
        )),
93
        Box::new(Expression::Atomic(
94
            Metadata::new(),
95
            Atom::Literal(Literal::Int(3)),
96
        )),
97
    );
98
    let correct_simplified_expression = Expression::Eq(
99
        Metadata::new(),
100
        Box::new(Expression::Sum(
101
            Metadata::new(),
102
            vec![
103
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1))),
104
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(2))),
105
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(3))),
106
                Expression::Atomic(
107
                    Metadata::new(),
108
                    Atom::Reference(Name::UserName(String::from("a"))),
109
                ),
110
            ],
111
        )),
112
        Box::new(Expression::Atomic(
113
            Metadata::new(),
114
            Atom::Literal(Literal::Int(3)),
115
        )),
116
    );
117

            
118
    let simplified_expression = simplify_expression(complex_expression.clone());
119
    assert_eq!(simplified_expression, correct_simplified_expression);
120
}
121

            
122
fn simplify_expression(expr: Expression) -> Expression {
123
    match expr {
124
        Expression::Sum(_metadata, expressions) => {
125
            if let Some(result) =
126
                evaluate_sum_of_constants(&Expression::Sum(Metadata::new(), expressions.clone()))
127
            {
128
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(result)))
129
            } else {
130
                Expression::Sum(
131
                    Metadata::new(),
132
                    expressions.into_iter().map(simplify_expression).collect(),
133
                )
134
            }
135
        }
136
        Expression::Eq(_metadata, left, right) => Expression::Eq(
137
            Metadata::new(),
138
            Box::new(simplify_expression(*left)),
139
            Box::new(simplify_expression(*right)),
140
        ),
141
        Expression::Geq(_metadata, left, right) => Expression::Geq(
142
            Metadata::new(),
143
            Box::new(simplify_expression(*left)),
144
            Box::new(simplify_expression(*right)),
145
        ),
146
        _ => expr,
147
    }
148
}
149

            
150
#[test]
151
fn rule_sum_constants() {
152
    let sum_constants = get_rule_by_name("partial_evaluator").unwrap();
153
    let unwrap_sum = get_rule_by_name("unwrap_sum").unwrap();
154

            
155
    let mut expr = Expression::Sum(
156
        Metadata::new(),
157
        vec![
158
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1))),
159
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(2))),
160
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(3))),
161
        ],
162
    );
163

            
164
    expr = sum_constants
165
        .apply(&expr, &Model::new_empty(Default::default()))
166
        .unwrap()
167
        .new_expression;
168
    expr = unwrap_sum
169
        .apply(&expr, &Model::new_empty(Default::default()))
170
        .unwrap()
171
        .new_expression;
172

            
173
    assert_eq!(
174
        expr,
175
        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(6)))
176
    );
177
}
178

            
179
#[test]
180
fn rule_sum_geq() {
181
    let flatten_sum_geq = get_rule_by_name("flatten_sum_geq").unwrap();
182

            
183
    let mut expr = Expression::Geq(
184
        Metadata::new(),
185
        Box::new(Expression::Sum(
186
            Metadata::new(),
187
            vec![
188
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1))),
189
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(2))),
190
            ],
191
        )),
192
        Box::new(Expression::Atomic(
193
            Metadata::new(),
194
            Atom::Literal(Literal::Int(3)),
195
        )),
196
    );
197

            
198
    expr = flatten_sum_geq
199
        .apply(&expr, &Model::new_empty(Default::default()))
200
        .unwrap()
201
        .new_expression;
202

            
203
    assert_eq!(
204
        expr,
205
        Expression::SumGeq(
206
            Metadata::new(),
207
            vec![
208
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1))),
209
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(2))),
210
            ],
211
            Box::new(Expression::Atomic(
212
                Metadata::new(),
213
                Atom::Literal(Literal::Int(3))
214
            ))
215
        )
216
    );
217
}
218

            
219
///
220
/// Reduce and solve:
221
/// ```text
222
/// find a,b,c : int(1..3)
223
/// such that a + b + c <= 2 + 3 - 1
224
/// such that a < b
225
/// ```
226
#[test]
227
fn reduce_solve_xyz() {
228
    println!("Rules: {:?}", get_rules());
229
    let sum_constants = get_rule_by_name("partial_evaluator").unwrap();
230
    let unwrap_sum = get_rule_by_name("unwrap_sum").unwrap();
231
    let lt_to_ineq = get_rule_by_name("lt_to_ineq").unwrap();
232
    let sum_leq_to_sumleq = get_rule_by_name("sum_leq_to_sumleq").unwrap();
233

            
234
    // 2 + 3 - 1
235
    let mut expr1 = Expression::Sum(
236
        Metadata::new(),
237
        vec![
238
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(2))),
239
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(3))),
240
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(-1))),
241
        ],
242
    );
243

            
244
    expr1 = sum_constants
245
        .apply(&expr1, &Model::new_empty(Default::default()))
246
        .unwrap()
247
        .new_expression;
248
    expr1 = unwrap_sum
249
        .apply(&expr1, &Model::new_empty(Default::default()))
250
        .unwrap()
251
        .new_expression;
252
    assert_eq!(
253
        expr1,
254
        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(4)))
255
    );
256

            
257
    // a + b + c = 4
258
    expr1 = Expression::Leq(
259
        Metadata::new(),
260
        Box::new(Expression::Sum(
261
            Metadata::new(),
262
            vec![
263
                Expression::Atomic(
264
                    Metadata::new(),
265
                    Atom::Reference(Name::UserName(String::from("a"))),
266
                ),
267
                Expression::Atomic(
268
                    Metadata::new(),
269
                    Atom::Reference(Name::UserName(String::from("b"))),
270
                ),
271
                Expression::Atomic(
272
                    Metadata::new(),
273
                    Atom::Reference(Name::UserName(String::from("c"))),
274
                ),
275
            ],
276
        )),
277
        Box::new(expr1),
278
    );
279
    expr1 = sum_leq_to_sumleq
280
        .apply(&expr1, &Model::new_empty(Default::default()))
281
        .unwrap()
282
        .new_expression;
283
    assert_eq!(
284
        expr1,
285
        Expression::SumLeq(
286
            Metadata::new(),
287
            vec![
288
                Expression::Atomic(
289
                    Metadata::new(),
290
                    Atom::Reference(Name::UserName(String::from("a")))
291
                ),
292
                Expression::Atomic(
293
                    Metadata::new(),
294
                    Atom::Reference(Name::UserName(String::from("b")))
295
                ),
296
                Expression::Atomic(
297
                    Metadata::new(),
298
                    Atom::Reference(Name::UserName(String::from("c")))
299
                ),
300
            ],
301
            Box::new(Expression::Atomic(
302
                Metadata::new(),
303
                Atom::Literal(Literal::Int(4))
304
            ))
305
        )
306
    );
307

            
308
    // a < b
309
    let mut expr2 = Expression::Lt(
310
        Metadata::new(),
311
        Box::new(Expression::Atomic(
312
            Metadata::new(),
313
            Atom::Reference(Name::UserName(String::from("a"))),
314
        )),
315
        Box::new(Expression::Atomic(
316
            Metadata::new(),
317
            Atom::Reference(Name::UserName(String::from("b"))),
318
        )),
319
    );
320
    expr2 = lt_to_ineq
321
        .apply(&expr2, &Model::new_empty(Default::default()))
322
        .unwrap()
323
        .new_expression;
324
    assert_eq!(
325
        expr2,
326
        Expression::Ineq(
327
            Metadata::new(),
328
            Box::new(Expression::Atomic(
329
                Metadata::new(),
330
                Atom::Reference(Name::UserName(String::from("a")))
331
            )),
332
            Box::new(Expression::Atomic(
333
                Metadata::new(),
334
                Atom::Reference(Name::UserName(String::from("b")))
335
            )),
336
            Box::new(Expression::Atomic(
337
                Metadata::new(),
338
                Atom::Literal(Literal::Int(-1))
339
            ))
340
        )
341
    );
342

            
343
    let mut model = Model::new(HashMap::new(), vec![expr1, expr2], Default::default());
344
    model.variables.insert(
345
        Name::UserName(String::from("a")),
346
        DecisionVariable {
347
            domain: Domain::IntDomain(vec![Range::Bounded(1, 3)]),
348
        },
349
    );
350
    model.variables.insert(
351
        Name::UserName(String::from("b")),
352
        DecisionVariable {
353
            domain: Domain::IntDomain(vec![Range::Bounded(1, 3)]),
354
        },
355
    );
356
    model.variables.insert(
357
        Name::UserName(String::from("c")),
358
        DecisionVariable {
359
            domain: Domain::IntDomain(vec![Range::Bounded(1, 3)]),
360
        },
361
    );
362

            
363
    let solver: Solver<adaptors::Minion> = Solver::new(adaptors::Minion::new());
364
    let solver = solver.load_model(model).unwrap();
365
    solver.solve(Box::new(|_| true)).unwrap();
366
}
367

            
368
#[test]
369
fn rule_remove_double_negation() {
370
    let remove_double_negation = get_rule_by_name("remove_double_negation").unwrap();
371

            
372
    let mut expr = Expression::Not(
373
        Metadata::new(),
374
        Box::new(Expression::Not(
375
            Metadata::new(),
376
            Box::new(Expression::Atomic(
377
                Metadata::new(),
378
                Atom::Literal(Literal::Bool(true)),
379
            )),
380
        )),
381
    );
382

            
383
    expr = remove_double_negation
384
        .apply(&expr, &Model::new_empty(Default::default()))
385
        .unwrap()
386
        .new_expression;
387

            
388
    assert_eq!(
389
        expr,
390
        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true)))
391
    );
392
}
393

            
394
#[test]
395
fn rule_unwrap_nested_or() {
396
    let unwrap_nested_or = get_rule_by_name("unwrap_nested_or").unwrap();
397

            
398
    let mut expr = Expression::Or(
399
        Metadata::new(),
400
        vec![
401
            Expression::Or(
402
                Metadata::new(),
403
                vec![
404
                    Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
405
                    Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false))),
406
                ],
407
            ),
408
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
409
        ],
410
    );
411

            
412
    expr = unwrap_nested_or
413
        .apply(&expr, &Model::new_empty(Default::default()))
414
        .unwrap()
415
        .new_expression;
416

            
417
    assert_eq!(
418
        expr,
419
        Expression::Or(
420
            Metadata::new(),
421
            vec![
422
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
423
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false))),
424
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
425
            ]
426
        )
427
    );
428
}
429

            
430
#[test]
431
fn rule_unwrap_nested_and() {
432
    let unwrap_nested_and = get_rule_by_name("unwrap_nested_and").unwrap();
433

            
434
    let mut expr = Expression::And(
435
        Metadata::new(),
436
        vec![
437
            Expression::And(
438
                Metadata::new(),
439
                vec![
440
                    Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
441
                    Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false))),
442
                ],
443
            ),
444
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
445
        ],
446
    );
447

            
448
    expr = unwrap_nested_and
449
        .apply(&expr, &Model::new_empty(Default::default()))
450
        .unwrap()
451
        .new_expression;
452

            
453
    assert_eq!(
454
        expr,
455
        Expression::And(
456
            Metadata::new(),
457
            vec![
458
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
459
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false))),
460
                Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
461
            ]
462
        )
463
    );
464
}
465

            
466
#[test]
467
fn unwrap_nested_or_not_changed() {
468
    let unwrap_nested_or = get_rule_by_name("unwrap_nested_or").unwrap();
469

            
470
    let expr = Expression::Or(
471
        Metadata::new(),
472
        vec![
473
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
474
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false))),
475
        ],
476
    );
477

            
478
    let result = unwrap_nested_or.apply(&expr, &Model::new_empty(Default::default()));
479

            
480
    assert!(result.is_err());
481
}
482

            
483
#[test]
484
fn unwrap_nested_and_not_changed() {
485
    let unwrap_nested_and = get_rule_by_name("unwrap_nested_and").unwrap();
486

            
487
    let expr = Expression::And(
488
        Metadata::new(),
489
        vec![
490
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
491
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false))),
492
        ],
493
    );
494

            
495
    let result = unwrap_nested_and.apply(&expr, &Model::new_empty(Default::default()));
496

            
497
    assert!(result.is_err());
498
}
499

            
500
#[test]
501
fn remove_trivial_and_or() {
502
    let remove_trivial_and = get_rule_by_name("remove_trivial_and").unwrap();
503
    let remove_trivial_or = get_rule_by_name("remove_trivial_or").unwrap();
504

            
505
    let mut expr_and = Expression::And(
506
        Metadata::new(),
507
        vec![Expression::Atomic(
508
            Metadata::new(),
509
            Atom::Literal(Literal::Bool(true)),
510
        )],
511
    );
512
    let mut expr_or = Expression::Or(
513
        Metadata::new(),
514
        vec![Expression::Atomic(
515
            Metadata::new(),
516
            Atom::Literal(Literal::Bool(false)),
517
        )],
518
    );
519

            
520
    expr_and = remove_trivial_and
521
        .apply(&expr_and, &Model::new_empty(Default::default()))
522
        .unwrap()
523
        .new_expression;
524
    expr_or = remove_trivial_or
525
        .apply(&expr_or, &Model::new_empty(Default::default()))
526
        .unwrap()
527
        .new_expression;
528

            
529
    assert_eq!(
530
        expr_and,
531
        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true)))
532
    );
533
    assert_eq!(
534
        expr_or,
535
        Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false)))
536
    );
537
}
538

            
539
#[test]
540
fn rule_distribute_not_over_and() {
541
    let distribute_not_over_and = get_rule_by_name("distribute_not_over_and").unwrap();
542

            
543
    let mut expr = Expression::Not(
544
        Metadata::new(),
545
        Box::new(Expression::And(
546
            Metadata::new(),
547
            vec![
548
                Expression::Atomic(
549
                    Metadata::new(),
550
                    Atom::Reference(Name::UserName(String::from("a"))),
551
                ),
552
                Expression::Atomic(
553
                    Metadata::new(),
554
                    Atom::Reference(Name::UserName(String::from("b"))),
555
                ),
556
            ],
557
        )),
558
    );
559

            
560
    expr = distribute_not_over_and
561
        .apply(&expr, &Model::new_empty(Default::default()))
562
        .unwrap()
563
        .new_expression;
564

            
565
    assert_eq!(
566
        expr,
567
        Expression::Or(
568
            Metadata::new(),
569
            vec![
570
                Expression::Not(
571
                    Metadata::new(),
572
                    Box::new(Expression::Atomic(
573
                        Metadata::new(),
574
                        Atom::Reference(Name::UserName(String::from("a")))
575
                    ))
576
                ),
577
                Expression::Not(
578
                    Metadata::new(),
579
                    Box::new(Expression::Atomic(
580
                        Metadata::new(),
581
                        Atom::Reference(Name::UserName(String::from("b")))
582
                    ))
583
                ),
584
            ]
585
        )
586
    );
587
}
588

            
589
#[test]
590
fn rule_distribute_not_over_or() {
591
    let distribute_not_over_or = get_rule_by_name("distribute_not_over_or").unwrap();
592

            
593
    let mut expr = Expression::Not(
594
        Metadata::new(),
595
        Box::new(Expression::Or(
596
            Metadata::new(),
597
            vec![
598
                Expression::Atomic(
599
                    Metadata::new(),
600
                    Atom::Reference(Name::UserName(String::from("a"))),
601
                ),
602
                Expression::Atomic(
603
                    Metadata::new(),
604
                    Atom::Reference(Name::UserName(String::from("b"))),
605
                ),
606
            ],
607
        )),
608
    );
609

            
610
    expr = distribute_not_over_or
611
        .apply(&expr, &Model::new_empty(Default::default()))
612
        .unwrap()
613
        .new_expression;
614

            
615
    assert_eq!(
616
        expr,
617
        Expression::And(
618
            Metadata::new(),
619
            vec![
620
                Expression::Not(
621
                    Metadata::new(),
622
                    Box::new(Expression::Atomic(
623
                        Metadata::new(),
624
                        Atom::Reference(Name::UserName(String::from("a")))
625
                    ))
626
                ),
627
                Expression::Not(
628
                    Metadata::new(),
629
                    Box::new(Expression::Atomic(
630
                        Metadata::new(),
631
                        Atom::Reference(Name::UserName(String::from("b")))
632
                    ))
633
                ),
634
            ]
635
        )
636
    );
637
}
638

            
639
#[test]
640
fn rule_distribute_not_over_and_not_changed() {
641
    let distribute_not_over_and = get_rule_by_name("distribute_not_over_and").unwrap();
642

            
643
    let expr = Expression::Not(
644
        Metadata::new(),
645
        Box::new(Expression::Atomic(
646
            Metadata::new(),
647
            Atom::Reference(Name::UserName(String::from("a"))),
648
        )),
649
    );
650

            
651
    let result = distribute_not_over_and.apply(&expr, &Model::new_empty(Default::default()));
652

            
653
    assert!(result.is_err());
654
}
655

            
656
#[test]
657
fn rule_distribute_not_over_or_not_changed() {
658
    let distribute_not_over_or = get_rule_by_name("distribute_not_over_or").unwrap();
659

            
660
    let expr = Expression::Not(
661
        Metadata::new(),
662
        Box::new(Expression::Atomic(
663
            Metadata::new(),
664
            Atom::Reference(Name::UserName(String::from("a"))),
665
        )),
666
    );
667

            
668
    let result = distribute_not_over_or.apply(&expr, &Model::new_empty(Default::default()));
669

            
670
    assert!(result.is_err());
671
}
672

            
673
#[test]
674
fn rule_distribute_or_over_and() {
675
    let distribute_or_over_and = get_rule_by_name("distribute_or_over_and").unwrap();
676

            
677
    let expr = Expression::Or(
678
        Metadata::new(),
679
        vec![
680
            Expression::And(
681
                Metadata::new(),
682
                vec![
683
                    Expression::Atomic(Metadata::new(), Atom::Reference(Name::MachineName(1))),
684
                    Expression::Atomic(Metadata::new(), Atom::Reference(Name::MachineName(2))),
685
                ],
686
            ),
687
            Expression::Atomic(Metadata::new(), Atom::Reference(Name::MachineName(3))),
688
        ],
689
    );
690

            
691
    let red = distribute_or_over_and
692
        .apply(&expr, &Model::new_empty(Default::default()))
693
        .unwrap();
694

            
695
    assert_eq!(
696
        red.new_expression,
697
        Expression::And(
698
            Metadata::new(),
699
            vec![
700
                Expression::Or(
701
                    Metadata::new(),
702
                    vec![
703
                        Expression::Atomic(Metadata::new(), Atom::Reference(Name::MachineName(3))),
704
                        Expression::Atomic(Metadata::new(), Atom::Reference(Name::MachineName(1))),
705
                    ]
706
                ),
707
                Expression::Or(
708
                    Metadata::new(),
709
                    vec![
710
                        Expression::Atomic(Metadata::new(), Atom::Reference(Name::MachineName(3))),
711
                        Expression::Atomic(Metadata::new(), Atom::Reference(Name::MachineName(2))),
712
                    ]
713
                ),
714
            ]
715
        ),
716
    );
717
}
718

            
719
///
720
/// Reduce and solve:
721
/// ```text
722
/// find a,b,c : int(1..3)
723
/// such that a + b + c = 4
724
/// such that a < b
725
/// ```
726
///
727
/// This test uses the rewrite function to simplify the expression instead
728
/// of applying the rules manually.
729
#[test]
730
fn rewrite_solve_xyz() {
731
    println!("Rules: {:?}", get_rules());
732

            
733
    let rule_sets = match resolve_rule_sets(SolverFamily::Minion, &vec!["Constant".to_string()]) {
734
        Ok(rs) => rs,
735
        Err(e) => {
736
            eprintln!("Error resolving rule sets: {}", e);
737
            exit(1);
738
        }
739
    };
740
    println!("Rule sets: {:?}", rule_sets);
741

            
742
    // Create variables and domains
743
    let variable_a = Atom::Reference(Name::UserName(String::from("a")));
744
    let variable_b = Atom::Reference(Name::UserName(String::from("b")));
745
    let variable_c = Atom::Reference(Name::UserName(String::from("c")));
746
    let domain = Domain::IntDomain(vec![Range::Bounded(1, 3)]);
747

            
748
    // Construct nested expression
749
    let nested_expr = Expression::And(
750
        Metadata::new(),
751
        vec![
752
            Expression::Eq(
753
                Metadata::new(),
754
                Box::new(Expression::Sum(
755
                    Metadata::new(),
756
                    vec![
757
                        Expression::Atomic(Metadata::new(), variable_a.clone()),
758
                        Expression::Atomic(Metadata::new(), variable_b.clone()),
759
                        Expression::Atomic(Metadata::new(), variable_c.clone()),
760
                    ],
761
                )),
762
                Box::new(Expression::Atomic(
763
                    Metadata::new(),
764
                    Atom::Literal(Literal::Int(4)),
765
                )),
766
            ),
767
            Expression::Lt(
768
                Metadata::new(),
769
                Box::new(Expression::Atomic(Metadata::new(), variable_a.clone())),
770
                Box::new(Expression::Atomic(Metadata::new(), variable_b.clone())),
771
            ),
772
        ],
773
    );
774

            
775
    let rule_sets = match resolve_rule_sets(SolverFamily::Minion, &vec!["Constant".to_string()]) {
776
        Ok(rs) => rs,
777
        Err(e) => {
778
            eprintln!("Error resolving rule sets: {}", e);
779
            exit(1);
780
        }
781
    };
782

            
783
    // Apply rewrite function to the nested expression
784
    let rewritten_expr = rewrite_model(
785
        &Model::new(HashMap::new(), vec![nested_expr], Default::default()),
786
        &rule_sets,
787
    )
788
    .unwrap()
789
    .constraints;
790

            
791
    // Check if the expression is in its simplest form
792

            
793
    assert!(rewritten_expr.iter().all(|expr| is_simple(expr)));
794

            
795
    // Create model with variables and constraints
796
    let mut model = Model::new(HashMap::new(), rewritten_expr, Default::default());
797

            
798
    // Insert variables and domains
799
    model.variables.insert(
800
        var_name_from_atom(&variable_a.clone()),
801
        DecisionVariable {
802
            domain: domain.clone(),
803
        },
804
    );
805
    model.variables.insert(
806
        var_name_from_atom(&variable_b.clone()),
807
        DecisionVariable {
808
            domain: domain.clone(),
809
        },
810
    );
811
    model.variables.insert(
812
        var_name_from_atom(&variable_c.clone()),
813
        DecisionVariable {
814
            domain: domain.clone(),
815
        },
816
    );
817

            
818
    let solver: Solver<adaptors::Minion> = Solver::new(adaptors::Minion::new());
819
    let solver = solver.load_model(model).unwrap();
820
    solver.solve(Box::new(|_| true)).unwrap();
821
}
822

            
823
#[test]
824
fn rewrite_solve_xyz_parameterized() {
825
    println!("Rules: {:?}", get_rules());
826

            
827
    let rule_sets = match resolve_rule_sets(SolverFamily::Minion, &vec!["Constant".to_string()]) {
828
        Ok(rs) => rs,
829
        Err(e) => {
830
            eprintln!("Error resolving rule sets: {}", e);
831
            exit(1);
832
        }
833
    };
834
    println!("Rule sets: {:?}", rule_sets);
835

            
836
    // Create variables and domain
837
    let variable_a = Atom::Reference(Name::UserName(String::from("a")));
838
    let variable_b = Atom::Reference(Name::UserName(String::from("b")));
839
    let variable_c = Atom::Reference(Name::UserName(String::from("c")));
840
    let domain = Domain::IntDomain(vec![Range::Bounded(1, 3)]);
841

            
842
    // Create a vector of test cases with varying number of OR clauses
843
    let test_cases = vec![1, 2, 3, 4];
844

            
845
    for num_or_clauses in test_cases {
846
        // Construct OR'd expression
847
        let mut or_exprs = Vec::new();
848
        for _i in 0..num_or_clauses {
849
            let expr = Expression::And(
850
                Metadata::new(),
851
                vec![
852
                    Expression::Eq(
853
                        Metadata::new(),
854
                        Box::new(Expression::Sum(
855
                            Metadata::new(),
856
                            vec![
857
                                Expression::Atomic(Metadata::new(), variable_a.clone()),
858
                                Expression::Atomic(Metadata::new(), variable_b.clone()),
859
                                Expression::Atomic(Metadata::new(), variable_c.clone()),
860
                            ],
861
                        )),
862
                        Box::new(Expression::Atomic(
863
                            Metadata::new(),
864
                            Atom::Literal(Literal::Int(4)),
865
                        )),
866
                    ),
867
                    Expression::Lt(
868
                        Metadata::new(),
869
                        Box::new(Expression::Atomic(Metadata::new(), variable_a.clone())),
870
                        Box::new(Expression::Atomic(Metadata::new(), variable_b.clone())),
871
                    ),
872
                ],
873
            );
874
            or_exprs.push(expr);
875
        }
876
        let nested_expr = Expression::Or(Metadata::new(), or_exprs);
877

            
878
        let model_for_rewrite = Model::new(
879
            HashMap::new(),
880
            vec![nested_expr.clone()],
881
            Default::default(),
882
        );
883
        let model_for_rewrite_unoptimized = Model::new(
884
            HashMap::new(),
885
            vec![nested_expr.clone()],
886
            Default::default(),
887
        );
888

            
889
        // Apply rewrite function to the nested expression
890
        let rewritten_expr = rewrite_model(&model_for_rewrite, &rule_sets)
891
            .unwrap()
892
            .constraints;
893

            
894
        env::set_var("OPTIMIZATIONS", "0");
895

            
896
        let rewritten_expr_unoptimized = rewrite_model(&model_for_rewrite_unoptimized, &rule_sets)
897
            .unwrap()
898
            .constraints;
899

            
900
        env::remove_var("OPTIMIZATIONS");
901

            
902
        let info_file_name_optimized = format!("rewrite_solve_xyz_optimized_{}", num_or_clauses);
903
        let info_file_name_unoptimized =
904
            format!("rewrite_solve_xyz_unoptimized_{}", num_or_clauses);
905

            
906
        save_stats_json(
907
            model_for_rewrite.context,
908
            "tests",
909
            &info_file_name_optimized,
910
        )
911
        .expect("Could not save stats!");
912
        save_stats_json(
913
            model_for_rewrite_unoptimized.context,
914
            "tests",
915
            &info_file_name_unoptimized,
916
        )
917
        .expect("Could not save stats!");
918

            
919
        // Check if the expression is in its simplest form
920
        assert!(rewritten_expr.iter().all(|expr| is_simple(expr)));
921

            
922
        assert!(rewritten_expr_unoptimized
923
            .iter()
924
            .all(|expr| is_simple(expr)));
925

            
926
        // Create model with variables and constraints
927
        let mut model = Model::new(HashMap::new(), rewritten_expr, Default::default());
928
        let mut model_unoptimized = Model::new(
929
            HashMap::new(),
930
            rewritten_expr_unoptimized,
931
            Default::default(),
932
        );
933

            
934
        // Insert variables and domains
935
        model.variables.insert(
936
            var_name_from_atom(&variable_a.clone()),
937
            DecisionVariable {
938
                domain: domain.clone(),
939
            },
940
        );
941
        model.variables.insert(
942
            var_name_from_atom(&variable_b.clone()),
943
            DecisionVariable {
944
                domain: domain.clone(),
945
            },
946
        );
947
        model.variables.insert(
948
            var_name_from_atom(&variable_c.clone()),
949
            DecisionVariable {
950
                domain: domain.clone(),
951
            },
952
        );
953

            
954
        model_unoptimized.variables.insert(
955
            var_name_from_atom(&variable_a.clone()),
956
            DecisionVariable {
957
                domain: domain.clone(),
958
            },
959
        );
960
        model_unoptimized.variables.insert(
961
            var_name_from_atom(&variable_b.clone()),
962
            DecisionVariable {
963
                domain: domain.clone(),
964
            },
965
        );
966
        model_unoptimized.variables.insert(
967
            var_name_from_atom(&variable_c.clone()),
968
            DecisionVariable {
969
                domain: domain.clone(),
970
            },
971
        );
972

            
973
        let solver: Solver<adaptors::Minion> = Solver::new(adaptors::Minion::new());
974
        let solver = solver.load_model(model).unwrap();
975
        solver.solve(Box::new(|_| true)).unwrap();
976

            
977
        let solver_unoptimized: Solver<adaptors::Minion> = Solver::new(adaptors::Minion::new());
978
        let solver_unoptimized = solver_unoptimized.load_model(model_unoptimized).unwrap();
979
        solver_unoptimized.solve(Box::new(|_| true)).unwrap();
980
    }
981
}
982

            
983
struct RuleResult<'a> {
984
    #[allow(dead_code)]
985
    rule: &'a Rule<'a>,
986
    new_expression: Expression,
987
}
988

            
989
/// # Returns
990
/// - True if `expression` is in its simplest form.
991
/// - False otherwise.
992
pub fn is_simple(expression: &Expression) -> bool {
993
    let rules = get_rules();
994
    let mut new = expression.clone();
995
    while let Some(step) = is_simple_iteration(&new, &rules) {
996
        new = step;
997
    }
998
    new == *expression
999
}
/// # Returns
/// - Some(<new_expression>) after applying the first applicable rule to `expr` or a sub-expression.
/// - None if no rule is applicable to the expression or any sub-expression.
fn is_simple_iteration<'a>(
    expression: &'a Expression,
    rules: &'a Vec<&'a Rule<'a>>,
) -> Option<Expression> {
    let rule_results = apply_all_rules(expression, rules);
    if let Some(new) = choose_rewrite(&rule_results) {
        return Some(new);
    } else {
        let mut sub = expression.children();
        for i in 0..sub.len() {
            if let Some(new) = is_simple_iteration(&sub[i], rules) {
                sub[i] = new;
                return Some(expression.with_children(sub.clone()));
            }
        }
    }
    None // No rules applicable to this branch of the expression
}
/// # Returns
/// - A list of RuleResults after applying all rules to `expression`.
/// - An empty list if no rules are applicable.
fn apply_all_rules<'a>(
    expression: &'a Expression,
    rules: &'a Vec<&'a Rule<'a>>,
) -> Vec<RuleResult<'a>> {
    let mut results = Vec::new();
    for rule in rules {
        match rule.apply(expression, &Model::new_empty(Default::default())) {
            Ok(red) => {
                results.push(RuleResult {
                    rule,
                    new_expression: red.new_expression,
                });
            }
            Err(_) => continue,
        }
    }
    results
}
/// # Returns
/// - Some(<new_expression>) after applying the first rule in `results`.
/// - None if `results` is empty.
fn choose_rewrite(results: &[RuleResult]) -> Option<Expression> {
    if results.is_empty() {
        return None;
    }
    // Return the first result for now
    // println!("Applying rule: {:?}", results[0].rule);
    Some(results[0].new_expression.clone())
}
#[test]
fn eval_const_int() {
    let expr = Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1)));
    let result = eval_constant(&expr);
    assert_eq!(result, Some(Literal::Int(1)));
}
#[test]
fn eval_const_bool() {
    let expr = Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true)));
    let result = eval_constant(&expr);
    assert_eq!(result, Some(Literal::Bool(true)));
}
#[test]
fn eval_const_and() {
    let expr = Expression::And(
        Metadata::new(),
        vec![
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false))),
        ],
    );
    let result = eval_constant(&expr);
    assert_eq!(result, Some(Literal::Bool(false)));
}
#[test]
fn eval_const_ref() {
    let expr = Expression::Atomic(
        Metadata::new(),
        Atom::Reference(Name::UserName(String::from("a"))),
    );
    let result = eval_constant(&expr);
    assert_eq!(result, None);
}
#[test]
fn eval_const_nested_ref() {
    let expr = Expression::Sum(
        Metadata::new(),
        vec![
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1))),
            Expression::And(
                Metadata::new(),
                vec![
                    Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
                    Expression::Atomic(
                        Metadata::new(),
                        Atom::Reference(Name::UserName(String::from("a"))),
                    ),
                ],
            ),
        ],
    );
    let result = eval_constant(&expr);
    assert_eq!(result, None);
}
#[test]
fn eval_const_eq_int() {
    let expr = Expression::Eq(
        Metadata::new(),
        Box::new(Expression::Atomic(
            Metadata::new(),
            Atom::Literal(Literal::Int(1)),
        )),
        Box::new(Expression::Atomic(
            Metadata::new(),
            Atom::Literal(Literal::Int(1)),
        )),
    );
    let result = eval_constant(&expr);
    assert_eq!(result, Some(Literal::Bool(true)));
}
#[test]
fn eval_const_eq_bool() {
    let expr = Expression::Eq(
        Metadata::new(),
        Box::new(Expression::Atomic(
            Metadata::new(),
            Atom::Literal(Literal::Bool(true)),
        )),
        Box::new(Expression::Atomic(
            Metadata::new(),
            Atom::Literal(Literal::Bool(true)),
        )),
    );
    let result = eval_constant(&expr);
    assert_eq!(result, Some(Literal::Bool(true)));
}
#[test]
fn eval_const_eq_mixed() {
    let expr = Expression::Eq(
        Metadata::new(),
        Box::new(Expression::Atomic(
            Metadata::new(),
            Atom::Literal(Literal::Int(1)),
        )),
        Box::new(Expression::Atomic(
            Metadata::new(),
            Atom::Literal(Literal::Bool(true)),
        )),
    );
    let result = eval_constant(&expr);
    assert_eq!(result, None);
}
#[test]
fn eval_const_sum_mixed() {
    let expr = Expression::Sum(
        Metadata::new(),
        vec![
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Int(1))),
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(true))),
        ],
    );
    let result = eval_constant(&expr);
    assert_eq!(result, None);
}
#[test]
fn eval_const_sum_xyz() {
    let expr = Expression::And(
        Metadata::new(),
        vec![
            Expression::Eq(
                Metadata::new(),
                Box::new(Expression::Sum(
                    Metadata::new(),
                    vec![
                        Expression::Atomic(
                            Metadata::new(),
                            Atom::Reference(Name::UserName(String::from("x"))),
                        ),
                        Expression::Atomic(
                            Metadata::new(),
                            Atom::Reference(Name::UserName(String::from("y"))),
                        ),
                        Expression::Atomic(
                            Metadata::new(),
                            Atom::Reference(Name::UserName(String::from("z"))),
                        ),
                    ],
                )),
                Box::new(Expression::Atomic(
                    Metadata::new(),
                    Atom::Literal(Literal::Int(4)),
                )),
            ),
            Expression::Geq(
                Metadata::new(),
                Box::new(Expression::Atomic(
                    Metadata::new(),
                    Atom::Reference(Name::UserName(String::from("x"))),
                )),
                Box::new(Expression::Atomic(
                    Metadata::new(),
                    Atom::Reference(Name::UserName(String::from("y"))),
                )),
            ),
        ],
    );
    let result = eval_constant(&expr);
    assert_eq!(result, None);
}
#[test]
fn eval_const_or() {
    let expr = Expression::Or(
        Metadata::new(),
        vec![
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false))),
            Expression::Atomic(Metadata::new(), Atom::Literal(Literal::Bool(false))),
        ],
    );
    let result = eval_constant(&expr);
    assert_eq!(result, Some(Literal::Bool(false)));
}