1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/*
 * AquaVM Workflow Engine
 *
 * Copyright (C) 2024 Fluence DAO
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation version 3 of the
 * License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod values_sparse_matrix;

use crate::execution_step::errors_prelude::*;
use crate::execution_step::value_types::CanonStreamMapWithProvenance;
use crate::execution_step::value_types::CanonStreamWithProvenance;
use crate::execution_step::value_types::ScalarRef;
use crate::execution_step::ExecutionResult;
use crate::execution_step::FoldState;
use crate::execution_step::ValueAggregate;
use values_sparse_matrix::ValuesSparseMatrix;

use std::collections::HashMap;

// TODO: move this code snippet to documentation when it's ready

/// There are two scopes for variable scalars in AIR: global and local. A local scope
/// is a scope inside every fold block, other scope is a global. It means that scalar
/// in an upper fold block could be shadowed by a scalar with the same name in a lower
/// fold block, it works "as expected". Let's consider the following example:
/// (seq
///   (seq
///     (call ... local) ;; (1)
///     (fold iterable_1 iterator_1
///       (seq
///         (seq
///           (seq
///             (call ... local) ;; (2)
///             (fold iterable_2 iterator_2
///               (seq
///                 (seq
///                    (call ... local) ;; (3)
///                    (call ... [local]) ;; local set by (3) will be used
///                  )
///                  (next iterator_2)
///               )
///             )
///           )
///           (call ... [local]) ;; local set by (2) will be used
///         )
///         (next iterator_1)
///       )
///     )
///   )
///   (seq
///     (call ... [local]) ;; local set by (1) will be used
///     (call ... local) ;; error will be occurred because, it's impossible to set variable twice
///                      ;; in a global scope
///   )
/// )
///
/// Although there could be only one iterable value for a fold block, because of CRDT rules.
/// This struct is intended to provide abilities to work with scalars as it was described.
pub(crate) struct Scalars<'i> {
    // TODO: use Rc<String> to avoid copying
    /// Terminology used here (mainly to resolve concerns re difference between scalars and values):
    ///  - scalar is an AIR scalar, iterable and non iterable. A scalar is addressed by a name.
    ///  - value is concrete value assigned to scalar on certain depth
    ///  - scope is a variable scope where variable is visible. If we consider fold as a graph where
    ///     each next produces a new level, then scope is a level in this tree. Please note that it
    ///     includes variable defined after next instruction.
    ///  - depth is a count of seen scopes (or a depth in a tree met in the previous definition)
    ///
    /// Non iterable variables hash map could be recognized as a sparse matrix, where a row
    /// corresponds to a variable name and contains all its values were set with respect to a depth.
    /// A column corresponds to a depth and contains all values were set at current depth.
    ///
    /// This matrix follows these invariants:
    ///   - all rows are non empty
    ///   - global variables have 0 depth
    ///   - cells in a row are sorted by depth
    ///   - all depths in cell in one row are unique
    pub(crate) non_iterable_variables: ValuesSparseMatrix<ValueAggregate>,

    pub(crate) canon_streams: ValuesSparseMatrix<CanonStreamWithProvenance>,

    pub(crate) canon_maps: ValuesSparseMatrix<CanonStreamMapWithProvenance>,

    pub(crate) iterable_variables: HashMap<String, FoldState<'i>>,
}

impl<'i> Scalars<'i> {
    pub fn new() -> Self {
        Self {
            non_iterable_variables: ValuesSparseMatrix::new(),
            canon_streams: ValuesSparseMatrix::new(),
            canon_maps: ValuesSparseMatrix::new(),
            iterable_variables: HashMap::new(),
        }
    }

    /// Returns true if there was a previous value for the provided key on the same
    /// fold block.
    pub(crate) fn set_scalar_value(&mut self, name: impl Into<String>, value: ValueAggregate) -> ExecutionResult<bool> {
        self.non_iterable_variables.set_value(name, value)
    }

    /// Returns true if there was a previous value for the provided key on the same
    /// fold block.
    pub(crate) fn set_canon_value(
        &mut self,
        name: impl Into<String>,
        value: CanonStreamWithProvenance,
    ) -> ExecutionResult<bool> {
        self.canon_streams.set_value(name, value)
    }

    /// Returns true if there was a previous value for the provided key on the same
    /// fold block.
    pub(crate) fn set_canon_map_value(
        &mut self,
        name: impl Into<String>,
        value: CanonStreamMapWithProvenance,
    ) -> ExecutionResult<bool> {
        self.canon_maps.set_value(name, value)
    }

    pub(crate) fn set_iterable_value(
        &mut self,
        name: impl Into<String>,
        fold_state: FoldState<'i>,
    ) -> ExecutionResult<()> {
        use std::collections::hash_map::Entry::{Occupied, Vacant};

        match self.iterable_variables.entry(name.into()) {
            Vacant(entry) => {
                entry.insert(fold_state);
                Ok(())
            }
            Occupied(entry) => Err(UncatchableError::MultipleIterableValues(entry.key().clone()).into()),
        }
    }

    pub(crate) fn remove_iterable_value(&mut self, name: &str) {
        self.iterable_variables.remove(name);
    }

    pub(crate) fn get_non_iterable_scalar(&'i self, name: &str) -> ExecutionResult<Option<&'i ValueAggregate>> {
        self.non_iterable_variables.get_value(name)
    }

    pub(crate) fn get_iterable(&mut self, name: &str) -> ExecutionResult<&FoldState<'i>> {
        self.iterable_variables
            .get(name)
            .ok_or_else(|| UncatchableError::FoldStateNotFound(name.to_string()).into())
    }

    pub(crate) fn get_iterable_mut(&mut self, name: &str) -> ExecutionResult<&mut FoldState<'i>> {
        self.iterable_variables
            .get_mut(name)
            .ok_or_else(|| UncatchableError::FoldStateNotFound(name.to_string()).into())
    }

    pub(crate) fn get_canon_stream(&'i self, name: &str) -> ExecutionResult<&'i CanonStreamWithProvenance> {
        self.canon_streams
            .get_value(name)?
            .ok_or_else(|| CatchableError::VariableWasNotInitializedAfterNew(name.to_string()).into())
    }

    pub(crate) fn get_canon_map(&'i self, name: &str) -> ExecutionResult<&'i CanonStreamMapWithProvenance> {
        self.canon_maps
            .get_value(name)?
            .ok_or_else(|| CatchableError::VariableWasNotInitializedAfterNew(name.to_string()).into())
    }

    pub(crate) fn get_value(&'i self, name: &str) -> ExecutionResult<ScalarRef<'i>> {
        let value = self.get_non_iterable_scalar(name);
        let iterable_value_with_prov = self.iterable_variables.get(name);

        match (value, iterable_value_with_prov) {
            (Err(_), None) => Err(CatchableError::VariableNotFound(name.to_string()).into()),
            (Ok(None), _) => Err(CatchableError::VariableWasNotInitializedAfterNew(name.to_string()).into()),
            (Ok(Some(value)), None) => Ok(ScalarRef::Value(value)),
            (Err(_), Some(iterable_value)) => Ok(ScalarRef::IterableValue(iterable_value)),
            (Ok(_), Some(_)) => unreachable!("this is checked on the parsing stage"),
        }
    }

    pub(crate) fn variable_could_be_set(&self, variable_name: &str) -> bool {
        self.non_iterable_variables.variable_could_be_set(variable_name)
            || self.canon_streams.variable_could_be_set(variable_name)
    }

    pub(crate) fn meet_fold_start(&mut self) {
        self.non_iterable_variables.meet_fold_start();
        self.canon_streams.meet_fold_start();
        self.canon_maps.meet_fold_start();
    }

    // meet next before recursion
    pub(crate) fn meet_next_before(&mut self) {
        self.non_iterable_variables.meet_next_before();
        self.canon_streams.meet_next_before();
        self.canon_maps.meet_next_before();
    }

    // meet next after recursion
    pub(crate) fn meet_next_after(&mut self) {
        self.non_iterable_variables.meet_next_after();
        self.canon_streams.meet_next_after();
        self.canon_maps.meet_next_after();
    }

    pub(crate) fn meet_fold_end(&mut self) {
        self.non_iterable_variables.meet_fold_end();
        self.canon_streams.meet_fold_end();
        self.canon_maps.meet_fold_end();
    }

    pub(crate) fn meet_new_start_scalar(&mut self, scalar_name: String) {
        self.non_iterable_variables.meet_new_start(scalar_name);
    }

    pub(crate) fn meet_new_start_canon_stream(&mut self, canon_stream_name: String) {
        self.canon_streams.meet_new_start(canon_stream_name);
    }

    pub(crate) fn meet_new_start_canon_stream_map(&mut self, canon_stream_map_name: String) {
        self.canon_maps.meet_new_start(canon_stream_map_name);
    }

    pub(crate) fn meet_new_end_scalar(&mut self, scalar_name: &str) -> ExecutionResult<()> {
        self.non_iterable_variables.meet_new_end(scalar_name)
    }

    pub(crate) fn meet_new_end_canon_stream(&mut self, canon_name: &str) -> ExecutionResult<()> {
        self.canon_streams.meet_new_end(canon_name)
    }

    pub(crate) fn meet_new_end_canon_stream_map(&mut self, canon_stream_map_name: &str) -> ExecutionResult<()> {
        self.canon_maps.meet_new_end(canon_stream_map_name)
    }
}

impl Default for Scalars<'_> {
    fn default() -> Self {
        Scalars::new()
    }
}

use std::fmt;

impl<'i> fmt::Display for Scalars<'i> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "scalars:\n{}", self.non_iterable_variables)?;
        writeln!(f, "canon_streams:\n{}", self.canon_streams)?;

        for (name, _) in self.iterable_variables.iter() {
            // it's impossible to print an iterable value for now
            writeln!(f, "{name} => iterable")?;
        }

        Ok(())
    }
}