1
use crate::{
2
    Model,
3
    ast::{DeclarationKind, DeclarationPtr, declaration::Declaration, eval_constant},
4
};
5
use anyhow::anyhow;
6

            
7
/// Instantiate a problem model with values from a parameter model.
8
///
9
/// For each `given` declaration in `problem_model`, this looks for a corresponding value `letting`
10
/// in `param_model`, checks it is a constant and within the given domain, and replaces the `given`
11
/// with a value-letting in the returned model.
12
152
pub fn instantiate_model(problem_model: Model, param_model: Model) -> anyhow::Result<Model> {
13
152
    let symbol_table = problem_model.symbols_ptr_unchecked().write();
14
152
    let param_table = param_model.symbols_ptr_unchecked().write();
15
152
    let mut pending_givens = symbol_table
16
152
        .iter_local()
17
896
        .filter_map(|(name, decl)| decl.as_given().map(|_| name.clone()))
18
152
        .collect::<Vec<_>>();
19

            
20
152
    while !pending_givens.is_empty() {
21
152
        let mut next_pending = Vec::new();
22
152
        let mut made_progress = false;
23

            
24
476
        for name in pending_givens {
25
476
            let mut decl = symbol_table
26
476
                .lookup_local(&name)
27
476
                .ok_or_else(|| anyhow!("Given declaration `{name}` not found in problem model"))?;
28

            
29
476
            let Some(domain) = decl.as_given() else {
30
                continue;
31
            };
32

            
33
476
            let param_decl = param_table.lookup(&name);
34
476
            let expr = param_decl
35
476
                .as_ref()
36
476
                .and_then(DeclarationPtr::as_value_letting)
37
476
                .ok_or_else(|| {
38
48
                    anyhow!(
39
                        "Given declaration `{name}` does not have corresponding letting in parameter file"
40
                    )
41
48
                })?;
42

            
43
428
            let expr_value = eval_constant(&expr)
44
428
                .ok_or_else(|| anyhow!("Letting expression `{expr}` cannot be evaluated"))?;
45

            
46
428
            let Ok(ground_domain) = domain.resolve() else {
47
                next_pending.push(name);
48
                continue;
49
            };
50

            
51
428
            if !ground_domain.contains(&expr_value)? {
52
8
                return Err(anyhow!(
53
8
                    "Domain of given statement `{name}` does not contain letting value"
54
8
                ));
55
420
            }
56

            
57
420
            let new_decl = Declaration::new(
58
420
                name.clone(),
59
420
                DeclarationKind::ValueLetting(expr.clone(), Some(domain.clone())),
60
            );
61
420
            drop(domain);
62
420
            decl.replace(new_decl);
63
420
            made_progress = true;
64

            
65
420
            tracing::info!("Replaced {name} given with letting.");
66
        }
67

            
68
96
        if next_pending.is_empty() {
69
96
            break;
70
        }
71

            
72
        if !made_progress {
73
            return Err(anyhow!(
74
                "Domain of given statement `{}` cannot be resolved",
75
                next_pending[0]
76
            ));
77
        }
78

            
79
        pending_givens = next_pending;
80
    }
81

            
82
96
    drop(symbol_table);
83
96
    Ok(problem_model)
84
152
}