Skip to main content

azul_layout/solver3/
calc.rs

1//! CSS `calc()` expression evaluator.
2//!
3//! This module implements a two-pass stack-machine evaluator for `calc()` expressions.
4//! It resolves `CalcAstItem` slices (flat, parenthesised AST) into a single `f32` pixel value.
5//!
6//! **Resolution context**: Em/rem units are resolved using per-node font sizes that are
7//! captured lazily during style translation and stored alongside the AST pointer passed
8//! to taffy. Percentages use the `basis` value provided by taffy (container width/height).
9
10use azul_css::props::{
11    basic::{
12        pixel::{DEFAULT_FONT_SIZE, PT_TO_PX},
13        PixelValue, SizeMetric,
14    },
15    layout::dimensions::{CalcAstItem, CalcAstItemVec},
16};
17
18/// Font-size context captured at style-translation time and stored alongside the calc AST.
19///
20/// Taffy's `resolve_calc_value` callback only receives `(*const (), f32)` — no node id.
21/// We therefore bundle the per-node font sizes into the heap-pinned data that the opaque
22/// pointer references, so the evaluator can resolve `em` / `rem` correctly.
23#[derive(Debug, Clone)]
24#[repr(C)]
25pub struct CalcResolveContext {
26    /// The calc AST items (flat stack-machine representation).
27    pub items: CalcAstItemVec,
28    /// Element's computed `font-size` in px — used for `em` resolution.
29    pub em_size: f32,
30    /// Root element's computed `font-size` in px — used for `rem` resolution.
31    pub rem_size: f32,
32}
33
34/// Internal intermediate representation: a number or an operator (after value resolution).
35#[derive(Clone, Debug)]
36enum CalcFlatItem {
37    Num(f32),
38    Op(CalcOp),
39}
40
41/// Arithmetic operators.
42#[derive(Clone, Copy, Debug, PartialEq)]
43enum CalcOp {
44    Add,
45    Sub,
46    Mul,
47    Div,
48}
49
50/// Evaluate a `CalcResolveContext` using the given `basis` (the "100 %" reference value,
51/// e.g. containing-block width for `width: calc(…)`).
52pub fn evaluate_calc(ctx: &CalcResolveContext, basis: f32) -> f32 {
53    evaluate_calc_ast(ctx.items.as_slice(), basis, ctx.em_size, ctx.rem_size)
54}
55
56/// Stack-machine evaluator for a flat `CalcAstItem` slice.
57///
58/// `basis`    — the "100 %" reference value (e.g. containing-block width).
59/// `em_size`  — element's computed font-size (for `em`).
60/// `rem_size` — root element's computed font-size (for `rem`).
61///
62/// Two-pass approach with correct operator precedence:
63///   Pass 1: evaluate `*` and `/`
64///   Pass 2: evaluate `+` and `-`
65/// Parenthesised sub-expressions are resolved recursively.
66pub fn evaluate_calc_ast(
67    items: &[CalcAstItem],
68    basis: f32,
69    em_size: f32,
70    rem_size: f32,
71) -> f32 {
72    // Convert into a working vec of resolved numbers and operators.
73    let mut flat: Vec<CalcFlatItem> = Vec::with_capacity(items.len());
74    let mut i = 0;
75    while i < items.len() {
76        match &items[i] {
77            CalcAstItem::Value(pv) => {
78                flat.push(CalcFlatItem::Num(resolve_pixel_value(
79                    pv, basis, em_size, rem_size,
80                )));
81            }
82            CalcAstItem::Add => flat.push(CalcFlatItem::Op(CalcOp::Add)),
83            CalcAstItem::Sub => flat.push(CalcFlatItem::Op(CalcOp::Sub)),
84            CalcAstItem::Mul => flat.push(CalcFlatItem::Op(CalcOp::Mul)),
85            CalcAstItem::Div => flat.push(CalcFlatItem::Op(CalcOp::Div)),
86            CalcAstItem::BraceOpen => {
87                // Find matching BraceClose and recurse
88                let start = i + 1;
89                let mut depth = 1u32;
90                let mut j = start;
91                while j < items.len() && depth > 0 {
92                    match &items[j] {
93                        CalcAstItem::BraceOpen => depth += 1,
94                        CalcAstItem::BraceClose => depth -= 1,
95                        _ => {}
96                    }
97                    if depth > 0 {
98                        j += 1;
99                    }
100                }
101                // items[start..j] is the inner sub-expression (excl. braces)
102                let sub_val = evaluate_calc_ast(&items[start..j], basis, em_size, rem_size);
103                flat.push(CalcFlatItem::Num(sub_val));
104                i = j; // skip past the closing brace
105            }
106            CalcAstItem::BraceClose => { /* shouldn't happen at top level */ }
107        }
108        i += 1;
109    }
110
111    // Pass 1: resolve * and /
112    let mut pass2: Vec<CalcFlatItem> = Vec::with_capacity(flat.len());
113    let mut k = 0;
114    while k < flat.len() {
115        if let CalcFlatItem::Op(op @ (CalcOp::Mul | CalcOp::Div)) = &flat[k] {
116            // Apply to previous Num in pass2 and next Num in flat
117            if let (Some(CalcFlatItem::Num(lhs)), Some(CalcFlatItem::Num(rhs))) =
118                (pass2.last(), flat.get(k + 1))
119            {
120                let result = match op {
121                    CalcOp::Mul => lhs * rhs,
122                    CalcOp::Div => {
123                        if *rhs != 0.0 {
124                            lhs / rhs
125                        } else {
126                            0.0
127                        }
128                    }
129                    _ => unreachable!(),
130                };
131                *pass2.last_mut().unwrap() = CalcFlatItem::Num(result);
132                k += 2; // skip operator + rhs
133                continue;
134            }
135        }
136        pass2.push(flat[k].clone());
137        k += 1;
138    }
139
140    // Pass 2: resolve + and -
141    let mut result = match pass2.first() {
142        Some(CalcFlatItem::Num(v)) => *v,
143        _ => return 0.0,
144    };
145    let mut m = 1;
146    while m < pass2.len() {
147        if let (CalcFlatItem::Op(op), Some(CalcFlatItem::Num(rhs))) =
148            (&pass2[m], pass2.get(m + 1))
149        {
150            match op {
151                CalcOp::Add => result += rhs,
152                CalcOp::Sub => result -= rhs,
153                _ => {} // already handled in pass 1
154            }
155            m += 2;
156        } else {
157            m += 1;
158        }
159    }
160
161    result
162}
163
164/// Resolve a single `PixelValue` to `f32` pixels inside a `calc()` expression.
165///
166/// - `basis`    — the "100 %" reference (containing-block width or height)
167/// - `em_size`  — element's computed font-size (for `em` units)
168/// - `rem_size` — root element's computed font-size (for `rem` units)
169pub fn resolve_pixel_value(
170    pv: &PixelValue,
171    basis: f32,
172    em_size: f32,
173    rem_size: f32,
174) -> f32 {
175    match pv.metric {
176        SizeMetric::Px => pv.number.get(),
177        SizeMetric::Pt => pv.number.get() * PT_TO_PX,
178        SizeMetric::In => pv.number.get() * 96.0,
179        SizeMetric::Cm => pv.number.get() * 96.0 / 2.54,
180        SizeMetric::Mm => pv.number.get() * 96.0 / 25.4,
181        SizeMetric::Em => pv.number.get() * em_size,
182        SizeMetric::Rem => pv.number.get() * rem_size,
183        SizeMetric::Percent => basis * (pv.number.get() / 100.0),
184        SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => {
185            // Viewport units: fallback — proper resolution requires viewport context
186            pv.number.get()
187        }
188    }
189}
190
191// ============================================================================
192// Unit tests
193// ============================================================================
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198    use azul_css::props::basic::{FloatValue, PixelValue, SizeMetric};
199
200    /// Helper: create a `PixelValue` from metric + number.
201    fn pv(metric: SizeMetric, number: f32) -> PixelValue {
202        PixelValue {
203            metric,
204            number: FloatValue::new(number),
205        }
206    }
207
208    /// Helper: wrap `CalcAstItem`s into a `CalcAstItemVec`.
209    fn items(v: Vec<CalcAstItem>) -> CalcAstItemVec {
210        v.into()
211    }
212
213    /// Helper: build a `CalcResolveContext` with default font sizes.
214    fn ctx(ast: Vec<CalcAstItem>) -> CalcResolveContext {
215        CalcResolveContext {
216            items: items(ast),
217            em_size: DEFAULT_FONT_SIZE,
218            rem_size: DEFAULT_FONT_SIZE,
219        }
220    }
221
222    /// Helper: build a `CalcResolveContext` with custom font sizes.
223    fn ctx_with_fonts(ast: Vec<CalcAstItem>, em: f32, rem: f32) -> CalcResolveContext {
224        CalcResolveContext {
225            items: items(ast),
226            em_size: em,
227            rem_size: rem,
228        }
229    }
230
231    // ------------------------------------------------------------------
232    // Basic value resolution
233    // ------------------------------------------------------------------
234
235    #[test]
236    fn single_px_value() {
237        // calc(100px)
238        let c = ctx(vec![CalcAstItem::Value(pv(SizeMetric::Px, 100.0))]);
239        assert_eq!(evaluate_calc(&c, 0.0), 100.0);
240    }
241
242    #[test]
243    fn single_percent_value() {
244        // calc(50%)  with basis=400 → 200
245        let c = ctx(vec![CalcAstItem::Value(pv(SizeMetric::Percent, 50.0))]);
246        assert_eq!(evaluate_calc(&c, 400.0), 200.0);
247    }
248
249    #[test]
250    fn single_pt_value() {
251        // calc(12pt) → 12 * 1.3333… = 16
252        let c = ctx(vec![CalcAstItem::Value(pv(SizeMetric::Pt, 12.0))]);
253        let result = evaluate_calc(&c, 0.0);
254        assert!((result - 16.0).abs() < 0.01);
255    }
256
257    // ------------------------------------------------------------------
258    // Addition / subtraction
259    // ------------------------------------------------------------------
260
261    #[test]
262    fn simple_addition() {
263        // calc(100px + 50px) = 150
264        let c = ctx(vec![
265            CalcAstItem::Value(pv(SizeMetric::Px, 100.0)),
266            CalcAstItem::Add,
267            CalcAstItem::Value(pv(SizeMetric::Px, 50.0)),
268        ]);
269        assert_eq!(evaluate_calc(&c, 0.0), 150.0);
270    }
271
272    #[test]
273    fn simple_subtraction() {
274        // calc(200px - 50px) = 150
275        let c = ctx(vec![
276            CalcAstItem::Value(pv(SizeMetric::Px, 200.0)),
277            CalcAstItem::Sub,
278            CalcAstItem::Value(pv(SizeMetric::Px, 50.0)),
279        ]);
280        assert_eq!(evaluate_calc(&c, 0.0), 150.0);
281    }
282
283    #[test]
284    fn percent_minus_px() {
285        // calc(100% - 20px) with basis=300 → 300 - 20 = 280
286        let c = ctx(vec![
287            CalcAstItem::Value(pv(SizeMetric::Percent, 100.0)),
288            CalcAstItem::Sub,
289            CalcAstItem::Value(pv(SizeMetric::Px, 20.0)),
290        ]);
291        assert_eq!(evaluate_calc(&c, 300.0), 280.0);
292    }
293
294    #[test]
295    fn thirds_calc() {
296        // calc(33.333% - 10px) with basis=900 → 300 - 10 = ~290
297        let c = ctx(vec![
298            CalcAstItem::Value(pv(SizeMetric::Percent, 33.333)),
299            CalcAstItem::Sub,
300            CalcAstItem::Value(pv(SizeMetric::Px, 10.0)),
301        ]);
302        let result = evaluate_calc(&c, 900.0);
303        assert!((result - 289.997).abs() < 0.01);
304    }
305
306    // ------------------------------------------------------------------
307    // Multiplication / division
308    // ------------------------------------------------------------------
309
310    #[test]
311    fn simple_multiplication() {
312        // calc(50px * 3) = 150
313        let c = ctx(vec![
314            CalcAstItem::Value(pv(SizeMetric::Px, 50.0)),
315            CalcAstItem::Mul,
316            CalcAstItem::Value(pv(SizeMetric::Px, 3.0)),
317        ]);
318        assert_eq!(evaluate_calc(&c, 0.0), 150.0);
319    }
320
321    #[test]
322    fn simple_division() {
323        // calc(300px / 4) = 75
324        let c = ctx(vec![
325            CalcAstItem::Value(pv(SizeMetric::Px, 300.0)),
326            CalcAstItem::Div,
327            CalcAstItem::Value(pv(SizeMetric::Px, 4.0)),
328        ]);
329        assert_eq!(evaluate_calc(&c, 0.0), 75.0);
330    }
331
332    #[test]
333    fn division_by_zero() {
334        // calc(100px / 0) → 0 (safe fallback)
335        let c = ctx(vec![
336            CalcAstItem::Value(pv(SizeMetric::Px, 100.0)),
337            CalcAstItem::Div,
338            CalcAstItem::Value(pv(SizeMetric::Px, 0.0)),
339        ]);
340        assert_eq!(evaluate_calc(&c, 0.0), 0.0);
341    }
342
343    // ------------------------------------------------------------------
344    // Operator precedence: * / before + -
345    // ------------------------------------------------------------------
346
347    #[test]
348    fn precedence_mul_before_add() {
349        // calc(10px + 5px * 3) = 10 + 15 = 25
350        let c = ctx(vec![
351            CalcAstItem::Value(pv(SizeMetric::Px, 10.0)),
352            CalcAstItem::Add,
353            CalcAstItem::Value(pv(SizeMetric::Px, 5.0)),
354            CalcAstItem::Mul,
355            CalcAstItem::Value(pv(SizeMetric::Px, 3.0)),
356        ]);
357        assert_eq!(evaluate_calc(&c, 0.0), 25.0);
358    }
359
360    #[test]
361    fn precedence_div_before_sub() {
362        // calc(100px - 60px / 3) = 100 - 20 = 80
363        let c = ctx(vec![
364            CalcAstItem::Value(pv(SizeMetric::Px, 100.0)),
365            CalcAstItem::Sub,
366            CalcAstItem::Value(pv(SizeMetric::Px, 60.0)),
367            CalcAstItem::Div,
368            CalcAstItem::Value(pv(SizeMetric::Px, 3.0)),
369        ]);
370        assert_eq!(evaluate_calc(&c, 0.0), 80.0);
371    }
372
373    #[test]
374    fn precedence_complex() {
375        // calc(2px * 3 + 4px * 5) = 6 + 20 = 26
376        let c = ctx(vec![
377            CalcAstItem::Value(pv(SizeMetric::Px, 2.0)),
378            CalcAstItem::Mul,
379            CalcAstItem::Value(pv(SizeMetric::Px, 3.0)),
380            CalcAstItem::Add,
381            CalcAstItem::Value(pv(SizeMetric::Px, 4.0)),
382            CalcAstItem::Mul,
383            CalcAstItem::Value(pv(SizeMetric::Px, 5.0)),
384        ]);
385        assert_eq!(evaluate_calc(&c, 0.0), 26.0);
386    }
387
388    // ------------------------------------------------------------------
389    // Parenthesised sub-expressions
390    // ------------------------------------------------------------------
391
392    #[test]
393    fn simple_parens() {
394        // calc((10px + 20px)) = 30
395        let c = ctx(vec![
396            CalcAstItem::BraceOpen,
397            CalcAstItem::Value(pv(SizeMetric::Px, 10.0)),
398            CalcAstItem::Add,
399            CalcAstItem::Value(pv(SizeMetric::Px, 20.0)),
400            CalcAstItem::BraceClose,
401        ]);
402        assert_eq!(evaluate_calc(&c, 0.0), 30.0);
403    }
404
405    #[test]
406    fn parens_override_precedence() {
407        // calc((10px + 5px) * 3) = 15 * 3 = 45
408        let c = ctx(vec![
409            CalcAstItem::BraceOpen,
410            CalcAstItem::Value(pv(SizeMetric::Px, 10.0)),
411            CalcAstItem::Add,
412            CalcAstItem::Value(pv(SizeMetric::Px, 5.0)),
413            CalcAstItem::BraceClose,
414            CalcAstItem::Mul,
415            CalcAstItem::Value(pv(SizeMetric::Px, 3.0)),
416        ]);
417        assert_eq!(evaluate_calc(&c, 0.0), 45.0);
418    }
419
420    #[test]
421    fn nested_parens() {
422        // calc(100px - (20px + (5px * 2)))
423        // inner: 5 * 2 = 10
424        // middle: 20 + 10 = 30
425        // outer: 100 - 30 = 70
426        let c = ctx(vec![
427            CalcAstItem::Value(pv(SizeMetric::Px, 100.0)),
428            CalcAstItem::Sub,
429            CalcAstItem::BraceOpen,
430            CalcAstItem::Value(pv(SizeMetric::Px, 20.0)),
431            CalcAstItem::Add,
432            CalcAstItem::BraceOpen,
433            CalcAstItem::Value(pv(SizeMetric::Px, 5.0)),
434            CalcAstItem::Mul,
435            CalcAstItem::Value(pv(SizeMetric::Px, 2.0)),
436            CalcAstItem::BraceClose,
437            CalcAstItem::BraceClose,
438        ]);
439        assert_eq!(evaluate_calc(&c, 0.0), 70.0);
440    }
441
442    // ------------------------------------------------------------------
443    // Em / rem resolution with node-local font sizes
444    // ------------------------------------------------------------------
445
446    #[test]
447    fn em_with_default_font_size() {
448        // calc(2em) with default em=16 → 32
449        let c = ctx(vec![CalcAstItem::Value(pv(SizeMetric::Em, 2.0))]);
450        assert_eq!(evaluate_calc(&c, 0.0), DEFAULT_FONT_SIZE * 2.0);
451    }
452
453    #[test]
454    fn em_with_custom_font_size() {
455        // calc(2em) with em=24 → 48
456        let c = ctx_with_fonts(
457            vec![CalcAstItem::Value(pv(SizeMetric::Em, 2.0))],
458            24.0,
459            16.0,
460        );
461        assert_eq!(evaluate_calc(&c, 0.0), 48.0);
462    }
463
464    #[test]
465    fn rem_with_custom_root_font_size() {
466        // calc(1.5rem) with rem=20 → 30
467        let c = ctx_with_fonts(
468            vec![CalcAstItem::Value(pv(SizeMetric::Rem, 1.5))],
469            16.0,
470            20.0,
471        );
472        assert_eq!(evaluate_calc(&c, 0.0), 30.0);
473    }
474
475    #[test]
476    fn em_and_rem_differ() {
477        // calc(1em + 1rem) with em=24, rem=20 → 24 + 20 = 44
478        let c = ctx_with_fonts(
479            vec![
480                CalcAstItem::Value(pv(SizeMetric::Em, 1.0)),
481                CalcAstItem::Add,
482                CalcAstItem::Value(pv(SizeMetric::Rem, 1.0)),
483            ],
484            24.0,
485            20.0,
486        );
487        assert_eq!(evaluate_calc(&c, 0.0), 44.0);
488    }
489
490    #[test]
491    fn em_percent_mixed() {
492        // calc(50% + 2em) with basis=200, em=12 → 100 + 24 = 124
493        let c = ctx_with_fonts(
494            vec![
495                CalcAstItem::Value(pv(SizeMetric::Percent, 50.0)),
496                CalcAstItem::Add,
497                CalcAstItem::Value(pv(SizeMetric::Em, 2.0)),
498            ],
499            12.0,
500            16.0,
501        );
502        assert_eq!(evaluate_calc(&c, 200.0), 124.0);
503    }
504
505    // ------------------------------------------------------------------
506    // Real-world calc expressions
507    // ------------------------------------------------------------------
508
509    #[test]
510    fn flexbox_three_column() {
511        // calc(33.333% - 10px) per column, basis=600 → 200 - 10 = 190
512        let c = ctx(vec![
513            CalcAstItem::Value(pv(SizeMetric::Percent, 33.333)),
514            CalcAstItem::Sub,
515            CalcAstItem::Value(pv(SizeMetric::Px, 10.0)),
516        ]);
517        let result = evaluate_calc(&c, 600.0);
518        // 33.333% of 600 = 199.998
519        assert!((result - 189.998).abs() < 0.01);
520    }
521
522    #[test]
523    fn sidebar_main_layout() {
524        // calc(100% - 250px) for a sidebar offset, basis=1024 → 774
525        let c = ctx(vec![
526            CalcAstItem::Value(pv(SizeMetric::Percent, 100.0)),
527            CalcAstItem::Sub,
528            CalcAstItem::Value(pv(SizeMetric::Px, 250.0)),
529        ]);
530        assert_eq!(evaluate_calc(&c, 1024.0), 774.0);
531    }
532
533    #[test]
534    fn responsive_padding() {
535        // calc(1rem + 2%) with rem=16, basis=800 → 16 + 16 = 32
536        let c = ctx_with_fonts(
537            vec![
538                CalcAstItem::Value(pv(SizeMetric::Rem, 1.0)),
539                CalcAstItem::Add,
540                CalcAstItem::Value(pv(SizeMetric::Percent, 2.0)),
541            ],
542            16.0,
543            16.0,
544        );
545        assert_eq!(evaluate_calc(&c, 800.0), 32.0);
546    }
547
548    // ------------------------------------------------------------------
549    // Edge cases
550    // ------------------------------------------------------------------
551
552    #[test]
553    fn empty_expression() {
554        let c = ctx(vec![]);
555        assert_eq!(evaluate_calc(&c, 100.0), 0.0);
556    }
557
558    #[test]
559    fn only_operator_no_values() {
560        let c = ctx(vec![CalcAstItem::Add]);
561        assert_eq!(evaluate_calc(&c, 100.0), 0.0);
562    }
563
564    #[test]
565    fn multiple_additions() {
566        // calc(10px + 20px + 30px) = 60
567        let c = ctx(vec![
568            CalcAstItem::Value(pv(SizeMetric::Px, 10.0)),
569            CalcAstItem::Add,
570            CalcAstItem::Value(pv(SizeMetric::Px, 20.0)),
571            CalcAstItem::Add,
572            CalcAstItem::Value(pv(SizeMetric::Px, 30.0)),
573        ]);
574        assert_eq!(evaluate_calc(&c, 0.0), 60.0);
575    }
576
577    #[test]
578    fn cm_unit() {
579        // calc(2.54cm) = 96px (1in = 2.54cm = 96px)
580        let c = ctx(vec![CalcAstItem::Value(pv(SizeMetric::Cm, 2.54))]);
581        let result = evaluate_calc(&c, 0.0);
582        assert!((result - 96.0).abs() < 0.01);
583    }
584
585    #[test]
586    fn mm_unit() {
587        // calc(25.4mm) = 96px
588        let c = ctx(vec![CalcAstItem::Value(pv(SizeMetric::Mm, 25.4))]);
589        let result = evaluate_calc(&c, 0.0);
590        assert!((result - 96.0).abs() < 0.01);
591    }
592
593    #[test]
594    fn in_unit() {
595        // calc(1in) = 96px
596        let c = ctx(vec![CalcAstItem::Value(pv(SizeMetric::In, 1.0))]);
597        assert_eq!(evaluate_calc(&c, 0.0), 96.0);
598    }
599
600    #[test]
601    fn chained_mul_div() {
602        // calc(100px * 2 / 4) = 200 / 4 = 50
603        let c = ctx(vec![
604            CalcAstItem::Value(pv(SizeMetric::Px, 100.0)),
605            CalcAstItem::Mul,
606            CalcAstItem::Value(pv(SizeMetric::Px, 2.0)),
607            CalcAstItem::Div,
608            CalcAstItem::Value(pv(SizeMetric::Px, 4.0)),
609        ]);
610        assert_eq!(evaluate_calc(&c, 0.0), 50.0);
611    }
612
613    // ------------------------------------------------------------------
614    // resolve_pixel_value directly
615    // ------------------------------------------------------------------
616
617    #[test]
618    fn resolve_px() {
619        assert_eq!(resolve_pixel_value(&pv(SizeMetric::Px, 42.0), 0.0, 16.0, 16.0), 42.0);
620    }
621
622    #[test]
623    fn resolve_percent() {
624        assert_eq!(resolve_pixel_value(&pv(SizeMetric::Percent, 25.0), 400.0, 16.0, 16.0), 100.0);
625    }
626
627    #[test]
628    fn resolve_em_custom() {
629        assert_eq!(resolve_pixel_value(&pv(SizeMetric::Em, 2.0), 0.0, 20.0, 16.0), 40.0);
630    }
631
632    #[test]
633    fn resolve_rem_custom() {
634        assert_eq!(resolve_pixel_value(&pv(SizeMetric::Rem, 2.0), 0.0, 20.0, 18.0), 36.0);
635    }
636}