Skip to main content

microcad_lang/value/
mod.rs

1// Copyright © 2024-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Evaluation entities.
5//!
6//! Every evaluation of any *symbol* leads to a [`Value`] which then might continued
7//! to process or ends up as the overall evaluation result.
8
9mod argument_value;
10mod argument_value_list;
11mod array;
12mod matrix;
13mod parameter_value;
14mod parameter_value_list;
15mod quantity;
16mod tuple;
17mod value_access;
18mod value_error;
19mod value_list;
20
21pub use argument_value::*;
22pub use argument_value_list::*;
23pub use array::*;
24use derive_more::From;
25pub use matrix::*;
26pub use parameter_value::*;
27pub use parameter_value_list::*;
28pub use quantity::*;
29pub use tuple::*;
30pub use value_access::*;
31pub use value_error::*;
32pub use value_list::*;
33
34use crate::{model::*, syntax::*, ty::*};
35use microcad_core::*;
36use microcad_lang_base::{SrcRef, invalid, invalid_no_ansi};
37
38pub(crate) type ValueResult<Type = Value> = std::result::Result<Type, ValueError>;
39
40/// A variant value with attached source code reference.
41#[derive(Clone, Default, PartialEq, From)]
42pub enum Value {
43    /// Invalid value (used for error handling).
44    #[default]
45    None,
46    /// A quantity value.
47    Quantity(Quantity),
48    /// A boolean value.
49    Bool(bool),
50    /// An integer value.
51    Integer(Integer),
52    /// A string value.
53    String(String),
54    /// A list of values with a common type.
55    Array(Array),
56    /// A tuple of named items.
57    Tuple(Box<Tuple>),
58    /// A matrix.
59    Matrix(Box<Matrix>),
60    /// A model in the model tree.
61    Model(Model),
62    /// Return value
63    Return(Box<Value>),
64}
65
66impl Value {
67    /// Check if the value is invalid.
68    pub fn is_invalid(&self) -> bool {
69        matches!(self, Value::None)
70    }
71
72    /// Calculate the power of two values, if possible.
73    pub fn pow(&self, rhs: &Value) -> ValueResult {
74        match (&self, rhs) {
75            (Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity(lhs.pow(rhs))),
76            (Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity(lhs.pow_int(rhs))),
77            (Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs.pow(*rhs as u32))),
78            _ => Err(ValueError::InvalidOperator("^".to_string())),
79        }
80    }
81
82    /// Binary operation
83    pub fn binary_op(lhs: Value, rhs: Value, op: &str) -> ValueResult {
84        match op {
85            "+" => lhs + rhs,
86            "-" => lhs - rhs,
87            "*" => lhs * rhs,
88            "/" => lhs / rhs,
89            "^" => lhs.pow(&rhs),
90            "&" => lhs & rhs,
91            "|" => lhs | rhs,
92            ">" => Ok(Value::Bool(lhs > rhs)),
93            "<" => Ok(Value::Bool(lhs < rhs)),
94            "≤" => Ok(Value::Bool(lhs <= rhs)),
95            "≥" => Ok(Value::Bool(lhs >= rhs)),
96            "~" => todo!("implement near ~="),
97            "==" => Ok(Value::Bool(lhs == rhs)),
98            "!=" => Ok(Value::Bool(lhs != rhs)),
99            _ => unimplemented!("{op:?}"),
100        }
101    }
102
103    /// Unary operation.
104    pub fn unary_op(self, op: &str) -> ValueResult {
105        match op {
106            "-" => -self,
107            "!" => !self,
108            _ => Err(ValueError::InvalidOperator(op.to_string())),
109        }
110    }
111
112    /// Try to get boolean value.
113    ///
114    /// A `Value::None` will return false.
115    pub fn try_bool(&self) -> Result<bool, ValueError> {
116        match self {
117            Value::Bool(b) => Ok(*b),
118            Value::None => Ok(false),
119            value => Err(ValueError::CannotConvertToBool(value.to_string())),
120        }
121    }
122
123    /// Try to convert to [`String`].
124    pub fn try_string(&self) -> Result<String, ValueError> {
125        match self {
126            Value::String(s) => return Ok(s.clone()),
127            Value::Integer(i) => return Ok(i.to_string()),
128            _ => {}
129        }
130
131        Err(ValueError::CannotConvert(self.to_string(), "String".into()))
132    }
133
134    /// Try to convert to [`Scalar`].
135    pub fn try_scalar(&self) -> Result<Scalar, ValueError> {
136        match self {
137            Value::Quantity(q) => return Ok(q.value),
138            Value::Integer(i) => return Ok((*i) as f64),
139            _ => {}
140        }
141
142        Err(ValueError::CannotConvert(self.to_string(), "Scalar".into()))
143    }
144
145    /// Unpack any Value::Return(..)
146    pub fn un_return(&self) -> Value {
147        match self {
148            Value::Return(value) => value.as_ref().clone(),
149            value => value.clone(),
150        }
151    }
152}
153
154impl PartialOrd for Value {
155    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
156        match (self, other) {
157            // integer type
158            (Value::Integer(lhs), Value::Integer(rhs)) => lhs.partial_cmp(rhs),
159            (Value::Quantity(lhs), Value::Quantity(rhs)) => lhs.partial_cmp(rhs),
160            (
161                Value::Quantity(Quantity {
162                    value,
163                    quantity_type: QuantityType::Scalar,
164                }),
165                Value::Integer(rhs),
166            ) => value.partial_cmp(&(*rhs as Scalar)),
167            _ => {
168                log::warn!("unhandled type mismatch between {self} and {other}");
169                None
170            }
171        }
172    }
173}
174
175impl crate::ty::Ty for Value {
176    fn ty(&self) -> Type {
177        match self {
178            Value::None => Type::Invalid,
179            Value::Integer(_) => Type::Integer,
180            Value::Quantity(q) => q.ty(),
181            Value::Bool(_) => Type::Bool,
182            Value::String(_) => Type::String,
183            Value::Array(list) => list.ty(),
184            Value::Tuple(tuple) => tuple.ty(),
185            Value::Matrix(matrix) => matrix.ty(),
186            Value::Model(_) => Type::Model,
187            Value::Return(r) => r.ty(),
188        }
189    }
190}
191
192impl std::ops::Neg for Value {
193    type Output = ValueResult;
194
195    fn neg(self) -> Self::Output {
196        match self {
197            Value::Integer(n) => Ok(Value::Integer(-n)),
198            Value::Quantity(q) => Ok(Value::Quantity(q.neg())),
199            Value::Array(a) => -a,
200            Value::Tuple(t) => -t.as_ref().clone(),
201            _ => Err(ValueError::InvalidOperator("-".into())),
202        }
203    }
204}
205
206impl std::ops::Not for Value {
207    type Output = ValueResult;
208
209    fn not(self) -> Self::Output {
210        match self {
211            Value::Bool(b) => Ok(Value::Bool(!b)),
212            Value::Array(a) => !a,
213            Value::Tuple(t) => !t.as_ref().clone(),
214            _ => Err(ValueError::InvalidOperator("!".into())),
215        }
216    }
217}
218
219/// Rules for operator `+`.
220impl std::ops::Add for Value {
221    type Output = ValueResult;
222
223    fn add(self, rhs: Self) -> Self::Output {
224        match (self, rhs) {
225            // Add two integers
226            (Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs + rhs)),
227            // Add a quantity to an integer
228            (Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs + rhs)?)),
229            // Add an integer to a quantity
230            (Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs + rhs)?)),
231            // Add two scalars
232            (Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs + rhs)?)),
233            // Concatenate two strings
234            (Value::String(lhs), Value::String(rhs)) => Ok(Value::String(lhs + &rhs)),
235            // Concatenate two lists
236            (Value::Array(lhs), Value::Array(rhs)) => {
237                if lhs.ty() != rhs.ty() {
238                    return Err(ValueError::CannotCombineVecOfDifferentType(
239                        lhs.ty(),
240                        rhs.ty(),
241                    ));
242                }
243
244                Ok(Value::Array(Array::from_values(
245                    lhs.iter().chain(rhs.iter()).cloned().collect(),
246                    lhs.ty(),
247                )))
248            }
249            // Add a value to an array.
250            (Value::Array(lhs), rhs) => Ok((lhs + rhs)?),
251            // Add two tuples of the same type: (x = 1., y = 2.) + (x = 3., y = 4.)
252            (Value::Tuple(lhs), Value::Tuple(rhs)) => Ok((*lhs + *rhs)?.into()),
253            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} + {rhs}"))),
254        }
255    }
256}
257
258/// Rules for operator `-`.
259impl std::ops::Sub for Value {
260    type Output = ValueResult;
261
262    fn sub(self, rhs: Self) -> Self::Output {
263        match (self, rhs) {
264            // Subtract two integers
265            (Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs - rhs)),
266            // Subtract an scalar and an integer
267            (Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs - rhs)?)),
268            // Subtract an integer and a scalar
269            (Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs - rhs)?)),
270            // Subtract two numbers
271            (Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs - rhs)?)),
272            // Subtract value to an array: `[1,2,3] - 1 = [0,1,2]`.
273            (Value::Array(lhs), rhs) => Ok((lhs - rhs)?),
274            // Subtract two tuples of the same type: (x = 1., y = 2.) - (x = 3., y = 4.)
275            (Value::Tuple(lhs), Value::Tuple(rhs)) => Ok((*lhs - *rhs)?.into()),
276
277            // Boolean difference operator for models
278            (Value::Model(lhs), Value::Model(rhs)) => Ok(Value::Model(
279                lhs.boolean_op(microcad_core::BooleanOp::Subtract, rhs),
280            )),
281            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} - {rhs}"))),
282        }
283    }
284}
285
286/// Rules for operator `*`.
287impl std::ops::Mul for Value {
288    type Output = ValueResult;
289
290    fn mul(self, rhs: Self) -> Self::Output {
291        match (self, rhs) {
292            (Value::Integer(lhs), Value::Model(rhs)) => Ok(Value::Model(
293                Models::from(rhs.multiply(lhs)).to_multiplicity(SrcRef(None)),
294            )),
295            // Multiply two integers
296            (Value::Integer(lhs), Value::Integer(rhs)) => Ok(Value::Integer(lhs * rhs)),
297            // Multiply an integer and a scalar, result is scalar
298            (Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs * rhs)?)),
299            // Multiply a scalar and an integer, result is scalar
300            (Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs * rhs)?)),
301            // Multiply two scalars
302            (Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs * rhs)?)),
303            (Value::Array(array), value) | (value, Value::Array(array)) => Ok((array * value)?),
304
305            (Value::Tuple(tuple), value) | (value, Value::Tuple(tuple)) => {
306                Ok((tuple.as_ref().clone() * value)?.into())
307            }
308            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} * {rhs}"))),
309        }
310    }
311}
312
313/// Multiply a Unit with a value. Used for unit bundling: `[1,2,3]mm`.
314///
315/// `[1,2,3]mm` is a shortcut for `[1,2,3] * 1mm`.
316impl std::ops::Mul<Unit> for Value {
317    type Output = ValueResult;
318
319    fn mul(self, unit: Unit) -> Self::Output {
320        match (self, unit.ty()) {
321            (value, Type::Quantity(QuantityType::Scalar)) | (value, Type::Integer) => Ok(value),
322            (Value::Integer(i), Type::Quantity(quantity_type)) => Ok(Value::Quantity(
323                Quantity::new(unit.normalize(i as Scalar), quantity_type),
324            )),
325            (Value::Quantity(quantity), Type::Quantity(quantity_type)) => Ok(Value::Quantity(
326                (quantity * Quantity::new(unit.normalize(1.0), quantity_type))?,
327            )),
328            (Value::Array(array), Type::Quantity(quantity_type)) => {
329                Ok((array * Value::Quantity(Quantity::new(unit.normalize(1.0), quantity_type)))?)
330            }
331            (value, _) => Err(ValueError::CannotAddUnitToValueWithUnit(value.to_string())),
332        }
333    }
334}
335
336/// Rules for operator `/`.
337impl std::ops::Div for Value {
338    type Output = ValueResult;
339
340    fn div(self, rhs: Self) -> Self::Output {
341        match (self, rhs) {
342            // Division with scalar result
343            (Value::Integer(lhs), Value::Integer(rhs)) => {
344                Ok(Value::Quantity((lhs as Scalar / rhs as Scalar).into()))
345            }
346            (Value::Quantity(lhs), Value::Integer(rhs)) => Ok(Value::Quantity((lhs / rhs)?)),
347            (Value::Integer(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs / rhs)?)),
348            (Value::Quantity(lhs), Value::Quantity(rhs)) => Ok(Value::Quantity((lhs / rhs)?)),
349            (Value::Array(array), value) => Ok((array / value)?),
350            (Value::Tuple(tuple), value) => Ok((tuple.as_ref().clone() / value)?.into()),
351            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} / {rhs}"))),
352        }
353    }
354}
355
356/// Rules for operator `|`` (union).
357impl std::ops::BitOr for Value {
358    type Output = ValueResult;
359
360    fn bitor(self, rhs: Self) -> Self::Output {
361        match (self, rhs) {
362            (Value::Model(lhs), Value::Model(rhs)) => Ok(Value::Model(
363                lhs.boolean_op(microcad_core::BooleanOp::Union, rhs),
364            )),
365            (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)),
366            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} | {rhs}"))),
367        }
368    }
369}
370
371/// Rules for operator `&` (intersection).
372impl std::ops::BitAnd for Value {
373    type Output = ValueResult;
374
375    fn bitand(self, rhs: Self) -> Self::Output {
376        match (self, rhs) {
377            (Value::Model(lhs), Value::Model(rhs)) => {
378                Ok(Value::Model(lhs.boolean_op(BooleanOp::Intersect, rhs)))
379            }
380            (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)),
381            (lhs, rhs) => Err(ValueError::InvalidOperator(format!("{lhs} & {rhs}"))),
382        }
383    }
384}
385
386impl std::fmt::Display for Value {
387    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
388        match self {
389            Value::None => write!(f, invalid_no_ansi!(VALUE)),
390            Value::Integer(n) => write!(f, "{n}"),
391            Value::Quantity(q) => write!(f, "{q}"),
392            Value::Bool(b) => write!(f, "{b}"),
393            Value::String(s) => write!(f, "{s}"),
394            Value::Array(l) => write!(f, "{l}"),
395            Value::Tuple(t) => write!(f, "{t}"),
396            Value::Matrix(m) => write!(f, "{m}"),
397            Value::Model(n) => write!(f, "{n}"),
398            Value::Return(r) => write!(f, "{r}"),
399        }
400    }
401}
402
403impl std::fmt::Debug for Value {
404    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
405        match self {
406            Value::None => write!(f, invalid!(VALUE)),
407            Value::Integer(n) => write!(f, "{n}"),
408            Value::Quantity(q) => write!(f, "{q:?}"),
409            Value::Bool(b) => write!(f, "{b}"),
410            Value::String(s) => write!(f, "{s:?}"),
411            Value::Array(l) => write!(f, "{l:?}"),
412            Value::Tuple(t) => write!(f, "{t:?}"),
413            Value::Matrix(m) => write!(f, "{m:?}"),
414            Value::Model(n) => write!(f, "\n {n:?}"),
415            Value::Return(r) => write!(f, "->{r:?}"),
416        }
417    }
418}
419
420impl std::hash::Hash for Value {
421    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
422        match self {
423            Value::None => std::mem::discriminant(&Value::None).hash(state),
424            Value::Quantity(quantity) => quantity.hash(state),
425            Value::Bool(b) => b.hash(state),
426            Value::Integer(i) => i.hash(state),
427            Value::String(s) => s.hash(state),
428            Value::Array(array) => array.hash(state),
429            Value::Tuple(tuple) => tuple.hash(state),
430            Value::Matrix(matrix) => matrix.hash(state),
431            Value::Model(model) => model.hash(state),
432            Value::Return(value) => value.hash(state),
433        }
434    }
435}
436
437macro_rules! impl_try_from {
438    ($($variant:ident),+ => $ty:ty ) => {
439        impl TryFrom<Value> for $ty {
440            type Error = ValueError;
441
442            fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
443                match value {
444                    $(Value::$variant(v) => Ok(v),)*
445                    value => Err(ValueError::CannotConvert(value.to_string(), stringify!($ty).into())),
446                }
447            }
448        }
449
450        impl TryFrom<&Value> for $ty {
451            type Error = ValueError;
452
453            fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
454                match value {
455                    $(Value::$variant(v) => Ok(v.clone().into()),)*
456                    value => Err(ValueError::CannotConvert(value.to_string(), stringify!($ty).into())),
457                }
458            }
459        }
460    };
461}
462
463impl_try_from!(Integer => i64);
464impl_try_from!(Bool => bool);
465impl_try_from!(String => String);
466
467impl TryFrom<&Value> for Scalar {
468    type Error = ValueError;
469
470    fn try_from(value: &Value) -> Result<Self, Self::Error> {
471        match value {
472            Value::Integer(i) => Ok(*i as Scalar),
473            Value::Quantity(Quantity {
474                value,
475                quantity_type: QuantityType::Scalar,
476            }) => Ok(*value),
477            _ => Err(ValueError::CannotConvert(
478                value.to_string(),
479                "Scalar".into(),
480            )),
481        }
482    }
483}
484
485impl TryFrom<Value> for Scalar {
486    type Error = ValueError;
487
488    fn try_from(value: Value) -> Result<Self, Self::Error> {
489        match value {
490            Value::Integer(i) => Ok(i as Scalar),
491            Value::Quantity(Quantity {
492                value,
493                quantity_type: QuantityType::Scalar,
494            }) => Ok(value),
495            _ => Err(ValueError::CannotConvert(
496                value.to_string(),
497                "Scalar".into(),
498            )),
499        }
500    }
501}
502
503impl TryFrom<&Value> for Angle {
504    type Error = ValueError;
505
506    fn try_from(value: &Value) -> Result<Self, Self::Error> {
507        match value {
508            Value::Quantity(Quantity {
509                value,
510                quantity_type: QuantityType::Angle,
511            }) => Ok(cgmath::Rad(*value)),
512            _ => Err(ValueError::CannotConvert(value.to_string(), "Angle".into())),
513        }
514    }
515}
516
517impl TryFrom<&Value> for Length {
518    type Error = ValueError;
519
520    fn try_from(value: &Value) -> Result<Self, Self::Error> {
521        match value {
522            Value::Quantity(Quantity {
523                value,
524                quantity_type: QuantityType::Length,
525            }) => Ok(Length(*value)),
526            _ => Err(ValueError::CannotConvert(
527                value.to_string(),
528                "Length".into(),
529            )),
530        }
531    }
532}
533
534impl TryFrom<&Value> for Size2 {
535    type Error = ValueError;
536
537    fn try_from(value: &Value) -> Result<Self, Self::Error> {
538        match value {
539            Value::Tuple(tuple) => Ok(tuple.as_ref().try_into()?),
540            _ => Err(ValueError::CannotConvert(value.to_string(), "Size2".into())),
541        }
542    }
543}
544
545impl TryFrom<&Value> for Mat3 {
546    type Error = ValueError;
547
548    fn try_from(value: &Value) -> Result<Self, Self::Error> {
549        if let Value::Matrix(m) = value {
550            if let Matrix::Matrix3(matrix3) = m.as_ref() {
551                return Ok(*matrix3);
552            }
553        }
554
555        Err(ValueError::CannotConvert(
556            value.to_string(),
557            "Matrix3".into(),
558        ))
559    }
560}
561
562impl From<usize> for Value {
563    fn from(value: usize) -> Self {
564        Value::Integer(value as Integer)
565    }
566}
567
568impl From<f32> for Value {
569    fn from(f: f32) -> Self {
570        Value::Quantity((f as Scalar).into())
571    }
572}
573
574impl From<Scalar> for Value {
575    fn from(scalar: Scalar) -> Self {
576        Value::Quantity(scalar.into())
577    }
578}
579
580impl From<Length> for Value {
581    fn from(length: Length) -> Self {
582        Value::Quantity(length.into())
583    }
584}
585
586impl From<Size2> for Value {
587    fn from(value: Size2) -> Self {
588        Self::Tuple(Box::new(value.into()))
589    }
590}
591
592impl From<Color> for Value {
593    fn from(color: Color) -> Self {
594        Self::Tuple(Box::new(color.into()))
595    }
596}
597
598impl From<Vec3> for Value {
599    fn from(v: Vec3) -> Self {
600        Self::Tuple(Box::new(v.into()))
601    }
602}
603
604impl FromIterator<Value> for Value {
605    fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
606        Self::Array(iter.into_iter().collect())
607    }
608}
609
610impl AttributesAccess for Value {
611    fn get_attributes_by_id(&self, id: &Identifier) -> Vec<crate::model::Attribute> {
612        match self {
613            Value::Model(model) => model.get_attributes_by_id(id),
614            _ => Vec::default(),
615        }
616    }
617}
618
619#[cfg(test)]
620fn integer(value: i64) -> Value {
621    Value::Integer(value)
622}
623
624#[cfg(test)]
625fn scalar(value: f64) -> Value {
626    Value::Quantity(Quantity::new(value, QuantityType::Scalar))
627}
628
629#[cfg(test)]
630fn check(result: ValueResult, value: Value) {
631    let result = result.expect("error result");
632    assert_eq!(result, value);
633}
634
635#[test]
636fn test_value_integer() {
637    let u = || integer(2);
638    let v = || integer(5);
639    let w = || scalar(5.0);
640
641    // symmetric operations
642    check(u() + v(), integer(2 + 5));
643    check(u() - v(), integer(2 - 5));
644    check(u() * v(), integer(2 * 5));
645    check(u() / v(), scalar(2.0 / 5.0));
646    check(-u(), integer(-2));
647
648    // asymmetric operations
649    check(u() + w(), scalar(2 as Scalar + 5.0));
650    check(u() - w(), scalar(2 as Scalar - 5.0));
651    check(u() * w(), scalar(2 as Scalar * 5.0));
652    check(u() / w(), scalar(2.0 / 5.0));
653}
654
655#[test]
656fn test_value_scalar() {
657    let u = || scalar(2.0);
658    let v = || scalar(5.0);
659    let w = || integer(5);
660
661    // symmetric operations
662    check(u() + v(), scalar(2.0 + 5.0));
663    check(u() - v(), scalar(2.0 - 5.0));
664    check(u() * v(), scalar(2.0 * 5.0));
665    check(u() / v(), scalar(2.0 / 5.0));
666    check(-u(), scalar(-2.0));
667
668    // asymmetric operations
669    check(u() + w(), scalar(2.0 + 5.0));
670    check(u() - w(), scalar(2.0 - 5.0));
671    check(u() * w(), scalar(2.0 * 5.0));
672    check(u() / w(), scalar(2.0 / 5.0));
673}