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
116
pub fn instantiate_model(problem_model: Model, param_model: Model) -> anyhow::Result<Model> {
13
116
    let symbol_table = problem_model.symbols_ptr_unchecked().write();
14
116
    let param_table = param_model.symbols_ptr_unchecked().write();
15
116
    let mut pending_givens = symbol_table
16
116
        .iter_local()
17
528
        .filter_map(|(name, decl)| decl.as_given().map(|_| name.clone()))
18
116
        .collect::<Vec<_>>();
19

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

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

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

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

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

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

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

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

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

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

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

            
79
22
        pending_givens = next_pending;
80
    }
81

            
82
68
    drop(symbol_table);
83
68
    Ok(problem_model)
84
116
}