logisheets_controller/calc_engine/calculator/infix/
range.rs

1use logisheets_parser::ast;
2
3use super::super::calc_vertex::{CalcReference, CalcVertex, ColRange, Reference, RowRange};
4use logisheets_base::Addr as Address;
5
6pub fn get_range(lhs: CalcVertex, rhs: CalcVertex) -> CalcVertex {
7    match (lhs, rhs) {
8        (CalcVertex::Reference(lhs_ref), CalcVertex::Reference(rhs_ref)) => {
9            let lhs_prefix = lhs_ref.sheet;
10            let rhs_prefix = rhs_ref.sheet;
11            if lhs_prefix != rhs_prefix {
12                return CalcVertex::from_error(ast::Error::Value);
13            }
14            let result = get_range_without_prefix(lhs_ref.reference, rhs_ref.reference);
15            match result {
16                Some(r) => CalcVertex::Reference(CalcReference {
17                    from_sheet: None,
18                    sheet: lhs_prefix,
19                    reference: r,
20                }),
21                None => CalcVertex::from_error(ast::Error::Value),
22            }
23        }
24        _ => CalcVertex::from_error(ast::Error::Value),
25    }
26}
27
28pub fn get_range_without_prefix(l_ref: Reference, r_ref: Reference) -> Option<Reference> {
29    match (l_ref, r_ref) {
30        (Reference::Addr(la), Reference::Addr(ra)) => get_range_of_addresses(la, ra),
31        (Reference::Addr(addr), Reference::ColumnRange(cr)) => {
32            get_range_of_addr_and_col_range(addr, cr)
33        }
34        (Reference::Addr(addr), Reference::RowRange(rr)) => {
35            get_range_of_addr_and_row_range(addr, rr)
36        }
37        (Reference::Addr(addr), Reference::Range(start, end)) => {
38            get_range_of_addr_and_range(addr, start, end)
39        }
40        (Reference::ColumnRange(cr), Reference::Addr(addr)) => {
41            get_range_of_addr_and_col_range(addr, cr)
42        }
43        (Reference::ColumnRange(lcr), Reference::ColumnRange(rcr)) => {
44            get_range_of_col_ranges(lcr, rcr)
45        }
46        (Reference::ColumnRange(cr), Reference::RowRange(rr)) => {
47            get_range_of_col_range_and_row_range(cr, rr)
48        }
49        (Reference::ColumnRange(cr), Reference::Range(start, end)) => {
50            get_range_of_range_and_col_range(start, end, cr)
51        }
52        (Reference::RowRange(rr), Reference::Addr(addr)) => {
53            get_range_of_addr_and_row_range(addr, rr)
54        }
55        (Reference::RowRange(rr), Reference::ColumnRange(cr)) => {
56            get_range_of_col_range_and_row_range(cr, rr)
57        }
58        (Reference::RowRange(lrr), Reference::RowRange(rrr)) => get_range_of_row_ranges(lrr, rrr),
59        (Reference::RowRange(rr), Reference::Range(start, end)) => {
60            get_range_of_range_and_row_range(start, end, rr)
61        }
62        (Reference::Range(start, end), Reference::Addr(addr)) => {
63            get_range_of_addr_and_range(addr, start, end)
64        }
65        (Reference::Range(start, end), Reference::ColumnRange(cr)) => {
66            get_range_of_range_and_col_range(start, end, cr)
67        }
68        (Reference::Range(start, end), Reference::RowRange(rr)) => {
69            get_range_of_range_and_row_range(start, end, rr)
70        }
71        (Reference::Range(left_start, left_end), Reference::Range(right_start, right_end)) => {
72            get_range_of_ranges(left_start, left_end, right_start, right_end)
73        }
74    }
75}
76
77fn get_range_of_addresses(la: Address, ra: Address) -> Option<Reference> {
78    Some(Reference::Range(la, ra))
79}
80
81fn get_range_of_ranges(
82    lr_start: Address,
83    lr_end: Address,
84    rr_start: Address,
85    rr_end: Address,
86) -> Option<Reference> {
87    let (col_start, col_end) =
88        get_range_of_intervals((lr_start.col, lr_end.col), (rr_start.col, rr_end.col));
89    let (row_start, row_end) =
90        get_range_of_intervals((lr_start.row, lr_end.row), (rr_start.row, rr_end.row));
91    Some(Reference::Range(
92        Address {
93            col: col_start,
94            row: row_start,
95        },
96        Address {
97            col: col_end,
98            row: row_end,
99        },
100    ))
101}
102
103fn get_range_of_col_ranges(lcr: ColRange, rcr: ColRange) -> Option<Reference> {
104    let (start, end) = get_range_of_intervals((lcr.start, lcr.end), (rcr.start, rcr.end));
105    Some(Reference::ColumnRange(ColRange { start, end }))
106}
107
108fn get_range_of_row_ranges(lrr: RowRange, rrr: RowRange) -> Option<Reference> {
109    let (start, end) = get_range_of_intervals((lrr.start, lrr.end), (rrr.start, rrr.end));
110    Some(Reference::RowRange(RowRange { start, end }))
111}
112
113fn get_range_of_addr_and_range(
114    addr: Address,
115    range_start: Address,
116    range_end: Address,
117) -> Option<Reference> {
118    let (col_start, col_end) =
119        get_range_of_point_and_interval(addr.col, (range_start.col, range_end.col));
120    let (row_start, row_end) =
121        get_range_of_point_and_interval(addr.row, (range_start.row, range_end.row));
122    Some(Reference::Range(
123        Address {
124            col: col_start,
125            row: row_start,
126        },
127        Address {
128            col: col_end,
129            row: row_end,
130        },
131    ))
132}
133
134fn get_range_of_addr_and_col_range(addr: Address, cr: ColRange) -> Option<Reference> {
135    let (start, end) = get_range_of_point_and_interval(addr.col, (cr.start, cr.end));
136    Some(Reference::ColumnRange(ColRange { start, end }))
137}
138
139fn get_range_of_addr_and_row_range(addr: Address, rr: RowRange) -> Option<Reference> {
140    let (start, end) = get_range_of_point_and_interval(addr.row, (rr.start, rr.end));
141    Some(Reference::RowRange(RowRange { start, end }))
142}
143
144fn get_range_of_range_and_col_range(
145    range_start: Address,
146    range_end: Address,
147    cr: ColRange,
148) -> Option<Reference> {
149    let (start, end) = get_range_of_intervals((range_start.col, range_end.col), (cr.start, cr.end));
150    Some(Reference::ColumnRange(ColRange { start, end }))
151}
152fn get_range_of_range_and_row_range(
153    range_start: Address,
154    range_end: Address,
155    rr: RowRange,
156) -> Option<Reference> {
157    let (start, end) = get_range_of_intervals((range_start.row, range_end.row), (rr.start, rr.end));
158    Some(Reference::RowRange(RowRange { start, end }))
159}
160
161fn get_range_of_col_range_and_row_range(_cr: ColRange, _rr: RowRange) -> Option<Reference> {
162    None
163}
164
165#[inline]
166fn get_range_of_intervals(
167    l_interval: (usize, usize),
168    r_interval: (usize, usize),
169) -> (usize, usize) {
170    let l_ordered = order(l_interval.0, l_interval.1);
171    let r_ordered = order(r_interval.0, r_interval.1);
172    (
173        order(l_ordered.0, r_ordered.0).0,
174        order(l_ordered.1, r_ordered.1).1,
175    )
176}
177
178#[inline]
179fn get_range_of_point_and_interval(p: usize, interval: (usize, usize)) -> (usize, usize) {
180    let ordered = order(interval.0, interval.1);
181    (order(p, ordered.0).0, order(p, ordered.1).1)
182}
183
184#[inline]
185fn order(l: usize, r: usize) -> (usize, usize) {
186    if l < r {
187        (l, r)
188    } else {
189        (r, l)
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::super::{CalcValue, Value};
196    use super::{
197        ast, get_range, get_range_without_prefix, Address, CalcReference, CalcVertex, ColRange,
198        Reference, RowRange,
199    };
200
201    #[test]
202    fn addr_test() {
203        let addr = Reference::Addr(Address { row: 2, col: 3 });
204
205        let r = get_range_without_prefix(addr.clone(), Reference::Addr(Address { row: 4, col: 1 }));
206        assert!(matches!(
207            r,
208            Some(Reference::Range(
209                Address { row: 2, col: 3 },
210                Address { row: 4, col: 1 }
211            )),
212        ));
213
214        let r = get_range_without_prefix(
215            addr.clone(),
216            Reference::Range(Address { row: 4, col: 2 }, Address { row: 6, col: 5 }),
217        );
218        assert!(matches!(
219            r,
220            Some(Reference::Range(
221                Address { row: 2, col: 2 },
222                Address { row: 6, col: 5 }
223            )),
224        ));
225
226        let r = get_range_without_prefix(
227            addr.clone(),
228            Reference::ColumnRange(ColRange { start: 1, end: 3 }),
229        );
230        assert!(matches!(
231            r,
232            Some(Reference::ColumnRange(ColRange { start: 1, end: 3 })),
233        ));
234
235        let r = get_range_without_prefix(
236            addr.clone(),
237            Reference::RowRange(RowRange { start: 5, end: 6 }),
238        );
239        assert!(matches!(
240            r,
241            Some(Reference::RowRange(RowRange { start: 2, end: 6 })),
242        ));
243    }
244
245    #[test]
246    fn range_test() {
247        let range = Reference::Range(Address { row: 6, col: 3 }, Address { row: 4, col: 6 });
248
249        let r = get_range_without_prefix(
250            range.clone(),
251            Reference::Range(Address { row: 3, col: 2 }, Address { row: 5, col: 8 }),
252        );
253        assert!(matches!(
254            r,
255            Some(Reference::Range(
256                Address { row: 3, col: 2 },
257                Address { row: 6, col: 8 }
258            )),
259        ));
260
261        let r = get_range_without_prefix(
262            range.clone(),
263            Reference::ColumnRange(ColRange { start: 1, end: 3 }),
264        );
265        assert!(matches!(
266            r,
267            Some(Reference::ColumnRange(ColRange { start: 1, end: 6 })),
268        ));
269
270        let r = get_range_without_prefix(
271            Reference::RowRange(RowRange { start: 8, end: 9 }),
272            range.clone(),
273        );
274        assert!(matches!(
275            r,
276            Some(Reference::RowRange(RowRange { start: 4, end: 9 })),
277        ));
278    }
279
280    #[test]
281    fn col_range_test() {
282        let cr = Reference::ColumnRange(ColRange { start: 3, end: 6 });
283
284        let r = get_range_without_prefix(
285            cr.clone(),
286            Reference::ColumnRange(ColRange { start: 1, end: 4 }),
287        );
288        assert!(matches!(
289            r,
290            Some(Reference::ColumnRange(ColRange { start: 1, end: 6 })),
291        ));
292
293        let r = get_range_without_prefix(
294            Reference::RowRange(RowRange { start: 8, end: 9 }),
295            cr.clone(),
296        );
297        assert!(matches!(r, None));
298    }
299
300    #[test]
301    fn row_range_test() {
302        let cr = Reference::RowRange(RowRange { start: 3, end: 6 });
303
304        let r = get_range_without_prefix(
305            Reference::RowRange(RowRange { start: 1, end: 7 }),
306            cr.clone(),
307        );
308        assert!(matches!(
309            r,
310            Some(Reference::RowRange(RowRange { start: 1, end: 7 })),
311        ));
312    }
313    #[test]
314    fn prefix_test() {
315        let cv1 = CalcVertex::Reference(CalcReference {
316            from_sheet: None,
317            sheet: 1,
318            reference: Reference::Addr(Address { row: 1, col: 1 }),
319        });
320        let cv2 = CalcVertex::Reference(CalcReference {
321            from_sheet: None,
322            sheet: 1,
323            reference: Reference::Addr(Address { row: 2, col: 2 }),
324        });
325        let r = get_range(cv1, cv2);
326        assert!(matches!(
327            r,
328            CalcVertex::Reference(CalcReference {
329                from_sheet: None,
330                sheet: 1,
331                reference: Reference::Range(Address { row: 1, col: 1 }, Address { row: 2, col: 2 },)
332            })
333        ));
334
335        let cv1 = CalcVertex::Reference(CalcReference {
336            from_sheet: None,
337            sheet: 1,
338            reference: Reference::Addr(Address { row: 1, col: 1 }),
339        });
340        let cv2 = CalcVertex::Reference(CalcReference {
341            from_sheet: None,
342            sheet: 2,
343            reference: Reference::Addr(Address { row: 1, col: 1 }),
344        });
345        let r = get_range(cv1, cv2);
346        assert!(matches!(
347            r,
348            CalcVertex::Value(CalcValue::Scalar(Value::Error(ast::Error::Value))),
349        ))
350    }
351}