diff --git a/doc/lisp/share/pugs.el b/doc/lisp/share/pugs.el
index 36830ffa5036575b65b08690cb22d986b34473a9..47b37a03ecaac8150196036ae9baaa8a0afb1e50 100644
--- a/doc/lisp/share/pugs.el
+++ b/doc/lisp/share/pugs.el
@@ -41,7 +41,7 @@ Ask to save the buffer if needed"
   "pugs mode is a major mode for editing pugs files"
   ;; define pugs keywords
   (defvar pugs-keywords
-    '("import" "let" "Z" "N" "B" "R" "string" "and" "or" "xor" "not" "true" "false" "let" "do" "while" "for" "if" "else" "break" "continue" "cout" "cerr" "clog" "+"))
+    '("import" "let" "Z" "N" "B" "R" "string" "and" "or" "xor" "not" "true" "false" "let" "do" "while" "for" "if" "else" "break" "continue" "cout" "cerr" "clog" "void"))
 
   (defvar pugs-special-symbols
     '(":" "," ";" "{" "}" "->" "<=" ">=" "=" "+" "-" "*" "/"  "<" ">" "^"))
diff --git a/doc/userdoc.org b/doc/userdoc.org
index ba7cd798a6a7991d6451c405a5b9c81dfa2c0362..490db865223c800e05ee0ee31b52bd86486f8872 100644
--- a/doc/userdoc.org
+++ b/doc/userdoc.org
@@ -1852,6 +1852,72 @@ The use case of tuples is to provide lists of data to the ~C++~
 underlying methods. A classical example is to provide a set of
 boundary conditions to a method.
 
+**** Tuple and other variables
+
+If one cannot access to specific values of tuple variables, one can
+however dispatch their values to compound variable lists or to simple
+variable. To do so the tuple content must be convertible to each of
+the compound variable using the operator ~=~ rules (see the conversion
+table in section [[implicit-conversion]]). The one to one matching is
+checked at runtime.
+#+NAME: tuple-to-compound
+#+BEGIN_SRC pugs :exports both :results output
+  let t:(R), t = (1, 2, 3.4, -5);
+
+  let (x, y, u, A):R*R*R^1*R^1x1, (x, y, u, A) = t;
+
+  cout << "t = " << t << "\n";
+  cout << "x = " << x << "\n";
+  cout << "y = " << y << "\n";
+  cout << "u = " << u << "\n";
+  cout << "A = " << A << "\n";
+#+END_SRC
+This code gives
+#+results: tuple-to-compound
+
+#+BEGIN_note
+One could have separate the declarations of ~x~, ~y~, ~u~ and ~A~ from the
+affectation. Writing simply at some point
+
+#+BEGIN_SRC pugs :exports code
+  (x, y, u, A) = t;
+#+END_SRC
+
+#+END_note
+
+If the tuple contains only one entry, one can assign its value to a
+simple variable.
+#+NAME: tuple-to-variable
+#+BEGIN_SRC pugs :exports both :results output
+  let t:(R^3), t = [1, 2, 3];
+
+  let x:R^3, x = t;
+
+  cout << "t = " << t << "\n";
+  cout << "x = " << x << "\n";
+#+END_SRC
+One gets
+#+results: tuple-to-variable
+
+If the size of the tuple does not match the number of space defining
+the compound type, one gets a runtime error. For instance
+#+NAME: tuple-to-compound-wrong-size
+#+BEGIN_SRC pugs-error :exports both :results output
+  let t:(Z), t = (1, 2);
+  let (a, b, c):R*R*R, (a, b, c) = t;
+#+END_SRC
+produces
+#+results: tuple-to-compound-wrong-size
+or
+#+NAME: tuple-to-variable-wrong-size
+#+BEGIN_SRC pugs-error :exports both :results output
+  let t:(Z), t = (1, 2);
+  let a:R, a = t;
+#+END_SRC
+which gives
+#+results: tuple-to-variable-wrong-size
+
+
 ** Statements
 
 The ~pugs~ language supports classical statements to control the data
@@ -2160,6 +2226,21 @@ compound). The type of ~x~ is ~X~ and for lists, each argument ~xi~ belongs
 to ~Xi~. The function themselves are defined by the expressions ~e~ (or
 ~e1~, ..., ~em~) which types are set by ~Y~ (or ~Y1~, ..., ~Ym~).
 
+#+BEGIN_warning
+- ~X~, ~X1~, ..., ~Xn~ cannot be tuples spaces.
+- ~Y~, ~Y1~, ..., ~Ym~ can be tuple spaces.
+
+Also, ~X~ can be the empty space $\varnothing$, but none of ~X1~, ..., ~Xn~
+(for $n>1$) and none of ~Y~, ~Y1~, ..., ~Ym~ can be $\varnothing$. In other
+word one can define functions such as
+\begin{align*}
+    \mbox{let }g:& \varnothing \to Y_1\times \cdots \times Y_m,\\
+                 & \varnothing\mapsto (y_1,\ldots,y_m)=g(\varnothing).
+\end{align*}
+The *keyword* associated to $\varnothing$ is ~void~. Keep in mind that ~void~
+is just a keyword in ~pugs~, it is not a type in the computer science
+point of view (one cannot declare variables of type ~void~).
+#+END_warning
 
 Let us give a few examples.
 #+NAME: R-to-R-function
@@ -2184,6 +2265,14 @@ returned type
 #+END_SRC
 #+results: R-to-R1-function
 
+But one cannot use tuples to define the domain
+#+NAME: no-tuple-in-domain
+#+BEGIN_SRC pugs-error :exports both :results output
+  let f: (N)->N, n -> 3;
+#+END_SRC
+produces the following compilation time error
+#+results: no-tuple-in-domain
+
 Using compound types as input and output, one can write
 #+NAME: R22-R-string-to-R-string-function
 #+BEGIN_SRC pugs :exports both :results output
@@ -2208,6 +2297,25 @@ Using compound types as input and output, one can write
 This meaningless example produces the following result.
 #+results: R22-R-string-to-R-string-function
 
+The following example shows how to use tuples as codomain.
+#+NAME: R-to-tuple-R-function
+#+BEGIN_SRC pugs :exports both :results output
+  let f: R -> (R), x -> (2*x, 2-x, 7*x-2, 2, x/3);
+
+  cout << "f(3.2) = " << f(3.2) << "\n";
+#+END_SRC
+#+results: R-to-tuple-R-function
+
+Finally, we give an example of an empty function. Observe the special
+role of the ~void~ keyword
+#+NAME: void-to-tuple-R-function
+#+BEGIN_SRC pugs :exports both :results output
+  let f: void -> (R^2), void -> ([2, 3.5], [6, 7]);
+
+  cout << "f() = " << f() << "\n";
+#+END_SRC
+#+results: void-to-tuple-R-function
+
 **** Lifetime of function arguments
 
 The arguments used to define a function are *local* variables that exist
@@ -2325,7 +2433,8 @@ produces the following compilation time error
 In ~pugs~ language, builtin functions are ~C++~ pieces of code that can be
 called in scripts. Their usage is very similar to user-defined
 functions. They differ from user-defined functions in three points.
-- Builtin functions may have no parameter or no returned value.
+- Builtin functions may have no returned value.
+- Builtin functions can use tuples as arguments.
 - Builtin functions are polymorphic. More precisely, this means that
   the signature of a builtin function is also defined by its expected
   argument types.
@@ -2722,32 +2831,35 @@ The information produced concerns
 
 This type is used to designate kinds of items (cell, face, edge or node).
 
-***** ~item_value~
+***** ~item_value~ and ~item_array~
 
-The type ~item_value~ is an abstract type use to designate arrays of
-values defined on the entities of a ~mesh~. Actually, these values are
-not associated to the mesh itself but to the *connectivity*. The values
-on the entities can be of type ~B~, ~N~, ~Z~, ~R~, ~R^1~, ~R^2~, ~R^3~, ~R^1x1~, ~R^2x2~
-and ~R^3x3~. Entities themselves can be cells, faces, edges or nodes.
+The types ~item_value~ and ~item_array~ are abstract types use to
+designate values or arrays of values defined on each entities of a
+~mesh~. Actually, these set of values (or arrays) are not associated to
+the mesh itself but to the *connectivity*. The values on the entities
+can be of type ~B~, ~N~, ~Z~, ~R~, ~R^1~, ~R^2~, ~R^3~, ~R^1x1~, ~R^2x2~ and
+~R^3x3~. Entities themselves can be cells, faces, edges or nodes.
 
 These variables are used to pass data from one function to another.
 
 #+BEGIN_warning
-By now, no mathematical operation is defined on ~item_value~ variables.
+By now, no mathematical operation is defined on ~item_value~ or
+~item_array~ variables.
 #+END_warning
 
 Moreover, ~item_value~ variables can be post processed. Observe that
 edge or face values cannot be post processed since neither ~VTK~ nor
-~Gnuplot~ can handle these data.
+~Gnuplot~ can handle these data. Also ~item_raray~ can only be post
+processed if array per item contain scalar data ( ~B~, ~N~, ~Z~, ~R~).
 
-***** ~sub_item_value~
+***** ~sub_item_value~ and ~sub_item_arrays~
 
-This abstract type handles values defined on the sub items of the
-items of a ~mesh~. Similarly these values are attached to a *connectivity*
-and not to a mesh. The values associated to the sub items can be of
-type ~B~, ~N~, ~Z~, ~R~, ~R^1~, ~R^2~, ~R^3~, ~R^1x1~, ~R^2x2~ or ~R^3x3~. An example of
-~sub_item_value~ is the $\mathbf{C}_{jr}$ vectors which are defined at each
-node of each cell.
+These abstract type handles values or arrays defined on the sub items
+of the items of a ~mesh~. Similarly these sets of values or arrays are
+attached to a *connectivity* and not to a mesh. The values associated to
+the sub items can be of type ~B~, ~N~, ~Z~, ~R~, ~R^1~, ~R^2~, ~R^3~, ~R^1x1~, ~R^2x2~
+or ~R^3x3~. An example of ~sub_item_value~ is the $\mathbf{C}_{jr}$ vectors
+which are defined at each node of each cell.
 
 These variables are used to pass data from one function to
 another. They cannot be post processed.
diff --git a/src/algebra/SmallMatrix.hpp b/src/algebra/SmallMatrix.hpp
index d0d6ee329f02c41626900384c794d1cfa490089c..b8d3b81b5fad0ac65521a94aa4f94183f4cff333 100644
--- a/src/algebra/SmallMatrix.hpp
+++ b/src/algebra/SmallMatrix.hpp
@@ -38,7 +38,7 @@ class [[nodiscard]] SmallMatrix   // LCOV_EXCL_LINE
 
   friend PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> copy(const SmallMatrix& A) noexcept
   {
-    return {A.m_nb_rows, A.m_nb_columns, copy(A.m_values)};
+    return SmallMatrix<std::remove_const_t<DataType>>{A.m_nb_rows, A.m_nb_columns, copy(A.m_values)};
   }
 
   friend PUGS_INLINE SmallMatrix<std::remove_const_t<DataType>> transpose(const SmallMatrix& A)
@@ -130,7 +130,8 @@ class [[nodiscard]] SmallMatrix   // LCOV_EXCL_LINE
   {
     static_assert(std::is_same_v<std::remove_const_t<DataType>, std::remove_const_t<DataType2>>,
                   "incompatible data types");
-    Assert((m_nb_rows == B.numberOfRows()) and (m_nb_columns == B.numberOfColumns()), "incompatible matrix sizes");
+    Assert((m_nb_rows == B.numberOfRows()) and (m_nb_columns == B.numberOfColumns()),
+           "cannot add matrix: incompatible sizes");
 
     parallel_for(
       m_values.size(), PUGS_LAMBDA(index_type i) { m_values[i] += B.m_values[i]; });
@@ -260,6 +261,12 @@ class [[nodiscard]] SmallMatrix   // LCOV_EXCL_LINE
 
   SmallMatrix(SmallMatrix &&) = default;
 
+  explicit SmallMatrix(size_t nb_rows, size_t nb_columns, const SmallArray<DataType>& values)
+    : m_nb_rows{nb_rows}, m_nb_columns{nb_columns}, m_values{values}
+  {
+    Assert(m_values.size() == m_nb_columns * m_nb_rows, "incompatible array size and matrix dimensions")
+  }
+
   explicit SmallMatrix(size_t nb_rows, size_t nb_columns) noexcept
     : m_nb_rows{nb_rows}, m_nb_columns{nb_columns}, m_values{nb_rows * nb_columns}
   {}
diff --git a/src/algebra/SmallVector.hpp b/src/algebra/SmallVector.hpp
index d20bb48f4cdb700f61a0881898fb5273217693aa..d4a0bf84268bdf1d164cf6216153117adafaab56 100644
--- a/src/algebra/SmallVector.hpp
+++ b/src/algebra/SmallVector.hpp
@@ -30,6 +30,18 @@ class SmallVector   // LCOV_EXCL_LINE
     return os << x.m_values;
   }
 
+  [[nodiscard]] friend std::remove_const_t<DataType>
+  min(const SmallVector& x)
+  {
+    return min(x.m_values);
+  }
+
+  [[nodiscard]] friend std::remove_const_t<DataType>
+  max(const SmallVector& x)
+  {
+    return max(x.m_values);
+  }
+
   friend SmallVector<std::remove_const_t<DataType>>
   copy(const SmallVector& x)
   {
@@ -40,7 +52,18 @@ class SmallVector   // LCOV_EXCL_LINE
     return x_copy;
   }
 
-  friend SmallVector operator*(const DataType& a, const SmallVector& x)
+  PUGS_INLINE
+  SmallVector<std::remove_const_t<DataType>>
+  operator-() const
+  {
+    SmallVector<std::remove_const_t<DataType>> opposite(this->size());
+    parallel_for(
+      opposite.size(), PUGS_LAMBDA(index_type i) { opposite.m_values[i] = -m_values[i]; });
+    return opposite;
+  }
+
+  friend SmallVector
+  operator*(const DataType& a, const SmallVector& x)
   {
     SmallVector<std::remove_const_t<DataType>> y = copy(x);
     return y *= a;
@@ -51,14 +74,8 @@ class SmallVector   // LCOV_EXCL_LINE
   dot(const SmallVector& x, const SmallVector<DataType2>& y)
   {
     Assert(x.size() == y.size(), "cannot compute dot product: incompatible vector sizes");
-    // Quite ugly, TODO: fix me in C++20
-    auto promoted = [] {
-      DataType a{0};
-      DataType2 b{0};
-      return a * b;
-    }();
 
-    decltype(promoted) sum = 0;
+    decltype(std::declval<DataType>() * std::declval<DataType2>()) sum = 0;
 
     // Does not use parallel_for to preserve sum order
     for (index_type i = 0; i < x.size(); ++i) {
@@ -136,7 +153,8 @@ class SmallVector   // LCOV_EXCL_LINE
   }
 
   PUGS_INLINE
-  DataType& operator[](index_type i) const noexcept(NO_ASSERT)
+  DataType&
+  operator[](index_type i) const noexcept(NO_ASSERT)
   {
     return m_values[i];
   }
diff --git a/src/algebra/TinyVector.hpp b/src/algebra/TinyVector.hpp
index ae616a4849d0e681bf09e613ad4d19263d0f035a..e39dcec6e9834e5cb5eb80e1e845de1054beb1b2 100644
--- a/src/algebra/TinyVector.hpp
+++ b/src/algebra/TinyVector.hpp
@@ -42,14 +42,12 @@ class [[nodiscard]] TinyVector
     return opposite;
   }
 
-  PUGS_INLINE
-  constexpr size_t dimension() const
+  [[nodiscard]] PUGS_INLINE constexpr size_t dimension() const
   {
     return N;
   }
 
-  PUGS_INLINE
-  constexpr bool operator==(const TinyVector& v) const
+  [[nodiscard]] PUGS_INLINE constexpr bool operator==(const TinyVector& v) const
   {
     for (size_t i = 0; i < N; ++i) {
       if (m_values[i] != v.m_values[i])
@@ -58,14 +56,12 @@ class [[nodiscard]] TinyVector
     return true;
   }
 
-  PUGS_INLINE
-  constexpr bool operator!=(const TinyVector& v) const
+  [[nodiscard]] PUGS_INLINE constexpr bool operator!=(const TinyVector& v) const
   {
     return not this->operator==(v);
   }
 
-  PUGS_INLINE
-  constexpr friend T dot(const TinyVector& u, const TinyVector& v)
+  [[nodiscard]] PUGS_INLINE constexpr friend T dot(const TinyVector& u, const TinyVector& v)
   {
     T t = u.m_values[0] * v.m_values[0];
     for (size_t i = 1; i < N; ++i) {
@@ -242,7 +238,7 @@ class [[nodiscard]] TinyVector
 };
 
 template <size_t N, typename T>
-PUGS_INLINE constexpr T
+[[nodiscard]] PUGS_INLINE constexpr T
 l2Norm(const TinyVector<N, T>& x)
 {
   static_assert(std::is_arithmetic<T>(), "Cannot compute L2 norm for non-arithmetic types");
@@ -250,13 +246,39 @@ l2Norm(const TinyVector<N, T>& x)
   return std::sqrt(dot(x, x));
 }
 
-// Cross product is only defined for K^3 vectors
+template <size_t N, typename T>
+[[nodiscard]] PUGS_INLINE constexpr T
+min(const TinyVector<N, T>& x)
+{
+  T m = x[0];
+  for (size_t i = 1; i < N; ++i) {
+    if (x[i] < m) {
+      m = x[i];
+    }
+  }
+  return m;
+}
+
+template <size_t N, typename T>
+[[nodiscard]] PUGS_INLINE constexpr T
+max(const TinyVector<N, T>& x)
+{
+  T m = x[0];
+  for (size_t i = 1; i < N; ++i) {
+    if (x[i] > m) {
+      m = x[i];
+    }
+  }
+  return m;
+}
+
+// Cross product is only defined for dimension 3 vectors
 template <typename T>
-PUGS_INLINE constexpr TinyVector<3, T>
+[[nodiscard]] PUGS_INLINE constexpr TinyVector<3, T>
 crossProduct(const TinyVector<3, T>& u, const TinyVector<3, T>& v)
 {
   TinyVector<3, T> cross_product(u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]);
   return cross_product;
 }
 
-#endif   // TINYVECTOR_HPP
+#endif   // TINY_VECTOR_HPP
diff --git a/src/algebra/Vector.hpp b/src/algebra/Vector.hpp
index c918ed409746a3c212720ad8ae6769cfb0882ba9..749a795a489f12a671f4ecfd452104702ba68147 100644
--- a/src/algebra/Vector.hpp
+++ b/src/algebra/Vector.hpp
@@ -31,6 +31,18 @@ class Vector   // LCOV_EXCL_LINE
     return os << x.m_values;
   }
 
+  [[nodiscard]] friend std::remove_const_t<DataType>
+  min(const Vector& x)
+  {
+    return min(x.m_values);
+  }
+
+  [[nodiscard]] friend std::remove_const_t<DataType>
+  max(const Vector& x)
+  {
+    return max(x.m_values);
+  }
+
   friend Vector<std::remove_const_t<DataType>>
   copy(const Vector& x)
   {
@@ -41,6 +53,16 @@ class Vector   // LCOV_EXCL_LINE
     return x_copy;
   }
 
+  PUGS_INLINE
+  Vector<std::remove_const_t<DataType>>
+  operator-() const
+  {
+    Vector<std::remove_const_t<DataType>> opposite(this->size());
+    parallel_for(
+      opposite.size(), PUGS_LAMBDA(index_type i) { opposite.m_values[i] = -m_values[i]; });
+    return opposite;
+  }
+
   friend Vector
   operator*(const DataType& a, const Vector& x)
   {
@@ -53,14 +75,7 @@ class Vector   // LCOV_EXCL_LINE
   dot(const Vector& x, const Vector<DataType2>& y)
   {
     Assert(x.size() == y.size(), "cannot compute dot product: incompatible vector sizes");
-    // Quite ugly, TODO: fix me in C++20
-    auto promoted = [] {
-      DataType a{0};
-      DataType2 b{0};
-      return a * b;
-    }();
-
-    decltype(promoted) sum = 0;
+    decltype(std::declval<DataType>() * std::declval<DataType2>()) sum = 0;
 
     // Does not use parallel_for to preserve sum order
     for (index_type i = 0; i < x.size(); ++i) {
diff --git a/src/language/PEGGrammar.hpp b/src/language/PEGGrammar.hpp
index 0e98c2b4bf710b8e86563cabfc0b58dc45f820ff..e2aaeec547bfb3793da15e2305a4882d29fd22e3 100644
--- a/src/language/PEGGrammar.hpp
+++ b/src/language/PEGGrammar.hpp
@@ -72,6 +72,8 @@ struct B_set : TAO_PEGTL_KEYWORD("B"){};
 struct N_set : TAO_PEGTL_KEYWORD("N"){};
 struct Z_set : TAO_PEGTL_KEYWORD("Z"){};
 struct R_set : TAO_PEGTL_KEYWORD("R"){};
+struct empty_set : TAO_PEGTL_KEYWORD("void"){};
+struct EMPTY_SET : seq< empty_set, ignored>{};
 
 struct string_type : TAO_PEGTL_KEYWORD("string") {};
 
@@ -92,7 +94,7 @@ struct tuple_type_specifier :  sor<try_catch< open_parent, simple_type_specifier
 struct TYPE_SPECIFIER : seq< sor<simple_type_specifier, tuple_type_specifier>, ignored >{};
 
 struct type_expression : list_must< TYPE_SPECIFIER, seq< ignored, one< '*' >, ignored > >{};
-struct TYPE_EXPRESSION : seq< type_expression, ignored >{};
+struct TYPE_EXPRESSION : seq< sor< empty_set, type_expression >, ignored >{};
 
 struct and_kw : TAO_PEGTL_KEYWORD("and") {};
 struct or_kw : TAO_PEGTL_KEYWORD("or") {};
@@ -129,7 +131,7 @@ struct BREAK : seq < break_kw, ignored > {};
 struct continue_kw : TAO_PEGTL_KEYWORD("continue") {};
 struct CONTINUE : seq < continue_kw, ignored > {};
 
-struct keyword : sor < basic_type, import_kw, true_kw, false_kw, let_kw, do_kw, while_kw, for_kw, if_kw, else_kw, and_kw, or_kw, xor_kw, not_kw, break_kw, continue_kw> {};
+struct keyword : sor < empty_set, basic_type, import_kw, true_kw, false_kw, let_kw, do_kw, while_kw, for_kw, if_kw, else_kw, and_kw, or_kw, xor_kw, not_kw, break_kw, continue_kw> {};
 
 struct identifier_minus_keyword : minus< identifier, keyword > {};
 
@@ -260,7 +262,7 @@ struct type_mapping : seq< TYPE_EXPRESSION, RIGHT_ARROW, TYPE_EXPRESSION >{};
 
 struct name_list : seq< open_parent, list_must< NAME, COMMA >, close_parent >{};
 
-struct function_definition : seq< sor< name_list, NAME >, RIGHT_ARROW, sor< expression_list, expression > >{};
+struct function_definition : seq< sor< EMPTY_SET, name_list, NAME >, RIGHT_ARROW, sor< expression_list, expression > >{};
 
 struct fct_declaration : seq< LET, NAME, COLUMN, type_mapping, COMMA, function_definition >{};
 
@@ -403,6 +405,9 @@ inline const std::string errors<language::not_at<subscript_expression_of_lvalue>
 template <>
 inline const std::string errors<language::simple_type_specifier>::error_message = "expecting simple type specifier";
 
+template <>
+inline const std::string errors<language::TYPE_SPECIFIER>::error_message = "parse error, expecting type specifier";
+
 // clang-format on
 
 }   // namespace language
diff --git a/src/language/ast/ASTBuilder.cpp b/src/language/ast/ASTBuilder.cpp
index 476c30201ffd43f1102efaa9c9430a5283cc8c89..326d4139e709507c95f6d40f49ca1f87411b7b5c 100644
--- a/src/language/ast/ASTBuilder.cpp
+++ b/src/language/ast/ASTBuilder.cpp
@@ -226,6 +226,7 @@ using selector = TAO_PEGTL_NAMESPACE::parse_tree::selector<
                                                      language::N_set,
                                                      language::Z_set,
                                                      language::R_set,
+                                                     language::empty_set,
                                                      language::type_name_id,
                                                      language::tuple_type_specifier,
                                                      language::inner_expression_list,
diff --git a/src/language/ast/ASTNodeDataTypeBuilder.cpp b/src/language/ast/ASTNodeDataTypeBuilder.cpp
index 1e7eb1c427042bef9566c2c55befeb72de0c5e67..b67d81af335e3de1d3c02b0307351f03e75f8089 100644
--- a/src/language/ast/ASTNodeDataTypeBuilder.cpp
+++ b/src/language/ast/ASTNodeDataTypeBuilder.cpp
@@ -41,6 +41,8 @@ ASTNodeDataTypeBuilder::_buildDeclarationNodeDataTypes(ASTNode& type_node, ASTNo
       data_type = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
     } else if (type_node.is_type<language::R_set>()) {
       data_type = ASTNodeDataType::build<ASTNodeDataType::double_t>();
+    } else if (type_node.is_type<language::empty_set>()) {
+      throw ParseError("'void' keyword does not define a type", std::vector{type_node.begin()});
     } else if (type_node.is_type<language::vector_type>()) {
       data_type = getVectorDataType(type_node);
     } else if (type_node.is_type<language::matrix_type>()) {
@@ -123,6 +125,8 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
         n.m_data_type = getVectorDataType(n);
       } else if (n.is_type<language::matrix_type>()) {
         n.m_data_type = getMatrixDataType(n);
+      } else if (n.is_type<language::tuple_type_specifier>()) {
+        n.m_data_type = getTupleDataType(n);
 
       } else if (n.is_type<language::literal>()) {
         n.m_data_type = ASTNodeDataType::build<ASTNodeDataType::string_t>();
@@ -173,6 +177,27 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
           throw ParseError(message.str(), parameters_domain_node.begin());
         }
 
+        {
+          if (parameters_domain_node.is_type<language::type_expression>()) {
+            for (size_t i_domain = 0; i_domain < parameters_domain_node.children.size(); ++i_domain) {
+              if (parameters_domain_node.children[i_domain]->is_type<language::tuple_type_specifier>()) {
+                std::ostringstream message;
+                message << "cannot use tuple " << rang::fgB::yellow
+                        << dataTypeName(parameters_domain_node.children[i_domain]->m_data_type) << rang::fg::reset
+                        << " as a domain for user functions" << rang::style::reset;
+                throw ParseError(message.str(), parameters_domain_node.children[i_domain]->begin());
+              }
+            }
+          } else {
+            if (parameters_domain_node.is_type<language::tuple_type_specifier>()) {
+              std::ostringstream message;
+              message << "cannot use tuple " << rang::fgB::yellow << dataTypeName(parameters_domain_node.m_data_type)
+                      << rang::fg::reset << " as a domain for user functions" << rang::style::reset;
+              throw ParseError(message.str(), parameters_domain_node.begin());
+            }
+          }
+        }
+
         auto simple_type_allocator = [&](const ASTNode& type_node, ASTNode& symbol_node) {
           Assert(symbol_node.is_type<language::name>());
 
@@ -194,18 +219,28 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
           i_symbol->attributes().setDataType(data_type);
         };
 
-        if (nb_parameter_domains == 1) {
-          simple_type_allocator(parameters_domain_node, parameters_name_node);
+        if (parameters_domain_node.is_type<language::empty_set>() or
+            parameters_name_node.is_type<language::empty_set>()) {
+          if (not parameters_domain_node.is_type<language::empty_set>()) {
+            std::ostringstream error_msg;
+            throw ParseError("unexpected 'void' keyword", std::vector{parameters_name_node.begin()});
+          } else if (not parameters_name_node.is_type<language::empty_set>()) {
+            throw ParseError("expecting 'void' keyword", std::vector{parameters_name_node.begin()});
+          }
         } else {
-          std::vector<std::shared_ptr<const ASTNodeDataType>> sub_data_type_list;
-          sub_data_type_list.reserve(nb_parameter_domains);
+          if (nb_parameter_domains == 1) {
+            simple_type_allocator(parameters_domain_node, parameters_name_node);
+          } else {
+            std::vector<std::shared_ptr<const ASTNodeDataType>> sub_data_type_list;
+            sub_data_type_list.reserve(nb_parameter_domains);
 
-          for (size_t i = 0; i < nb_parameter_domains; ++i) {
-            simple_type_allocator(*parameters_domain_node.children[i], *parameters_name_node.children[i]);
-            sub_data_type_list.push_back(
-              std::make_shared<const ASTNodeDataType>(parameters_name_node.children[i]->m_data_type));
+            for (size_t i = 0; i < nb_parameter_domains; ++i) {
+              simple_type_allocator(*parameters_domain_node.children[i], *parameters_name_node.children[i]);
+              sub_data_type_list.push_back(
+                std::make_shared<const ASTNodeDataType>(parameters_name_node.children[i]->m_data_type));
+            }
+            parameters_name_node.m_data_type = ASTNodeDataType::build<ASTNodeDataType::list_t>(sub_data_type_list);
           }
-          parameters_name_node.m_data_type = ASTNodeDataType::build<ASTNodeDataType::list_t>(sub_data_type_list);
         }
 
         this->_buildNodeDataTypes(function_descriptor.definitionNode());
@@ -218,32 +253,14 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
         const size_t nb_image_expressions =
           (image_expression_node.is_type<language::expression_list>()) ? image_expression_node.children.size() : 1;
 
-        if (nb_image_domains != nb_image_expressions) {
-          if (image_domain_node.is_type<language::vector_type>()) {
-            ASTNodeDataType image_type = getVectorDataType(image_domain_node);
-            if (image_type.dimension() != nb_image_expressions) {
-              std::ostringstream message;
-              message << "expecting " << image_type.dimension() << " scalar expressions or an "
-                      << dataTypeName(image_type) << ", found " << nb_image_expressions << " scalar expressions";
-              throw ParseError(message.str(), image_expression_node.begin());
-            }
-          } else if (image_domain_node.is_type<language::matrix_type>()) {
-            ASTNodeDataType image_type = getMatrixDataType(image_domain_node);
-            if (image_type.numberOfRows() * image_type.numberOfColumns() != nb_image_expressions) {
-              std::ostringstream message;
-              message << "expecting " << image_type.numberOfRows() * image_type.numberOfColumns()
-                      << " scalar expressions or an " << dataTypeName(image_type) << ", found " << nb_image_expressions
-                      << " scalar expressions";
-              throw ParseError(message.str(), image_expression_node.begin());
-            }
-          } else {
-            std::ostringstream message;
-            message << "number of image spaces (" << nb_image_domains << ") " << rang::fgB::yellow
-                    << image_domain_node.string() << rang::style::reset << rang::style::bold
-                    << " differs from number of expressions (" << nb_image_expressions << ") " << rang::fgB::yellow
-                    << image_expression_node.string() << rang::style::reset;
-            throw ParseError(message.str(), image_domain_node.begin());
-          }
+        if ((not image_domain_node.is_type<language::tuple_type_specifier>()) and
+            (nb_image_domains != nb_image_expressions)) {
+          std::ostringstream message;
+          message << "number of image spaces (" << nb_image_domains << ") " << rang::fgB::yellow
+                  << image_domain_node.string() << rang::style::reset << rang::style::bold
+                  << " differs from number of expressions (" << nb_image_expressions << ") " << rang::fgB::yellow
+                  << image_expression_node.string() << rang::style::reset;
+          throw ParseError(message.str(), image_domain_node.begin());
         }
 
         this->_buildNodeDataTypes(image_expression_node);
@@ -518,6 +535,8 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
         n.m_data_type = [&] {
           if (image_domain_node.m_data_type == ASTNodeDataType::type_id_t) {
             return ASTNodeDataType::build<ASTNodeDataType::type_id_t>(image_domain_node.m_data_type.nameOfTypeId());
+          } else if (image_domain_node.m_data_type == ASTNodeDataType::tuple_t) {
+            return ASTNodeDataType::build<ASTNodeDataType::tuple_t>(image_domain_node.m_data_type.contentType());
           } else {
             return image_domain_node.m_data_type.contentType();
           }
@@ -582,6 +601,9 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
     } else if (n.is_type<language::R_set>()) {
       n.m_data_type =
         ASTNodeDataType::build<ASTNodeDataType::typename_t>(ASTNodeDataType::build<ASTNodeDataType::double_t>());
+    } else if (n.is_type<language::empty_set>()) {
+      n.m_data_type =
+        ASTNodeDataType::build<ASTNodeDataType::typename_t>(ASTNodeDataType::build<ASTNodeDataType::void_t>());
     } else if (n.is_type<language::vector_type>()) {
       n.m_data_type = ASTNodeDataType::build<ASTNodeDataType::typename_t>(getVectorDataType(n));
     } else if (n.is_type<language::matrix_type>()) {
@@ -608,6 +630,6 @@ ASTNodeDataTypeBuilder::ASTNodeDataTypeBuilder(ASTNode& node)
   this->_buildNodeDataTypes(node);
 
   if (ConsoleManager::showPreamble()) {
-    std::cout << " - build node data types\n";
+    std::cout << " - built node data types\n";
   }
 }
diff --git a/src/language/ast/ASTNodeDataTypeFlattener.cpp b/src/language/ast/ASTNodeDataTypeFlattener.cpp
index 00b77e0bf98e13314fce52396c7a6cef5717254e..201906e28020345cd70a34c501bcb2cf0b5fa258 100644
--- a/src/language/ast/ASTNodeDataTypeFlattener.cpp
+++ b/src/language/ast/ASTNodeDataTypeFlattener.cpp
@@ -39,6 +39,11 @@ ASTNodeDataTypeFlattener::ASTNodeDataTypeFlattener(ASTNode& node, FlattenedDataT
             flattened_datatype_list.push_back(
               {ASTNodeDataType::build<ASTNodeDataType::type_id_t>(image_sub_domain->m_data_type.nameOfTypeId()), node});
             break;
+          }
+          case ASTNodeDataType::tuple_t: {
+            flattened_datatype_list.push_back(
+              {ASTNodeDataType::build<ASTNodeDataType::tuple_t>(image_sub_domain->m_data_type.contentType()), node});
+            break;
           }
             // LCOV_EXCL_START
           default: {
diff --git a/src/language/ast/ASTNodeExpressionBuilder.cpp b/src/language/ast/ASTNodeExpressionBuilder.cpp
index 70722aea1a1ac73851df7157820a029e87266a66..4ce7c9c026d41db6c3540b97bbfacb0844af5d02 100644
--- a/src/language/ast/ASTNodeExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeExpressionBuilder.cpp
@@ -182,6 +182,8 @@ ASTNodeExpressionBuilder::_buildExpression(ASTNode& n)
     n.m_node_processor = std::make_unique<BreakProcessor>();
   } else if (n.is_type<language::continue_kw>()) {
     n.m_node_processor = std::make_unique<ContinueProcessor>();
+  } else if (n.is_type<language::empty_set>()) {
+    n.m_node_processor = std::make_unique<FakeProcessor>();
   } else {
     std::ostringstream error_message;
     error_message << "undefined node processor type '" << rang::fgB::red << n.name() << rang::fg::reset << "'";
diff --git a/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp b/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp
index 656000c69cd45b85bcf72907850230c7e0f9615b..7e0d981c33a6d2c9bcd9c8caf79ea7ea95407f4e 100644
--- a/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp
@@ -301,24 +301,30 @@ ASTNodeFunctionExpressionBuilder::_buildArgumentConverter(FunctionDescriptor& fu
 
   const size_t arguments_number = flattened_datatype_list.size();
 
-  const size_t parameters_number =
-    parameter_variables.is_type<language::name_list>() ? parameter_variables.children.size() : 1;
+  const size_t parameters_number = [&]() -> size_t {
+    if (parameter_variables.is_type<language::name_list>()) {
+      return parameter_variables.children.size();
+    } else if (parameter_variables.is_type<language::empty_set>()) {
+      return 0;
+    } else {
+      return 1;
+    }
+  }();
 
   if (arguments_number != parameters_number) {
     std::ostringstream error_message;
-    error_message << "bad number of arguments: expecting " << rang::fgB::yellow << parameters_number
-                  << rang::style::reset << rang::style::bold << ", provided " << rang::fgB::yellow << arguments_number
-                  << rang::style::reset;
+    error_message << "bad number of arguments: expecting " << rang::fgB::yellow << parameters_number << rang::fg::reset
+                  << ", provided " << rang::fgB::yellow << arguments_number;
     throw ParseError(error_message.str(), argument_nodes.begin());
   }
 
-  if (arguments_number > 1) {
+  if (arguments_number == 1) {
+    this->_storeArgumentConverter(parameter_variables, flattened_datatype_list[0], *function_processor);
+  } else if (arguments_number > 1) {
     for (size_t i = 0; i < arguments_number; ++i) {
       ASTNode& parameter_variable = *parameter_variables.children[i];
       this->_storeArgumentConverter(parameter_variable, flattened_datatype_list[i], *function_processor);
     }
-  } else {
-    this->_storeArgumentConverter(parameter_variables, flattened_datatype_list[0], *function_processor);
   }
 
   return function_processor;
@@ -404,7 +410,7 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
         return std::make_unique<FunctionExpressionProcessor<ReturnT, ReturnT>>(function_component_expression);
       } else {
         // LCOV_EXCL_START
-        throw ParseError("unexpected error: invalid dimension for returned vector",
+        throw ParseError("unexpected error: invalid dimensions for returned matrix",
                          std::vector{function_component_expression.begin()});
         // LCOV_EXCL_STOP
       }
@@ -451,6 +457,547 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
     }
   };
 
+  auto get_function_processor_for_expression_tuple_of_value =
+    [&](const auto& tuple_content_v) -> std::unique_ptr<INodeProcessor> {
+    using TupleContentT = std::decay_t<decltype(tuple_content_v)>;
+    switch (function_component_expression.m_data_type) {
+    case ASTNodeDataType::bool_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, bool>>(
+        function_component_expression);
+    }
+    case ASTNodeDataType::unsigned_int_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, uint64_t>>(
+        function_component_expression);
+    }
+    case ASTNodeDataType::int_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, int64_t>>(
+        function_component_expression);
+    }
+    case ASTNodeDataType::double_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, double>>(
+        function_component_expression);
+    }
+    case ASTNodeDataType::string_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::string>>(
+        function_component_expression);
+    }
+    case ASTNodeDataType::type_id_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, EmbeddedData>>(
+        function_component_expression);
+    }
+
+    case ASTNodeDataType::vector_t: {
+      switch (function_component_expression.m_data_type.dimension()) {
+      case 1: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyVector<1>>>(
+          function_component_expression);
+      }
+      case 2: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyVector<2>>>(
+          function_component_expression);
+      }
+      case 3: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyVector<3>>>(
+          function_component_expression);
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined parameter type", std::vector{m_node.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+    case ASTNodeDataType::matrix_t: {
+      if (function_component_expression.m_data_type.numberOfRows() !=
+          function_component_expression.m_data_type.numberOfColumns()) {
+        // LCOV_EXCL_START
+        throw ParseError("unexpected error: invalid dimensions for returned matrix",
+                         std::vector{function_component_expression.begin()});
+        // LCOV_EXCL_STOP
+      }
+      switch (function_component_expression.m_data_type.numberOfRows()) {
+      case 1: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyMatrix<1>>>(
+          function_component_expression);
+      }
+      case 2: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyMatrix<2>>>(
+          function_component_expression);
+      }
+      case 3: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TinyMatrix<3>>>(
+          function_component_expression);
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined parameter type", std::vector{m_node.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+    case ASTNodeDataType::tuple_t: {
+      const ASTNodeDataType& tuple_content_type = function_component_expression.m_data_type.contentType();
+      switch (tuple_content_type) {
+      case ASTNodeDataType::bool_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<bool>>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::unsigned_int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<uint64_t>>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<int64_t>>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::double_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<double>>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::type_id_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<EmbeddedData>>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::vector_t: {
+        switch (tuple_content_type.dimension()) {
+        case 1: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyVector<1>>>>(
+            function_component_expression);
+        }
+        case 2: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyVector<2>>>>(
+            function_component_expression);
+        }
+        case 3: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyVector<3>>>>(
+            function_component_expression);
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined parameter type", std::vector{m_node.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::matrix_t: {
+        if (tuple_content_type.numberOfRows() != tuple_content_type.numberOfColumns()) {
+          // LCOV_EXCL_START
+          throw ParseError("unexpected error: invalid dimensions for returned matrix",
+                           std::vector{function_component_expression.begin()});
+          // LCOV_EXCL_STOP
+        }
+        switch (tuple_content_type.numberOfRows()) {
+        case 1: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyMatrix<1>>>>(
+            function_component_expression);
+        }
+        case 2: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyMatrix<2>>>>(
+            function_component_expression);
+        }
+        case 3: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TinyMatrix<3>>>>(
+            function_component_expression);
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined parameter type", std::vector{m_node.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::string_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<std::string>>>(
+          function_component_expression);
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+    case ASTNodeDataType::list_t: {
+      return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, AggregateDataVariant>>(
+        function_component_expression);
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw ParseError("unexpected error: undefined expression value type for function",
+                       std::vector{function_component_expression.begin()});
+    }
+      // LCOV_EXCL_STOP
+    }
+  };
+
+  auto get_function_processor_for_expression_tuple_of_vector =
+    [&](const auto& tuple_content_v) -> std::unique_ptr<INodeProcessor> {
+    using TupleContentT = std::decay_t<decltype(tuple_content_v)>;
+    if constexpr (TupleContentT::Dimension == 1) {
+      switch (function_component_expression.m_data_type) {
+      case ASTNodeDataType::bool_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, bool>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::unsigned_int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, uint64_t>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, int64_t>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::double_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, double>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::vector_t: {
+        if (function_component_expression.m_data_type.dimension() == TupleContentT::Dimension) {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TupleContentT>>(
+            function_component_expression);
+        } else {
+          // LCOV_EXCL_START
+          throw ParseError("unexpected error: invalid vector_t dimension",
+                           std::vector{function_component_expression.begin()});
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::list_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, AggregateDataVariant>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::tuple_t: {
+        switch (function_component_expression.m_data_type.contentType()) {
+        case ASTNodeDataType::bool_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<bool>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::unsigned_int_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<uint64_t>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::int_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<int64_t>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::double_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<double>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::vector_t: {
+          if (function_component_expression.m_data_type.contentType().dimension() == TupleContentT::Dimension) {
+            return std::make_unique<
+              FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TupleContentT>>>(
+              function_component_expression);
+          } else {
+            // LCOV_EXCL_START
+            throw ParseError("unexpected error: invalid vector_t dimension",
+                             std::vector{function_component_expression.begin()});
+            // LCOV_EXCL_STOP
+          }
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined expression value type for function",
+                           std::vector{function_component_expression.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    } else {
+      switch (function_component_expression.m_data_type) {
+      case ASTNodeDataType::int_t: {
+        if (function_component_expression.is_type<language::integer>()) {
+          if (std::stoi(function_component_expression.string()) == 0) {
+            return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, ZeroType>>(
+              function_component_expression);
+          }
+        }
+        // LCOV_EXCL_START
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+        // LCOV_EXCL_STOP
+      }
+      case ASTNodeDataType::list_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, AggregateDataVariant>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::vector_t: {
+        if (function_component_expression.m_data_type.dimension() == TupleContentT::Dimension) {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TupleContentT>>(
+            function_component_expression);
+        } else {
+          // LCOV_EXCL_START
+          throw ParseError("unexpected error: invalid vector_t dimension",
+                           std::vector{function_component_expression.begin()});
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::tuple_t: {
+        switch (function_component_expression.m_data_type.contentType()) {
+        case ASTNodeDataType::vector_t: {
+          if (function_component_expression.m_data_type.contentType().dimension() == TupleContentT::Dimension) {
+            return std::make_unique<
+              FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TupleContentT>>>(
+              function_component_expression);
+          } else {
+            // LCOV_EXCL_START
+            throw ParseError("unexpected error: invalid vector_t dimension",
+                             std::vector{function_component_expression.begin()});
+            // LCOV_EXCL_STOP
+          }
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined expression value type for function",
+                           std::vector{function_component_expression.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+  };
+
+  auto get_function_processor_for_expression_tuple_of_matrix =
+    [&](const auto& tuple_content_v) -> std::unique_ptr<INodeProcessor> {
+    using TupleContentT = std::decay_t<decltype(tuple_content_v)>;
+    static_assert(TupleContentT::NumberOfColumns == TupleContentT::NumberOfRows);
+
+    if constexpr (TupleContentT::NumberOfRows == 1) {
+      switch (function_component_expression.m_data_type) {
+      case ASTNodeDataType::bool_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, bool>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::unsigned_int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, uint64_t>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::int_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, int64_t>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::double_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, double>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::matrix_t: {
+        if ((function_component_expression.m_data_type.numberOfRows() == TupleContentT::NumberOfRows) and
+            (function_component_expression.m_data_type.numberOfColumns() == TupleContentT::NumberOfColumns)) {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TupleContentT>>(
+            function_component_expression);
+        } else {
+          // LCOV_EXCL_START
+          throw ParseError("unexpected error: invalid matrix_t dimensions",
+                           std::vector{function_component_expression.begin()});
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::list_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, AggregateDataVariant>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::tuple_t: {
+        switch (function_component_expression.m_data_type.contentType()) {
+        case ASTNodeDataType::bool_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<bool>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::unsigned_int_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<uint64_t>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::int_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<int64_t>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::double_t: {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<double>>>(
+            function_component_expression);
+        }
+        case ASTNodeDataType::matrix_t: {
+          if ((function_component_expression.m_data_type.contentType().numberOfRows() ==
+               TupleContentT::NumberOfRows) and
+              (function_component_expression.m_data_type.contentType().numberOfColumns() ==
+               TupleContentT::NumberOfColumns)) {
+            return std::make_unique<
+              FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TupleContentT>>>(
+              function_component_expression);
+          } else {
+            // LCOV_EXCL_START
+            throw ParseError("unexpected error: invalid matrix_t dimensions",
+                             std::vector{function_component_expression.begin()});
+            // LCOV_EXCL_STOP
+          }
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined expression value type for function",
+                           std::vector{function_component_expression.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    } else {
+      switch (function_component_expression.m_data_type) {
+      case ASTNodeDataType::int_t: {
+        if (function_component_expression.is_type<language::integer>()) {
+          if (std::stoi(function_component_expression.string()) == 0) {
+            return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, ZeroType>>(
+              function_component_expression);
+          }
+        }
+        // LCOV_EXCL_START
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+        // LCOV_EXCL_STOP
+      }
+      case ASTNodeDataType::list_t: {
+        return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, AggregateDataVariant>>(
+          function_component_expression);
+      }
+      case ASTNodeDataType::matrix_t: {
+        if ((function_component_expression.m_data_type.numberOfColumns() == TupleContentT::NumberOfColumns) and
+            (function_component_expression.m_data_type.numberOfRows() == TupleContentT::NumberOfRows)) {
+          return std::make_unique<FunctionExpressionProcessor<std::vector<TupleContentT>, TupleContentT>>(
+            function_component_expression);
+        } else {
+          // LCOV_EXCL_START
+          throw ParseError("unexpected error: invalid matrix_t dimensions",
+                           std::vector{function_component_expression.begin()});
+          // LCOV_EXCL_STOP
+        }
+      }
+      case ASTNodeDataType::tuple_t: {
+        switch (function_component_expression.m_data_type.contentType()) {
+        case ASTNodeDataType::matrix_t: {
+          if ((function_component_expression.m_data_type.contentType().numberOfColumns() ==
+               TupleContentT::NumberOfColumns) and
+              (function_component_expression.m_data_type.contentType().numberOfRows() == TupleContentT::NumberOfRows)) {
+            return std::make_unique<
+              FunctionExpressionProcessor<std::vector<TupleContentT>, std::vector<TupleContentT>>>(
+              function_component_expression);
+          } else {
+            // LCOV_EXCL_START
+            throw ParseError("unexpected error: invalid matrix_t dimensions",
+                             std::vector{function_component_expression.begin()});
+            // LCOV_EXCL_STOP
+          }
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: undefined expression value type for function",
+                           std::vector{function_component_expression.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: undefined expression value type for function",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+  };
+
+  auto get_function_processor_for_expression_tuple =
+    [&](const auto& tuple_content_v) -> std::unique_ptr<INodeProcessor> {
+    switch (tuple_content_v) {
+    case ASTNodeDataType::vector_t: {
+      switch (tuple_content_v.dimension()) {
+      case 1: {
+        return get_function_processor_for_expression_tuple_of_vector(TinyVector<1>{});
+      }
+      case 2: {
+        return get_function_processor_for_expression_tuple_of_vector(TinyVector<2>{});
+      }
+      case 3: {
+        return get_function_processor_for_expression_tuple_of_vector(TinyVector<3>{});
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw ParseError("unexpected error: invalid vector_t dimension",
+                         std::vector{function_component_expression.begin()});
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+    case ASTNodeDataType::matrix_t: {
+      if (tuple_content_v.numberOfRows() == tuple_content_v.numberOfColumns()) {
+        switch (tuple_content_v.numberOfRows()) {
+        case 1: {
+          return get_function_processor_for_expression_tuple_of_matrix(TinyMatrix<1>{});
+        }
+        case 2: {
+          return get_function_processor_for_expression_tuple_of_matrix(TinyMatrix<2>{});
+        }
+        case 3: {
+          return get_function_processor_for_expression_tuple_of_matrix(TinyMatrix<3>{});
+        }
+          // LCOV_EXCL_START
+        default: {
+          throw ParseError("unexpected error: invalid vector_t dimension",
+                           std::vector{function_component_expression.begin()});
+        }
+          // LCOV_EXCL_STOP
+        }
+      } else {
+        // LCOV_EXCL_START
+        throw ParseError("unexpected error: invalid dimensions for returned matrix",
+                         std::vector{function_component_expression.begin()});
+        // LCOV_EXCL_STOP
+      }
+    }
+    case ASTNodeDataType::bool_t: {
+      return get_function_processor_for_expression_tuple_of_value(bool{});
+    }
+    case ASTNodeDataType::unsigned_int_t: {
+      return get_function_processor_for_expression_tuple_of_value(uint64_t{});
+    }
+    case ASTNodeDataType::int_t: {
+      return get_function_processor_for_expression_tuple_of_value(int64_t{});
+    }
+    case ASTNodeDataType::double_t: {
+      return get_function_processor_for_expression_tuple_of_value(double{});
+    }
+    case ASTNodeDataType::type_id_t: {
+      return get_function_processor_for_expression_tuple_of_value(EmbeddedData{});
+    }
+    case ASTNodeDataType::string_t: {
+      return get_function_processor_for_expression_tuple_of_value(std::string{});
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError("invalid tuple content type");
+    }
+      // LCOV_EXCL_STOP
+    }
+  };
+
   auto get_function_processor_for_value = [&]() {
     switch (return_value_type) {
     case ASTNodeDataType::bool_t: {
@@ -516,6 +1063,9 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
     }
     case ASTNodeDataType::type_id_t: {
       return get_function_processor_for_expression_type_id(return_value_type.nameOfTypeId());
+    }
+    case ASTNodeDataType::tuple_t: {
+      return get_function_processor_for_expression_tuple(return_value_type.contentType());
     }
       // LCOV_EXCL_START
     default: {
@@ -548,6 +1098,9 @@ ASTNodeFunctionExpressionBuilder::ASTNodeFunctionExpressionBuilder(ASTNode& node
       }
       case ASTNodeDataType::type_id_t: {
         return ASTNodeDataType::build<ASTNodeDataType::type_id_t>(image_domain_node.m_data_type.nameOfTypeId());
+      }
+      case ASTNodeDataType::tuple_t: {
+        return ASTNodeDataType::build<ASTNodeDataType::tuple_t>(image_domain_node.m_data_type.contentType());
       }
         // LCOV_EXCL_START
       default: {
@@ -566,22 +1119,6 @@ ASTNodeFunctionExpressionBuilder::ASTNodeFunctionExpressionBuilder(ASTNode& node
   ASTNode& function_image_domain = *function_descriptor.domainMappingNode().children[1];
   ASTNode& function_expression   = *function_descriptor.definitionNode().children[1];
 
-  const ASTNodeDataType function_return_type = [&] {
-    switch (function_image_domain.m_data_type) {
-    case ASTNodeDataType::typename_t: {
-      return function_image_domain.m_data_type.contentType();
-    }
-    case ASTNodeDataType::type_id_t: {
-      return ASTNodeDataType::build<ASTNodeDataType::type_id_t>(function_image_domain.m_data_type.nameOfTypeId());
-    }
-      // LCOV_EXCL_START
-    default: {
-      throw UnexpectedError("invalid function return type");
-    }
-      // LCOV_EXCL_STOP
-    }
-  }();
-
   if (function_image_domain.is_type<language::vector_type>()) {
     ASTNodeDataType vector_type = getVectorDataType(function_image_domain);
 
@@ -664,6 +1201,10 @@ ASTNodeFunctionExpressionBuilder::ASTNodeFunctionExpressionBuilder(ASTNode& node
       node.m_node_processor = std::move(function_processor);
     }
 
+  } else if (function_image_domain.is_type<language::tuple_type_specifier>()) {
+    add_component_expression(function_expression, function_image_domain);
+
+    node.m_node_processor = std::move(function_processor);
   } else {
     if (function_expression.is_type<language::expression_list>()) {
       ASTNode& image_domain_node = function_image_domain;
diff --git a/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp b/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp
index 5ec9c784508f92d9051e23d72e8cbd420712346c..ec499edb30589824b5578dbc0701db0b3639166b 100644
--- a/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp
@@ -9,6 +9,371 @@
 #include <language/utils/AffectationMangler.hpp>
 #include <language/utils/OperatorRepository.hpp>
 
+template <typename OperatorT, typename TupleContentType>
+void
+ASTNodeListAffectationExpressionBuilder::_buildListAffectationFromTupleProcessor(
+  ListAffectationFromTupleProcessor<OperatorT, TupleContentType>& list_affectation_processor)
+{
+  ASTNode& name_list_node = *m_node.children[0];
+  for (size_t i = 0; i < name_list_node.children.size(); ++i) {
+    ASTNode& value_node = *name_list_node.children[i];
+    if constexpr (std::is_same_v<TupleContentType, bool>) {
+      switch (value_node.m_data_type) {
+      case ASTNodeDataType::bool_t: {
+        list_affectation_processor.template add<bool>(value_node);
+        break;
+      }
+      case ASTNodeDataType::unsigned_int_t: {
+        list_affectation_processor.template add<uint64_t>(value_node);
+        break;
+      }
+      case ASTNodeDataType::int_t: {
+        list_affectation_processor.template add<int64_t>(value_node);
+        break;
+      }
+      case ASTNodeDataType::double_t: {
+        list_affectation_processor.template add<double>(value_node);
+        break;
+      }
+      case ASTNodeDataType::vector_t: {
+        if (value_node.m_data_type.dimension() == 1) {
+          list_affectation_processor.template add<TinyVector<1>>(value_node);
+        } else {
+          // LCOV_EXCL_START
+          throw UnexpectedError("invalid vector dimension");
+          // LCOV_EXCL_STOP
+        }
+        break;
+      }
+      case ASTNodeDataType::matrix_t: {
+        if ((value_node.m_data_type.numberOfRows() == 1) and (value_node.m_data_type.numberOfColumns() == 1)) {
+          list_affectation_processor.template add<TinyMatrix<1>>(value_node);
+        } else {
+          // LCOV_EXCL_START
+          throw UnexpectedError("invalid vector dimension");
+          // LCOV_EXCL_STOP
+        }
+        break;
+      }
+      case ASTNodeDataType::string_t: {
+        list_affectation_processor.template add<std::string>(value_node);
+        break;
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("incompatible tuple types in affectation");
+      }
+        // LCOV_EXCL_STOP
+      }
+    } else if constexpr ((std::is_same_v<TupleContentType, uint64_t>) or (std::is_same_v<TupleContentType, int64_t>)) {
+      switch (value_node.m_data_type) {
+      case ASTNodeDataType::unsigned_int_t: {
+        list_affectation_processor.template add<uint64_t>(value_node);
+        break;
+      }
+      case ASTNodeDataType::int_t: {
+        list_affectation_processor.template add<int64_t>(value_node);
+        break;
+      }
+      case ASTNodeDataType::double_t: {
+        list_affectation_processor.template add<double>(value_node);
+        break;
+      }
+      case ASTNodeDataType::vector_t: {
+        if (value_node.m_data_type.dimension() == 1) {
+          list_affectation_processor.template add<TinyVector<1>>(value_node);
+        } else {
+          // LCOV_EXCL_START
+          throw UnexpectedError("invalid vector dimension");
+          // LCOV_EXCL_STOP
+        }
+        break;
+      }
+      case ASTNodeDataType::matrix_t: {
+        if ((value_node.m_data_type.numberOfRows() == 1) and (value_node.m_data_type.numberOfColumns() == 1)) {
+          list_affectation_processor.template add<TinyMatrix<1>>(value_node);
+        } else {
+          // LCOV_EXCL_START
+          throw UnexpectedError("invalid vector dimension");
+          // LCOV_EXCL_STOP
+        }
+        break;
+      }
+      case ASTNodeDataType::string_t: {
+        list_affectation_processor.template add<std::string>(value_node);
+        break;
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("incompatible tuple types in affectation");
+      }
+        // LCOV_EXCL_STOP
+      }
+    } else if constexpr (std::is_same_v<TupleContentType, double>) {
+      switch (value_node.m_data_type) {
+      case ASTNodeDataType::double_t: {
+        list_affectation_processor.template add<double>(value_node);
+        break;
+      }
+      case ASTNodeDataType::vector_t: {
+        if (value_node.m_data_type.dimension() == 1) {
+          list_affectation_processor.template add<TinyVector<1>>(value_node);
+        } else {
+          // LCOV_EXCL_START
+          throw UnexpectedError("invalid vector dimension");
+          // LCOV_EXCL_STOP
+        }
+        break;
+      }
+      case ASTNodeDataType::matrix_t: {
+        if ((value_node.m_data_type.numberOfRows() == 1) and (value_node.m_data_type.numberOfColumns() == 1)) {
+          list_affectation_processor.template add<TinyMatrix<1>>(value_node);
+        } else {
+          // LCOV_EXCL_START
+          throw UnexpectedError("invalid vector dimension");
+          // LCOV_EXCL_STOP
+        }
+        break;
+      }
+      case ASTNodeDataType::string_t: {
+        list_affectation_processor.template add<std::string>(value_node);
+        break;
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("incompatible tuple types in affectation");
+      }
+        // LCOV_EXCL_STOP
+      }
+    } else if constexpr (is_tiny_vector_v<TupleContentType>) {
+      switch (value_node.m_data_type) {
+      case ASTNodeDataType::vector_t: {
+        if (value_node.m_data_type.dimension() == TupleContentType::Dimension) {
+          list_affectation_processor.template add<TupleContentType>(value_node);
+        } else {
+          // LCOV_EXCL_START
+          throw UnexpectedError("invalid vector dimension");
+          // LCOV_EXCL_STOP
+        }
+        break;
+      }
+      case ASTNodeDataType::string_t: {
+        list_affectation_processor.template add<std::string>(value_node);
+        break;
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("incompatible tuple types in affectation");
+      }
+        // LCOV_EXCL_STOP
+      }
+    } else if constexpr (is_tiny_matrix_v<TupleContentType>) {
+      switch (value_node.m_data_type) {
+      case ASTNodeDataType::matrix_t: {
+        if ((value_node.m_data_type.numberOfRows() == TupleContentType::NumberOfRows) and
+            (value_node.m_data_type.numberOfColumns() == TupleContentType::NumberOfColumns)) {
+          list_affectation_processor.template add<TupleContentType>(value_node);
+        } else {
+          // LCOV_EXCL_START
+          throw UnexpectedError("invalid vector dimension");
+          // LCOV_EXCL_STOP
+        }
+        break;
+      }
+      case ASTNodeDataType::string_t: {
+        list_affectation_processor.template add<std::string>(value_node);
+        break;
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("incompatible tuple types in affectation");
+      }
+        // LCOV_EXCL_STOP
+      }
+    } else if constexpr (std::is_same_v<std::string, TupleContentType>) {
+      if (value_node.m_data_type == ASTNodeDataType::string_t) {
+        list_affectation_processor.template add<std::string>(value_node);
+      } else {
+        // LCOV_EXCL_START
+        throw UnexpectedError("incompatible tuple types in affectation");
+        // LCOV_EXCL_STOP
+      }
+    } else if constexpr (std::is_same_v<EmbeddedData, TupleContentType>) {
+      if (value_node.m_data_type == ASTNodeDataType::type_id_t) {
+        list_affectation_processor.template add<EmbeddedData>(value_node);
+      } else {
+        // LCOV_EXCL_START
+        throw UnexpectedError("incompatible tuple types in affectation");
+        // LCOV_EXCL_STOP
+      }
+    } else {
+      // LCOV_EXCL_START
+      throw UnexpectedError("incompatible tuple types in affectation");
+      // LCOV_EXCL_STOP
+    }
+  }
+}
+
+template <typename OperatorT>
+void
+ASTNodeListAffectationExpressionBuilder::_buildListAffectationFromTupleProcessor()
+{
+  auto& rhs = *m_node.children[1];
+
+  Assert(rhs.m_data_type == ASTNodeDataType::tuple_t);
+
+  const ASTNodeSubDataType tuple_content_type{rhs.m_data_type.contentType(), rhs};
+
+  for (auto&& child : m_node.children[0]->children) {
+    if (child->m_data_type == ASTNodeDataType::tuple_t) {
+      throw ParseError("cannot affect a tuple to a compound type made of tuples", std::vector{child->begin()});
+    }
+
+    ASTNodeNaturalConversionChecker<AllowRToR1Conversion>(tuple_content_type, child->m_data_type);
+  }
+
+  switch (rhs.m_data_type.contentType()) {
+  case ASTNodeDataType::bool_t: {
+    std::unique_ptr list_affectation_processor =
+      std::make_unique<ListAffectationFromTupleProcessor<OperatorT, bool>>(m_node);
+
+    this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+    m_node.m_node_processor = std::move(list_affectation_processor);
+    break;
+  }
+  case ASTNodeDataType::unsigned_int_t: {
+    std::unique_ptr list_affectation_processor =
+      std::make_unique<ListAffectationFromTupleProcessor<OperatorT, uint64_t>>(m_node);
+
+    this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+    m_node.m_node_processor = std::move(list_affectation_processor);
+    break;
+  }
+  case ASTNodeDataType::int_t: {
+    std::unique_ptr list_affectation_processor =
+      std::make_unique<ListAffectationFromTupleProcessor<OperatorT, int64_t>>(m_node);
+
+    this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+    m_node.m_node_processor = std::move(list_affectation_processor);
+    break;
+  }
+  case ASTNodeDataType::double_t: {
+    std::unique_ptr list_affectation_processor =
+      std::make_unique<ListAffectationFromTupleProcessor<OperatorT, double>>(m_node);
+
+    this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+    m_node.m_node_processor = std::move(list_affectation_processor);
+    break;
+  }
+  case ASTNodeDataType::vector_t: {
+    switch (rhs.m_data_type.contentType().dimension()) {
+    case 1: {
+      std::unique_ptr list_affectation_processor =
+        std::make_unique<ListAffectationFromTupleProcessor<OperatorT, TinyVector<1>>>(m_node);
+
+      this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+      m_node.m_node_processor = std::move(list_affectation_processor);
+      break;
+    }
+    case 2: {
+      std::unique_ptr list_affectation_processor =
+        std::make_unique<ListAffectationFromTupleProcessor<OperatorT, TinyVector<2>>>(m_node);
+
+      this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+      m_node.m_node_processor = std::move(list_affectation_processor);
+      break;
+    }
+    case 3: {
+      std::unique_ptr list_affectation_processor =
+        std::make_unique<ListAffectationFromTupleProcessor<OperatorT, TinyVector<3>>>(m_node);
+
+      this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+      m_node.m_node_processor = std::move(list_affectation_processor);
+      break;
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError("invalid vector dimension");
+    }
+      // LCOV_EXCL_STOP
+    }
+    break;
+  }
+  case ASTNodeDataType::matrix_t: {
+    if (rhs.m_data_type.contentType().numberOfColumns() != rhs.m_data_type.contentType().numberOfRows()) {
+      // LCOV_EXCL_START
+      throw UnexpectedError("invalid matrix dimensions");
+      // LCOV_EXCL_STOP
+    }
+    switch (rhs.m_data_type.contentType().numberOfColumns()) {
+    case 1: {
+      std::unique_ptr list_affectation_processor =
+        std::make_unique<ListAffectationFromTupleProcessor<OperatorT, TinyMatrix<1>>>(m_node);
+
+      this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+      m_node.m_node_processor = std::move(list_affectation_processor);
+      break;
+    }
+    case 2: {
+      std::unique_ptr list_affectation_processor =
+        std::make_unique<ListAffectationFromTupleProcessor<OperatorT, TinyMatrix<2>>>(m_node);
+
+      this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+      m_node.m_node_processor = std::move(list_affectation_processor);
+      break;
+    }
+    case 3: {
+      std::unique_ptr list_affectation_processor =
+        std::make_unique<ListAffectationFromTupleProcessor<OperatorT, TinyMatrix<3>>>(m_node);
+
+      this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+      m_node.m_node_processor = std::move(list_affectation_processor);
+      break;
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError("invalid matrix dimensions");
+    }
+      // LCOV_EXCL_STOP
+    }
+    break;
+  }
+  case ASTNodeDataType::string_t: {
+    std::unique_ptr list_affectation_processor =
+      std::make_unique<ListAffectationFromTupleProcessor<OperatorT, std::string>>(m_node);
+
+    this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+    m_node.m_node_processor = std::move(list_affectation_processor);
+    break;
+  }
+  case ASTNodeDataType::type_id_t: {
+    std::unique_ptr list_affectation_processor =
+      std::make_unique<ListAffectationFromTupleProcessor<OperatorT, EmbeddedData>>(m_node);
+
+    this->_buildListAffectationFromTupleProcessor(*list_affectation_processor);
+
+    m_node.m_node_processor = std::move(list_affectation_processor);
+    break;
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("undefined affectation type");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
+
 template <typename OperatorT>
 void
 ASTNodeListAffectationExpressionBuilder::_buildAffectationProcessor(
@@ -905,13 +1270,18 @@ ASTNodeListAffectationExpressionBuilder::_buildListAffectationProcessor()
 ASTNodeListAffectationExpressionBuilder::ASTNodeListAffectationExpressionBuilder(ASTNode& node) : m_node(node)
 {
   if (node.children[1]->is_type<language::expression_list>() or
-      node.children[1]->is_type<language::function_evaluation>()) {
+      node.children[1]->is_type<language::function_evaluation>() or
+      (node.children[1]->m_data_type == ASTNodeDataType::tuple_t)) {
     if (node.is_type<language::eq_op>()) {
-      this->_buildListAffectationProcessor<language::eq_op>();
+      if (node.children[1]->m_data_type == ASTNodeDataType::tuple_t) {
+        this->_buildListAffectationFromTupleProcessor<language::eq_op>();
+      } else {
+        this->_buildListAffectationProcessor<language::eq_op>();
+      }
     } else {
-      throw ParseError("undefined affectation operator for tuples", std::vector{node.begin()});
+      throw ParseError("undefined affectation operator for lists", std::vector{node.begin()});
     }
   } else {
-    throw ParseError("invalid right hand side in tuple affectation", std::vector{node.children[1]->begin()});
+    throw ParseError("invalid right hand side in list affectation", std::vector{node.children[1]->begin()});
   }
 }
diff --git a/src/language/ast/ASTNodeListAffectationExpressionBuilder.hpp b/src/language/ast/ASTNodeListAffectationExpressionBuilder.hpp
index 50aaf94d27d364ecb5f40a3b125e09470c4b9d56..1cda4a94402b9d1803e0731484bf51a4eb8c2a14 100644
--- a/src/language/ast/ASTNodeListAffectationExpressionBuilder.hpp
+++ b/src/language/ast/ASTNodeListAffectationExpressionBuilder.hpp
@@ -4,6 +4,9 @@
 #include <language/ast/ASTNode.hpp>
 #include <language/ast/ASTNodeSubDataType.hpp>
 
+template <typename OperatorT, typename TupleType>
+class ListAffectationFromTupleProcessor;
+
 template <typename OperatorT>
 class ListAffectationProcessor;
 
@@ -12,6 +15,12 @@ class ASTNodeListAffectationExpressionBuilder
  private:
   ASTNode& m_node;
 
+  template <typename OperatorT, typename TupleContentType>
+  void _buildListAffectationFromTupleProcessor(ListAffectationFromTupleProcessor<OperatorT, TupleContentType>&);
+
+  template <typename OperatorT>
+  void _buildListAffectationFromTupleProcessor();
+
   template <typename OperatorT>
   void _buildAffectationProcessor(const ASTNodeSubDataType& rhs_node_sub_data_type,
                                   ASTNode& value_node,
diff --git a/src/language/ast/ASTSymbolTableBuilder.cpp b/src/language/ast/ASTSymbolTableBuilder.cpp
index 87baa5716cd36bd19db71e81e8e4398bfcb2e1fe..0fad55b80e7df5cdafb653fadbc08a5623818137 100644
--- a/src/language/ast/ASTSymbolTableBuilder.cpp
+++ b/src/language/ast/ASTSymbolTableBuilder.cpp
@@ -100,7 +100,9 @@ ASTSymbolTableBuilder::buildSymbolTable(ASTNode& n, std::shared_ptr<SymbolTable>
           i_symbol->attributes().setIsInitialized();
         };
 
-        if (n.children[0]->is_type<language::name>()) {
+        if (n.children[0]->is_type<language::empty_set>()) {
+          // nothing to do
+        } else if (n.children[0]->is_type<language::name>()) {
           register_and_initialize_symbol(*n.children[0]);
         } else {   // treats the case of list of parameters
           Assert(n.children[0]->is_type<language::name_list>());
diff --git a/src/language/modules/MeshModule.cpp b/src/language/modules/MeshModule.cpp
index af1fca45e7dec5898a12731430358691728c7a2a..1f2d0ce4db829694dca9875593f168ebcac47b6a 100644
--- a/src/language/modules/MeshModule.cpp
+++ b/src/language/modules/MeshModule.cpp
@@ -5,6 +5,7 @@
 #include <language/utils/BinaryOperatorProcessorBuilder.hpp>
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
 #include <language/utils/FunctionTable.hpp>
+#include <language/utils/ItemArrayVariantFunctionInterpoler.hpp>
 #include <language/utils/ItemValueVariantFunctionInterpoler.hpp>
 #include <language/utils/OStream.hpp>
 #include <language/utils/OperatorRepository.hpp>
@@ -17,6 +18,7 @@
 #include <mesh/GmshReader.hpp>
 #include <mesh/IBoundaryDescriptor.hpp>
 #include <mesh/IZoneDescriptor.hpp>
+#include <mesh/ItemArrayVariant.hpp>
 #include <mesh/ItemValueVariant.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshRelaxer.hpp>
@@ -25,6 +27,7 @@
 #include <mesh/NamedZoneDescriptor.hpp>
 #include <mesh/NumberedBoundaryDescriptor.hpp>
 #include <mesh/NumberedZoneDescriptor.hpp>
+#include <mesh/SubItemArrayPerItemVariant.hpp>
 #include <mesh/SubItemValuePerItemVariant.hpp>
 #include <utils/Exceptions.hpp>
 
@@ -39,7 +42,9 @@ MeshModule::MeshModule()
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const ItemType>>);
 
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const ItemValueVariant>>);
+  this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const ItemArrayVariant>>);
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const SubItemValuePerItemVariant>>);
+  this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const SubItemArrayPerItemVariant>>);
 
   this->_addBuiltinFunction("cell", std::function(
 
@@ -125,6 +130,17 @@ MeshModule::MeshModule()
 
                               ));
 
+  this->_addBuiltinFunction(
+    "interpolate_array",
+    std::function(
+
+      [](std::shared_ptr<const IMesh> mesh, std::shared_ptr<const ItemType> item_type,
+         const std::vector<FunctionSymbolId>& function_id_list) -> std::shared_ptr<const ItemArrayVariant> {
+        return ItemArrayVariantFunctionInterpoler{mesh, *item_type, function_id_list}.interpolate();
+      }
+
+      ));
+
   this->_addBuiltinFunction("transform", std::function(
 
                                            [](std::shared_ptr<const IMesh> p_mesh,
diff --git a/src/language/modules/MeshModule.hpp b/src/language/modules/MeshModule.hpp
index bf9f0b714b327a1216f7ec3e54fc98a3ec65d70f..580f25aa839e2c1cc691247dd7359d8a6c1aa036 100644
--- a/src/language/modules/MeshModule.hpp
+++ b/src/language/modules/MeshModule.hpp
@@ -29,11 +29,21 @@ template <>
 inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const ItemValueVariant>> =
   ASTNodeDataType::build<ASTNodeDataType::type_id_t>("item_value");
 
+class ItemArrayVariant;
+template <>
+inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const ItemArrayVariant>> =
+  ASTNodeDataType::build<ASTNodeDataType::type_id_t>("item_array");
+
 class SubItemValuePerItemVariant;
 template <>
 inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const SubItemValuePerItemVariant>> =
   ASTNodeDataType::build<ASTNodeDataType::type_id_t>("sub_item_value");
 
+class SubItemArrayPerItemVariant;
+template <>
+inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const SubItemArrayPerItemVariant>> =
+  ASTNodeDataType::build<ASTNodeDataType::type_id_t>("sub_item_array");
+
 class MeshModule : public BuiltinModule
 {
  public:
diff --git a/src/language/modules/WriterModule.cpp b/src/language/modules/WriterModule.cpp
index aa479c0eb94005d59000d98dd460d8fedb89ab21..af7d0cb4b1cf50de79462a56d07f262cf1bf95ad 100644
--- a/src/language/modules/WriterModule.cpp
+++ b/src/language/modules/WriterModule.cpp
@@ -4,12 +4,14 @@
 #include <language/utils/TypeDescriptor.hpp>
 #include <mesh/Connectivity.hpp>
 #include <mesh/GmshReader.hpp>
+#include <mesh/ItemArrayVariant.hpp>
 #include <mesh/ItemValueVariant.hpp>
 #include <mesh/Mesh.hpp>
 #include <output/GnuplotWriter.hpp>
 #include <output/GnuplotWriter1D.hpp>
 #include <output/IWriter.hpp>
 #include <output/NamedDiscreteFunction.hpp>
+#include <output/NamedItemArrayVariant.hpp>
 #include <output/NamedItemValueVariant.hpp>
 #include <output/VTKWriter.hpp>
 #include <scheme/DiscreteFunctionVariant.hpp>
@@ -80,6 +82,7 @@ WriterModule::WriterModule()
                                              }
 
                                              ));
+
   this->_addBuiltinFunction("name_output", std::function(
 
                                              [](std::shared_ptr<const ItemValueVariant> item_value_variant,
@@ -90,6 +93,16 @@ WriterModule::WriterModule()
 
                                              ));
 
+  this->_addBuiltinFunction("name_output", std::function(
+
+                                             [](std::shared_ptr<const ItemArrayVariant> item_array_variant,
+                                                const std::string& name) -> std::shared_ptr<const INamedDiscreteData> {
+                                               return std::make_shared<const NamedItemArrayVariant>(item_array_variant,
+                                                                                                    name);
+                                             }
+
+                                             ));
+
   this->_addBuiltinFunction("write_mesh",
                             std::function(
 
diff --git a/src/language/node_processor/AffectationProcessor.hpp b/src/language/node_processor/AffectationProcessor.hpp
index f4c0d731a7a11ab91ffe7cb005bf14ebb5525dcf..8fea3a7546b9f96298e97b0603a849c84915f47a 100644
--- a/src/language/node_processor/AffectationProcessor.hpp
+++ b/src/language/node_processor/AffectationProcessor.hpp
@@ -82,7 +82,7 @@ struct AffOp<language::minuseq_op>
 
 struct IAffectationExecutor
 {
-  virtual void affect(ExecutionPolicy& exec_policy, DataVariant&& rhs) = 0;
+  virtual void affect(DataVariant&& rhs) = 0;
 
   IAffectationExecutor(const IAffectationExecutor&) = delete;
   IAffectationExecutor(IAffectationExecutor&&)      = delete;
@@ -101,9 +101,7 @@ class AffectationExecutor final : public IAffectationExecutor
 
   static inline const bool m_is_defined{[] {
     if constexpr (std::is_same_v<std::decay_t<ValueT>, bool>) {
-      if constexpr (not std::is_same_v<OperatorT, language::eq_op>) {
-        return false;
-      }
+      return std::is_same_v<OperatorT, language::eq_op>;
     }
     return true;
   }()};
@@ -119,7 +117,7 @@ class AffectationExecutor final : public IAffectationExecutor
   }
 
   PUGS_INLINE void
-  affect(ExecutionPolicy&, DataVariant&& rhs)
+  affect(DataVariant&& rhs)
   {
     if constexpr (m_is_defined) {
       if constexpr (not std::is_same_v<DataT, ZeroType>) {
@@ -459,7 +457,7 @@ class AffectationProcessor final : public INodeProcessor
   execute(ExecutionPolicy& exec_policy)
   {
     try {
-      m_affectation_executor->affect(exec_policy, m_rhs_node.execute(exec_policy));
+      m_affectation_executor->affect(m_rhs_node.execute(exec_policy));
     }
     catch (std::domain_error& e) {
       throw ParseError(e.what(), m_rhs_node.begin());
@@ -550,20 +548,76 @@ class AffectationToTupleProcessor final : public AffectationToDataVariantProcess
 };
 
 template <typename ValueT>
-class AffectationToTupleFromListProcessor final : public AffectationToDataVariantProcessorBase
+class AffectationFromTupleProcessor final : public AffectationToDataVariantProcessorBase
 {
  private:
+  ASTNode& m_lhs_node;
   ASTNode& m_rhs_node;
 
-  template <typename T>
-  std::string
-  stringify(const T& value)
+ public:
+  DataVariant
+  execute(ExecutionPolicy& exec_policy)
   {
-    std::ostringstream os;
-    os << std::boolalpha << value;
-    return os.str();
+    DataVariant value = m_rhs_node.execute(exec_policy);
+
+    try {
+      std::visit(
+        [&](auto&& v) {
+          using T = std::decay_t<decltype(v)>;
+          if constexpr (is_std_vector_v<T>) {
+            using TupleContentType = std::decay_t<typename T::value_type>;
+            if (v.size() == 1) {
+              if constexpr (std::is_same_v<TupleContentType, ValueT>) {
+                *m_lhs = std::move(static_cast<ValueT>(v[0]));
+              } else if constexpr (std::is_arithmetic_v<ValueT> and std::is_convertible_v<TupleContentType, ValueT>) {
+                if constexpr (std::is_same_v<uint64_t, ValueT> and std::is_same_v<int64_t, TupleContentType>) {
+                  if (v[0] < 0) {
+                    throw std::domain_error("trying to affect negative value (" + stringify(v[0]) + ")");
+                  }
+                }
+                *m_lhs = std::move(static_cast<ValueT>(v[0]));
+              } else if constexpr ((std::is_same_v<ValueT, TinyVector<1>> or
+                                    std::is_same_v<ValueT, TinyMatrix<1>>)and(std::is_arithmetic_v<TupleContentType>)) {
+                *m_lhs = std::move(ValueT(v[0]));
+              } else if constexpr (std::is_same_v<std::string, ValueT>) {
+                *m_lhs = std::move(stringify(static_cast<TupleContentType>(v[0])));
+              } else {
+                // LCOV_EXCL_START
+                throw UnexpectedError("unexpected rhs tuple content");
+                // LCOV_EXCL_STOP
+              }
+            } else {
+              std::ostringstream error_msg;
+              error_msg << "cannot affect a " << rang::fgB::yellow << dataTypeName(m_rhs_node.m_data_type)
+                        << rang::fg::reset << " of size " << v.size() << " to a " << rang::fgB::yellow
+                        << dataTypeName(m_lhs_node.m_data_type) << rang::fg::reset;
+              throw ParseError(error_msg.str(), m_rhs_node.begin());
+            }
+          } else {
+            // LCOV_EXCL_START
+            throw UnexpectedError("right hand side must be a tuple");
+            // LCOV_EXCL_STOP
+          }
+        },
+        value);
+    }
+    catch (std::domain_error& e) {
+      throw ParseError(e.what(), m_rhs_node.begin());
+    }
+    return {};
   }
 
+  AffectationFromTupleProcessor(ASTNode& lhs_node, ASTNode& rhs_node)
+    : AffectationToDataVariantProcessorBase(lhs_node), m_lhs_node(lhs_node), m_rhs_node{rhs_node}
+  {}
+};
+
+template <typename ValueT>
+class AffectationToTupleFromListProcessor final : public AffectationToDataVariantProcessorBase
+{
+ private:
+  ASTNode& m_rhs_node;
+
   void
   _copyAggregateDataVariant(const AggregateDataVariant& children_values)
   {
@@ -627,15 +681,48 @@ class AffectationToTupleFromListProcessor final : public AffectationToDataVarian
     *m_lhs = std::move(tuple_value);
   }
 
+ public:
+  DataVariant
+  execute(ExecutionPolicy& exec_policy)
+  {
+    try {
+      std::visit(
+        [&](auto&& value_list) {
+          using ValueListT = std::decay_t<decltype(value_list)>;
+          if constexpr (std::is_same_v<AggregateDataVariant, ValueListT>) {
+            this->_copyAggregateDataVariant(value_list);
+          } else {
+            // LCOV_EXCL_START
+            throw UnexpectedError("invalid rhs (expecting list)");
+            // LCOV_EXCL_STOP
+          }
+        },
+        m_rhs_node.execute(exec_policy));
+    }
+    catch (std::domain_error& e) {
+      throw ParseError(e.what(), m_rhs_node.begin());   // LCOV_EXCL_LINE
+    }
+    return {};
+  }
+
+  AffectationToTupleFromListProcessor(ASTNode& lhs_node, ASTNode& rhs_node)
+    : AffectationToDataVariantProcessorBase(lhs_node), m_rhs_node{rhs_node}
+  {}
+};
+
+template <typename ValueT>
+class AffectationToTupleFromTupleProcessor final : public AffectationToDataVariantProcessorBase
+{
+ private:
+  ASTNode& m_rhs_node;
+
   template <typename DataType>
   void
   _copyVector(const std::vector<DataType>& values)
   {
     std::vector<ValueT> v(values.size());
     if constexpr (std::is_same_v<ValueT, DataType>) {
-      for (size_t i = 0; i < values.size(); ++i) {
-        v[i] = values[i];
-      }
+      v = std::move(values);
     } else if constexpr (std::is_arithmetic_v<ValueT> and std::is_convertible_v<DataType, ValueT>) {
       for (size_t i = 0; i < values.size(); ++i) {
         const DataType& vi = values[i];
@@ -646,6 +733,14 @@ class AffectationToTupleFromListProcessor final : public AffectationToDataVarian
         }
         v[i] = static_cast<DataType>(vi);
       }
+    } else if constexpr (std::is_same_v<TinyVector<1>, ValueT> and std::is_arithmetic_v<DataType>) {
+      for (size_t i = 0; i < values.size(); ++i) {
+        v[i][0] = values[i];
+      }
+    } else if constexpr (std::is_same_v<TinyMatrix<1>, ValueT> and std::is_arithmetic_v<DataType>) {
+      for (size_t i = 0; i < values.size(); ++i) {
+        v[i](0, 0) = values[i];
+      }
     } else if constexpr (std::is_same_v<ValueT, std::string>) {
       for (size_t i = 0; i < values.size(); ++i) {
         v[i] = std::move(stringify(values[i]));
@@ -667,13 +762,11 @@ class AffectationToTupleFromListProcessor final : public AffectationToDataVarian
       std::visit(
         [&](auto&& value_list) {
           using ValueListT = std::decay_t<decltype(value_list)>;
-          if constexpr (std::is_same_v<AggregateDataVariant, ValueListT>) {
-            this->_copyAggregateDataVariant(value_list);
-          } else if constexpr (is_std_vector_v<ValueListT>) {
+          if constexpr (is_std_vector_v<ValueListT>) {
             this->_copyVector(value_list);
           } else {
             // LCOV_EXCL_START
-            throw UnexpectedError("invalid lhs (expecting list or tuple)");
+            throw UnexpectedError("invalid rhs (expecting tuple)");
             // LCOV_EXCL_STOP
           }
         },
@@ -685,7 +778,7 @@ class AffectationToTupleFromListProcessor final : public AffectationToDataVarian
     return {};
   }
 
-  AffectationToTupleFromListProcessor(ASTNode& lhs_node, ASTNode& rhs_node)
+  AffectationToTupleFromTupleProcessor(ASTNode& lhs_node, ASTNode& rhs_node)
     : AffectationToDataVariantProcessorBase(lhs_node), m_rhs_node{rhs_node}
   {}
 };
@@ -744,7 +837,7 @@ class ListAffectationProcessor final : public INodeProcessor
     Assert(m_affectation_executor_list.size() == children_values.size());
     for (size_t i = 0; i < m_affectation_executor_list.size(); ++i) {
       try {
-        m_affectation_executor_list[i]->affect(exec_policy, std::move(children_values[i]));
+        m_affectation_executor_list[i]->affect(std::move(children_values[i]));
       }
       catch (std::domain_error& e) {
         throw ParseError(e.what(), m_node.children[1]->children[i]->begin());
@@ -756,4 +849,123 @@ class ListAffectationProcessor final : public INodeProcessor
   ListAffectationProcessor(ASTNode& node) : m_node{node} {}
 };
 
+struct IAffectationFromTupleExecutor
+{
+  virtual void affect(ExecutionPolicy& exec_policy, DataVariant&& rhs) = 0;
+
+  IAffectationFromTupleExecutor(const IAffectationFromTupleExecutor&) = delete;
+  IAffectationFromTupleExecutor(IAffectationFromTupleExecutor&&)      = delete;
+
+  IAffectationFromTupleExecutor() = default;
+
+  virtual ~IAffectationFromTupleExecutor() = default;
+};
+
+template <typename OperatorT, typename ValueT, typename DataT>
+class AffectationFromTupleExecutor final : public IAffectationFromTupleExecutor
+{
+ private:
+  DataVariant& m_lhs;
+  ASTNode& m_node;
+
+  static_assert(std::is_same_v<OperatorT, language::eq_op>);
+
+ public:
+  AffectationFromTupleExecutor(ASTNode& node, DataVariant& lhs) : m_lhs{lhs}, m_node{node} {}
+
+  PUGS_INLINE void
+  affect(ExecutionPolicy&, DataVariant&& rhs) final
+  {
+    if constexpr (std::is_same_v<DataT, ValueT>) {
+      m_lhs = rhs;
+    } else if constexpr (std::is_convertible_v<DataT, ValueT>) {
+      const DataT& v = std::get<DataT>(rhs);
+      if constexpr (std::is_same_v<int64_t, DataT> and std::is_same_v<uint64_t, ValueT>) {
+        if (v < 0) {
+          throw std::domain_error("trying to affect negative value (" + stringify(v) + ")");
+        }
+      }
+      m_lhs = static_cast<ValueT>(v);
+    } else if constexpr ((std::is_same_v<ValueT, TinyVector<1>>)and(std::is_arithmetic_v<DataT>)) {
+      m_lhs = std::move(ValueT(std::get<DataT>(rhs)));
+    } else if constexpr ((std::is_same_v<ValueT, TinyMatrix<1>>)and(std::is_arithmetic_v<DataT>)) {
+      m_lhs = std::move(ValueT(std::get<DataT>(rhs)));
+    } else if constexpr (std::is_same_v<ValueT, std::string>) {
+      m_lhs = std::move(stringify(std::get<DataT>(rhs)));
+    } else {
+      // LCOV_EXCL_START
+      throw UnexpectedError("incompatible types in affectation from tuple");
+      // LCOV_EXCL_STOP
+    }
+  }
+};
+
+template <typename OperatorT, typename TupleContentType>
+class ListAffectationFromTupleProcessor final : public INodeProcessor
+{
+ public:
+  static_assert(not is_std_vector_v<TupleContentType>);
+
+ private:
+  ASTNode& m_node;
+
+  std::vector<std::unique_ptr<IAffectationFromTupleExecutor>> m_affectation_executor_list;
+
+ public:
+  template <typename ValueT>
+  void
+  add(ASTNode& lhs_node)
+  {
+    using DataT = std::decay_t<TupleContentType>;
+
+    using AffectationExecutorT = AffectationFromTupleExecutor<OperatorT, ValueT, DataT>;
+
+    if (lhs_node.is_type<language::name>()) {
+      const std::string& symbol = lhs_node.string();
+      auto [i_symbol, found]    = m_node.m_symbol_table->find(symbol, m_node.children[0]->end());
+      Assert(found);
+      DataVariant& value = i_symbol->attributes().value();
+
+      if (not std::holds_alternative<ValueT>(value)) {
+        value = ValueT{};
+      }
+
+      m_affectation_executor_list.emplace_back(std::make_unique<AffectationExecutorT>(m_node, value));
+    } else {
+      // LCOV_EXCL_START
+      throw UnexpectedError("invalid left hand side");
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  DataVariant
+  execute(ExecutionPolicy& exec_policy)
+  {
+    using TupleType = std::vector<TupleContentType>;
+
+    TupleType tuple_values = std::move(std::get<TupleType>(m_node.children[1]->execute(exec_policy)));
+
+    if (m_affectation_executor_list.size() == tuple_values.size()) {
+      for (size_t i = 0; i < m_affectation_executor_list.size(); ++i) {
+        try {
+          m_affectation_executor_list[i]->affect(exec_policy,
+                                                 DataVariant(static_cast<TupleContentType>(tuple_values[i])));
+        }
+        catch (std::domain_error& e) {
+          throw ParseError(e.what(), m_node.children[1]->begin());
+        }
+      }
+    } else {
+      std::ostringstream error_msg;
+      error_msg << "cannot affect a " << rang::fgB::yellow << dataTypeName(m_node.children[1]->m_data_type)
+                << rang::fg::reset << " of size " << tuple_values.size() << " to a " << rang::fgB::yellow
+                << dataTypeName(m_node.children[0]->m_data_type) << rang::fg::reset;
+      throw ParseError(error_msg.str(), m_node.children[1]->begin());
+    }
+    return {};
+  }
+
+  ListAffectationFromTupleProcessor(ASTNode& node) : m_node{node} {}
+};
+
 #endif   // AFFECTATION_PROCESSOR_HPP
diff --git a/src/language/node_processor/FunctionArgumentConverter.hpp b/src/language/node_processor/FunctionArgumentConverter.hpp
index 6a4075307e3af9f225cd493176fbd0a7873a3c75..36c5bab29c2517cffbec47476f2d74da55943667 100644
--- a/src/language/node_processor/FunctionArgumentConverter.hpp
+++ b/src/language/node_processor/FunctionArgumentConverter.hpp
@@ -131,8 +131,10 @@ class FunctionTinyVectorArgumentConverter final : public IFunctionArgumentConver
           std::move(static_cast<ExpectedValueType>(std::get<ProvidedValueType>(value)));
       }
     } else {
+      // LCOV_EXCL_START
       throw UnexpectedError(std::string{"cannot convert '"} + demangle<ProvidedValueType>() + "' to '" +
                             demangle<ExpectedValueType>() + "'");
+      // LCOV_EXCL_STOP
     }
     return {};
   }
@@ -193,8 +195,10 @@ class FunctionTinyMatrixArgumentConverter final : public IFunctionArgumentConver
           std::move(static_cast<ExpectedValueType>(std::get<ProvidedValueType>(value)));
       }
     } else {
+      // LCOV_EXCL_START
       throw UnexpectedError(std::string{"cannot convert '"} + demangle<ProvidedValueType>() + "' to '" +
                             demangle<ExpectedValueType>() + "'");
+      // LCOV_EXCL_STOP
     }
 
     return {};
diff --git a/src/language/node_processor/FunctionProcessor.hpp b/src/language/node_processor/FunctionProcessor.hpp
index bc8c3c0bdd6d9cd5a7e1b8fb91f7e21e3c45f8a8..2b3e65ecb130c6f50105a5cba08552e43e16596c 100644
--- a/src/language/node_processor/FunctionProcessor.hpp
+++ b/src/language/node_processor/FunctionProcessor.hpp
@@ -38,6 +38,224 @@ class FunctionExpressionProcessor final : public INodeProcessor
                          (is_tiny_vector_v<ReturnType> or is_tiny_matrix_v<ReturnType>)) {
       static_assert(ReturnType::Dimension == 1, "invalid conversion");
       return ReturnType(std::get<ExpressionValueType>(m_function_expression.execute(exec_policy)));
+    } else if constexpr (is_std_vector_v<ReturnType>) {
+      auto expression_value = m_function_expression.execute(exec_policy);
+
+      const ExpressionValueType& v = std::get<ExpressionValueType>(expression_value);
+
+      using TupleContentType = std::decay_t<typename ReturnType::value_type>;
+
+      if constexpr (std::is_convertible_v<ExpressionValueType, TupleContentType>) {
+        if constexpr (std::is_same_v<TupleContentType, uint64_t> and std::is_same_v<ExpressionValueType, int64_t>) {
+          if (v < 0) {
+            throw std::domain_error("trying to convert negative value (" + stringify(v) + ")");
+          }
+        }
+        return ReturnType{static_cast<TupleContentType>(v)};
+      } else if constexpr (is_tiny_matrix_v<TupleContentType>) {
+        static_assert(ReturnType::value_type::NumberOfRows == ReturnType::value_type::NumberOfColumns);
+        if constexpr (ReturnType::value_type::NumberOfRows == 1) {
+          if constexpr (std::is_arithmetic_v<ExpressionValueType>) {
+            return ReturnType{TupleContentType(v)};
+          } else if constexpr (std::is_same_v<ExpressionValueType, AggregateDataVariant>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < result.size(); ++i) {
+              std::visit(
+                [&](auto&& v_i) {
+                  using ListElementType = std::decay_t<decltype(v_i)>;
+                  if constexpr ((std::is_same_v<ListElementType, TupleContentType>) or
+                                ((std::is_arithmetic_v<ListElementType>))) {
+                    result[i] = static_cast<TupleContentType>(v_i);
+                  } else {
+                    // LCOV_EXCL_START
+                    throw UnexpectedError("invalid list element type");
+                    // LCOV_EXCL_STOP
+                  }
+                },
+                v[i]);
+            }
+            return result;
+          } else {
+            static_assert(is_std_vector_v<ExpressionValueType>);
+            ReturnType result(v.size());
+            for (size_t i = 0; i < v.size(); ++i) {
+              result[i] = static_cast<TupleContentType>(v[i]);
+            }
+            return result;
+          }
+        } else {
+          if constexpr (std::is_same_v<ExpressionValueType, int64_t>) {
+            return ReturnType{TupleContentType(ZeroType::zero)};
+          } else if constexpr (std::is_same_v<ExpressionValueType, AggregateDataVariant>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < result.size(); ++i) {
+              std::visit(
+                [&](auto&& v_i) {
+                  using ListElementType = std::decay_t<decltype(v_i)>;
+                  if constexpr (std::is_same_v<ListElementType, TupleContentType>) {
+                    result[i] = v_i;
+                  } else if constexpr (std::is_same_v<ListElementType, int64_t>) {
+                    result[i] = ZeroType::zero;
+                  } else {
+                    // LCOV_EXCL_START
+                    throw UnexpectedError("invalid list element type");
+                    // LCOV_EXCL_STOP
+                  }
+                },
+                v[i]);
+            }
+            return result;
+          } else if constexpr (is_std_vector_v<ExpressionValueType>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < v.size(); ++i) {
+              if constexpr (std::is_same_v<typename ExpressionValueType::value_type, TupleContentType>) {
+                result[i] = v[i];
+              } else if constexpr (std::is_same_v<typename ExpressionValueType::value_type, int64_t>) {
+                result[i] = ZeroType::zero;
+              } else {
+                // LCOV_EXCL_START
+                throw UnexpectedError("invalid list element type");
+                // LCOV_EXCL_STOP
+              }
+            }
+            return result;
+          } else {
+            // LCOV_EXCL_START
+            throw UnexpectedError("invalid expression value");
+            // LCOV_EXCL_STOP
+          }
+        }
+      } else if constexpr (is_tiny_vector_v<TupleContentType>) {
+        if constexpr (ReturnType::value_type::Dimension == 1) {
+          if constexpr (std::is_arithmetic_v<ExpressionValueType>) {
+            return ReturnType{TupleContentType(v)};
+          } else if constexpr (std::is_same_v<ExpressionValueType, AggregateDataVariant>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < result.size(); ++i) {
+              std::visit(
+                [&](auto&& v_i) {
+                  using ListElementType = std::decay_t<decltype(v_i)>;
+                  if constexpr ((std::is_same_v<ListElementType, TupleContentType>) or
+                                ((std::is_arithmetic_v<ListElementType>))) {
+                    result[i] = static_cast<TupleContentType>(v_i);
+                  } else {
+                    // LCOV_EXCL_START
+                    throw UnexpectedError("invalid list element type");
+                    // LCOV_EXCL_STOP
+                  }
+                },
+                v[i]);
+            }
+            return result;
+          } else {
+            static_assert(is_std_vector_v<ExpressionValueType>);
+            ReturnType result(v.size());
+            for (size_t i = 0; i < v.size(); ++i) {
+              result[i] = static_cast<TupleContentType>(v[i]);
+            }
+            return result;
+          }
+        } else {
+          if constexpr (std::is_same_v<ExpressionValueType, int64_t>) {
+            return ReturnType{TupleContentType(ZeroType::zero)};
+          } else if constexpr (std::is_same_v<ExpressionValueType, AggregateDataVariant>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < result.size(); ++i) {
+              std::visit(
+                [&](auto&& v_i) {
+                  using ListElementType = std::decay_t<decltype(v_i)>;
+                  if constexpr (std::is_same_v<ListElementType, TupleContentType>) {
+                    result[i] = v_i;
+                  } else if constexpr (std::is_same_v<ListElementType, int64_t>) {
+                    result[i] = ZeroType::zero;
+                  } else {
+                    // LCOV_EXCL_START
+                    throw UnexpectedError("invalid list element type");
+                    // LCOV_EXCL_STOP
+                  }
+                },
+                v[i]);
+            }
+            return result;
+          } else if constexpr (is_std_vector_v<ExpressionValueType>) {
+            ReturnType result(v.size());
+            for (size_t i = 0; i < v.size(); ++i) {
+              if constexpr (std::is_same_v<typename ExpressionValueType::value_type, TupleContentType>) {
+                result[i] = v[i];
+              } else if constexpr (std::is_same_v<typename ExpressionValueType::value_type, int64_t>) {
+                result[i] = ZeroType::zero;
+              } else {
+                // LCOV_EXCL_START
+                throw UnexpectedError("invalid tuple element type");
+                // LCOV_EXCL_STOP
+              }
+            }
+            return result;
+          }
+        }
+        // LCOV_EXCL_START
+        throw UnexpectedError("invalid expression type");
+        // LCOV_EXCL_STOP
+      } else if constexpr (std::is_same_v<TupleContentType, std::string>) {
+        return ReturnType{stringify(v)};
+      } else if constexpr (is_std_vector_v<ExpressionValueType>) {
+        using ExpressionContentType = typename ExpressionValueType::value_type;
+        if constexpr (std::is_convertible_v<ExpressionContentType, TupleContentType>) {
+          ReturnType result(v.size());
+          for (size_t i = 0; i < v.size(); ++i) {
+            if constexpr (std::is_same_v<TupleContentType, uint64_t> and
+                          std::is_same_v<ExpressionContentType, int64_t>) {
+              if (v[i] < 0) {
+                throw std::domain_error("trying to convert negative value (" + stringify(v[i]) + ")");
+              }
+            }
+            result[i] = v[i];
+          }
+          return result;
+        } else if constexpr (std::is_same_v<TupleContentType, std::string>) {
+          ReturnType result(v.size());
+          for (size_t i = 0; i < v.size(); ++i) {
+            result[i] = stringify(v[i]);
+          }
+          return result;
+        } else {
+          // LCOV_EXCL_START
+          throw UnexpectedError("invalid tuple type");
+          // LCOV_EXCL_STOP
+        }
+      } else if constexpr (std::is_same_v<AggregateDataVariant, ExpressionValueType>) {
+        ReturnType result(v.size());
+        for (size_t i = 0; i < result.size(); ++i) {
+          std::visit(
+            [&](auto&& v_i) {
+              using ListElementType = std::decay_t<decltype(v_i)>;
+              if constexpr (std::is_convertible_v<ListElementType, TupleContentType>) {
+                if constexpr (std::is_same_v<TupleContentType, uint64_t> and std::is_same_v<ListElementType, int64_t>) {
+                  if (v_i < 0) {
+                    throw std::domain_error("trying to convert negative value (" + stringify(v_i) + ")");
+                  }
+                }
+                result[i] = static_cast<TupleContentType>(v_i);
+              } else if constexpr (std::is_same_v<TupleContentType, std::string>) {
+                if constexpr (std::is_same_v<ListElementType, std::string>) {
+                  result[i] = std::move(v_i);
+                } else {
+                  result[i] = stringify(v_i);
+                }
+              } else {
+                // LCOV_EXCL_START
+                throw UnexpectedError("invalid list element type");
+                // LCOV_EXCL_STOP
+              }
+            },
+            v[i]);
+        }
+        return result;
+      } else {
+        // LCOV_EXCL_START
+        throw UnexpectedError("invalid tuple type");
+        // LCOV_EXCL_STOP
+      }
     } else {
       // LCOV_EXCL_START
       throw UnexpectedError("invalid conversion");
diff --git a/src/language/utils/AffectationMangler.hpp b/src/language/utils/AffectationMangler.hpp
index 85aaab35bfbabde5b9bdbc9b250f8440e68829e6..42592a091d1e1792f99d3d76e5af0d167732aeba 100644
--- a/src/language/utils/AffectationMangler.hpp
+++ b/src/language/utils/AffectationMangler.hpp
@@ -40,8 +40,6 @@ affectationMangler(const ASTNodeDataType& lhs, const ASTNodeDataType& rhs)
   const std::string rhs_name = [&]() -> std::string {
     if (rhs == ASTNodeDataType::list_t) {
       return "list";
-    } else if (rhs == ASTNodeDataType::tuple_t) {
-      return "tuple";
     } else {
       return dataTypeName(rhs);
     }
diff --git a/src/language/utils/AffectationProcessorBuilder.hpp b/src/language/utils/AffectationProcessorBuilder.hpp
index cc66971b2ccfbcc4a9a16ae708121c7905a1478b..6b5ff55841863b9e93afd8a12498bf306e4d51f4 100644
--- a/src/language/utils/AffectationProcessorBuilder.hpp
+++ b/src/language/utils/AffectationProcessorBuilder.hpp
@@ -44,6 +44,18 @@ class AffectationToTupleProcessorBuilder final : public IAffectationProcessorBui
   }
 };
 
+template <typename ValueT>
+class AffectationFromTupleProcessorBuilder final : public IAffectationProcessorBuilder
+{
+ public:
+  AffectationFromTupleProcessorBuilder() = default;
+  std::unique_ptr<INodeProcessor>
+  getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final
+  {
+    return std::make_unique<AffectationFromTupleProcessor<ValueT>>(lhs_node, rhs_node);
+  }
+};
+
 template <typename ValueT>
 class AffectationToTupleFromListProcessorBuilder final : public IAffectationProcessorBuilder
 {
@@ -57,6 +69,19 @@ class AffectationToTupleFromListProcessorBuilder final : public IAffectationProc
   }
 };
 
+template <typename ValueT>
+class AffectationToTupleFromTupleProcessorBuilder final : public IAffectationProcessorBuilder
+{
+ public:
+  AffectationToTupleFromTupleProcessorBuilder() = default;
+  std::unique_ptr<INodeProcessor>
+  getNodeProcessor(ASTNode& lhs_node, ASTNode& rhs_node) const final
+  {
+    ASTNodeNaturalConversionChecker<AllowRToR1Conversion>(rhs_node, lhs_node.m_data_type);
+    return std::make_unique<AffectationToTupleFromTupleProcessor<ValueT>>(lhs_node, rhs_node);
+  }
+};
+
 template <typename OperatorT, typename ValueT>
 class AffectationFromZeroProcessorBuilder final : public IAffectationProcessorBuilder
 {
diff --git a/src/language/utils/AffectationRegisterForN.cpp b/src/language/utils/AffectationRegisterForN.cpp
index 64f93ad0f74da6490b636c3643de62bcf7afc0fc..9340545ea3320cca700b61076a5fce2ed8d9e166 100644
--- a/src/language/utils/AffectationRegisterForN.cpp
+++ b/src/language/utils/AffectationRegisterForN.cpp
@@ -9,23 +9,36 @@ AffectationRegisterForN::_register_eq_op()
 {
   OperatorRepository& repository = OperatorRepository::instance();
 
+  auto B = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
   auto N = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
 
   repository
-    .addAffectation<language::eq_op>(N, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+    .addAffectation<language::eq_op>(N, B,
                                      std::make_shared<AffectationProcessorBuilder<language::eq_op, uint64_t, bool>>());
 
   repository.addAffectation<
-    language::eq_op>(N, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
-                     std::make_shared<AffectationProcessorBuilder<language::eq_op, uint64_t, int64_t>>());
+    language::eq_op>(N, Z, std::make_shared<AffectationProcessorBuilder<language::eq_op, uint64_t, int64_t>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
-                                             ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N), B,
                                              std::make_shared<AffectationToTupleProcessorBuilder<uint64_t>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
-                                             ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N), Z,
                                              std::make_shared<AffectationToTupleProcessorBuilder<uint64_t>>());
+
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                             ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                             std::make_shared<AffectationToTupleFromTupleProcessorBuilder<uint64_t>>());
+
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                             ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                             std::make_shared<AffectationToTupleFromTupleProcessorBuilder<uint64_t>>());
+
+  repository.addAffectation<language::eq_op>(N, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<uint64_t>>());
+
+  repository.addAffectation<language::eq_op>(N, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<uint64_t>>());
 }
 
 void
@@ -33,19 +46,19 @@ AffectationRegisterForN::_register_pluseq_op()
 {
   OperatorRepository& repository = OperatorRepository::instance();
 
+  auto B = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
   auto N = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
 
   repository.addAffectation<
-    language::pluseq_op>(N, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
-                         std::make_shared<AffectationProcessorBuilder<language::pluseq_op, uint64_t, bool>>());
+    language::pluseq_op>(N, B, std::make_shared<AffectationProcessorBuilder<language::pluseq_op, uint64_t, bool>>());
 
   repository.addAffectation<
-    language::pluseq_op>(N, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+    language::pluseq_op>(N, N,
                          std::make_shared<AffectationProcessorBuilder<language::pluseq_op, uint64_t, uint64_t>>());
 
   repository.addAffectation<
-    language::pluseq_op>(N, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
-                         std::make_shared<AffectationProcessorBuilder<language::pluseq_op, uint64_t, int64_t>>());
+    language::pluseq_op>(N, Z, std::make_shared<AffectationProcessorBuilder<language::pluseq_op, uint64_t, int64_t>>());
 }
 
 void
@@ -53,18 +66,19 @@ AffectationRegisterForN::_register_minuseq_op()
 {
   OperatorRepository& repository = OperatorRepository::instance();
 
+  auto B = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
   auto N = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
 
   repository.addAffectation<
-    language::minuseq_op>(N, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
-                          std::make_shared<AffectationProcessorBuilder<language::minuseq_op, uint64_t, bool>>());
+    language::minuseq_op>(N, B, std::make_shared<AffectationProcessorBuilder<language::minuseq_op, uint64_t, bool>>());
 
   repository.addAffectation<
-    language::minuseq_op>(N, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+    language::minuseq_op>(N, N,
                           std::make_shared<AffectationProcessorBuilder<language::minuseq_op, uint64_t, uint64_t>>());
 
   repository.addAffectation<
-    language::minuseq_op>(N, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    language::minuseq_op>(N, Z,
                           std::make_shared<AffectationProcessorBuilder<language::minuseq_op, uint64_t, int64_t>>());
 }
 
@@ -73,17 +87,19 @@ AffectationRegisterForN::_register_multiplyeq_op()
 {
   OperatorRepository& repository = OperatorRepository::instance();
 
+  auto B = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
   auto N = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
 
   repository.addAffectation<
-    language::multiplyeq_op>(N, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+    language::multiplyeq_op>(N, B,
                              std::make_shared<AffectationProcessorBuilder<language::multiplyeq_op, uint64_t, bool>>());
 
-  repository.addAffectation<language::multiplyeq_op>(N, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+  repository.addAffectation<language::multiplyeq_op>(N, N,
                                                      std::make_shared<AffectationProcessorBuilder<
                                                        language::multiplyeq_op, uint64_t, uint64_t>>());
 
-  repository.addAffectation<language::multiplyeq_op>(N, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+  repository.addAffectation<language::multiplyeq_op>(N, Z,
                                                      std::make_shared<AffectationProcessorBuilder<
                                                        language::multiplyeq_op, uint64_t, int64_t>>());
 }
@@ -94,13 +110,14 @@ AffectationRegisterForN::_register_divideeq_op()
   OperatorRepository& repository = OperatorRepository::instance();
 
   auto N = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
 
   repository.addAffectation<
-    language::divideeq_op>(N, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+    language::divideeq_op>(N, N,
                            std::make_shared<AffectationProcessorBuilder<language::divideeq_op, uint64_t, uint64_t>>());
 
   repository.addAffectation<
-    language::divideeq_op>(N, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    language::divideeq_op>(N, Z,
                            std::make_shared<AffectationProcessorBuilder<language::divideeq_op, uint64_t, int64_t>>());
 }
 
diff --git a/src/language/utils/AffectationRegisterForR.cpp b/src/language/utils/AffectationRegisterForR.cpp
index 7959b35b3d2119a8b6930cc6d4beb7d61f115da8..409e66815396bb9c539874ef114e6ef8e0e6688e 100644
--- a/src/language/utils/AffectationRegisterForR.cpp
+++ b/src/language/utils/AffectationRegisterForR.cpp
@@ -9,31 +9,51 @@ AffectationRegisterForR::_register_eq_op()
 {
   OperatorRepository& repository = OperatorRepository::instance();
 
+  auto B = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
+  auto N = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
   auto R = ASTNodeDataType::build<ASTNodeDataType::double_t>();
 
   repository
-    .addAffectation<language::eq_op>(R, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+    .addAffectation<language::eq_op>(R, B,
                                      std::make_shared<AffectationProcessorBuilder<language::eq_op, double, bool>>());
 
   repository.addAffectation<
-    language::eq_op>(R, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
-                     std::make_shared<AffectationProcessorBuilder<language::eq_op, double, uint64_t>>());
+    language::eq_op>(R, N, std::make_shared<AffectationProcessorBuilder<language::eq_op, double, uint64_t>>());
 
   repository
-    .addAffectation<language::eq_op>(R, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    .addAffectation<language::eq_op>(R, Z,
                                      std::make_shared<AffectationProcessorBuilder<language::eq_op, double, int64_t>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
-                                             ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R), B,
                                              std::make_shared<AffectationToTupleProcessorBuilder<double>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
-                                             ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R), N,
                                              std::make_shared<AffectationToTupleProcessorBuilder<double>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
-                                             ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R), Z,
                                              std::make_shared<AffectationToTupleProcessorBuilder<double>>());
+
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
+                                             ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                             std::make_shared<AffectationToTupleFromTupleProcessorBuilder<double>>());
+
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
+                                             ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                             std::make_shared<AffectationToTupleFromTupleProcessorBuilder<double>>());
+
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
+                                             ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                             std::make_shared<AffectationToTupleFromTupleProcessorBuilder<double>>());
+
+  repository.addAffectation<language::eq_op>(R, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<double>>());
+
+  repository.addAffectation<language::eq_op>(R, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<double>>());
+
+  repository.addAffectation<language::eq_op>(R, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<double>>());
 }
 
 void
diff --git a/src/language/utils/AffectationRegisterForRn.cpp b/src/language/utils/AffectationRegisterForRn.cpp
index 180bd5ebce8d19dd8aa6f4b628a700632ea44ab0..9850970f3556a91ace84bb603fe54c55a9329ee2 100644
--- a/src/language/utils/AffectationRegisterForRn.cpp
+++ b/src/language/utils/AffectationRegisterForRn.cpp
@@ -11,14 +11,14 @@ AffectationRegisterForRn<Dimension>::_register_eq_op()
   OperatorRepository& repository = OperatorRepository::instance();
 
   auto Rn = ASTNodeDataType::build<ASTNodeDataType::vector_t>(Dimension);
+  auto Z  = ASTNodeDataType::build<ASTNodeDataType::int_t>();
 
   repository.addAffectation<
-    language::eq_op>(Rn, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    language::eq_op>(Rn, Z,
                      std::make_shared<AffectationFromZeroProcessorBuilder<language::eq_op, TinyVector<Dimension>>>());
 
   repository
-    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rn),
-                                     ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rn), Z,
                                      std::make_shared<AffectationToTupleProcessorBuilder<TinyVector<Dimension>>>());
 }
 
@@ -30,43 +30,80 @@ AffectationRegisterForRn<1>::_register_eq_op()
 
   OperatorRepository& repository = OperatorRepository::instance();
 
-  auto Rn = ASTNodeDataType::build<ASTNodeDataType::vector_t>(Dimension);
+  auto B = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
+  auto N = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
+  auto R = ASTNodeDataType::build<ASTNodeDataType::double_t>();
+
+  auto R1 = ASTNodeDataType::build<ASTNodeDataType::vector_t>(Dimension);
 
   repository.addAffectation<
-    language::eq_op>(Rn, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+    language::eq_op>(R1, B,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, TinyVector<Dimension>, bool>>());
 
   repository.addAffectation<
-    language::eq_op>(Rn, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+    language::eq_op>(R1, N,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, TinyVector<Dimension>, uint64_t>>());
 
   repository.addAffectation<
-    language::eq_op>(Rn, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    language::eq_op>(R1, Z,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, TinyVector<Dimension>, int64_t>>());
 
   repository.addAffectation<
-    language::eq_op>(Rn, ASTNodeDataType::build<ASTNodeDataType::double_t>(),
+    language::eq_op>(R1, R,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, TinyVector<Dimension>, double>>());
 
   repository
-    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rn),
-                                     ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1), B,
                                      std::make_shared<AffectationToTupleProcessorBuilder<TinyVector<Dimension>>>());
 
   repository
-    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rn),
-                                     ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1), N,
                                      std::make_shared<AffectationToTupleProcessorBuilder<TinyVector<Dimension>>>());
 
   repository
-    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rn),
-                                     ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1), Z,
                                      std::make_shared<AffectationToTupleProcessorBuilder<TinyVector<Dimension>>>());
 
   repository
-    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rn),
-                                     ASTNodeDataType::build<ASTNodeDataType::double_t>(),
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1), R,
                                      std::make_shared<AffectationToTupleProcessorBuilder<TinyVector<Dimension>>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<TinyVector<1>>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<TinyVector<1>>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<TinyVector<1>>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<TinyVector<1>>>());
+
+  repository
+    .addAffectation<language::eq_op>(R1, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                     std::make_shared<AffectationFromTupleProcessorBuilder<TinyVector<Dimension>>>());
+
+  repository
+    .addAffectation<language::eq_op>(R1, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                     std::make_shared<AffectationFromTupleProcessorBuilder<TinyVector<Dimension>>>());
+
+  repository
+    .addAffectation<language::eq_op>(R1, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                     std::make_shared<AffectationFromTupleProcessorBuilder<TinyVector<Dimension>>>());
+
+  repository
+    .addAffectation<language::eq_op>(R1, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
+                                     std::make_shared<AffectationFromTupleProcessorBuilder<TinyVector<Dimension>>>());
 }
 
 template <size_t Dimension>
@@ -105,19 +142,24 @@ AffectationRegisterForRn<Dimension>::_register_multiplyeq_op()
 
   auto Rn = ASTNodeDataType::build<ASTNodeDataType::vector_t>(Dimension);
 
-  repository.addAffectation<language::multiplyeq_op>(Rn, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+  auto B = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
+  auto N = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
+  auto R = ASTNodeDataType::build<ASTNodeDataType::double_t>();
+
+  repository.addAffectation<language::multiplyeq_op>(Rn, B,
                                                      std::make_shared<AffectationProcessorBuilder<
                                                        language::multiplyeq_op, TinyVector<Dimension>, bool>>());
 
-  repository.addAffectation<language::multiplyeq_op>(Rn, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+  repository.addAffectation<language::multiplyeq_op>(Rn, N,
                                                      std::make_shared<AffectationProcessorBuilder<
                                                        language::multiplyeq_op, TinyVector<Dimension>, uint64_t>>());
 
-  repository.addAffectation<language::multiplyeq_op>(Rn, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+  repository.addAffectation<language::multiplyeq_op>(Rn, Z,
                                                      std::make_shared<AffectationProcessorBuilder<
                                                        language::multiplyeq_op, TinyVector<Dimension>, int64_t>>());
 
-  repository.addAffectation<language::multiplyeq_op>(Rn, ASTNodeDataType::build<ASTNodeDataType::double_t>(),
+  repository.addAffectation<language::multiplyeq_op>(Rn, R,
                                                      std::make_shared<AffectationProcessorBuilder<
                                                        language::multiplyeq_op, TinyVector<Dimension>, double>>());
 }
diff --git a/src/language/utils/AffectationRegisterForRnxn.cpp b/src/language/utils/AffectationRegisterForRnxn.cpp
index 0eaf1c6af2e0caccf0fa143208c774a047d6e550..04cb101667497c55002bae68d4c7727d49f4b3d6 100644
--- a/src/language/utils/AffectationRegisterForRnxn.cpp
+++ b/src/language/utils/AffectationRegisterForRnxn.cpp
@@ -10,15 +10,16 @@ AffectationRegisterForRnxn<Dimension>::_register_eq_op()
 {
   OperatorRepository& repository = OperatorRepository::instance();
 
+  auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
+
   auto Rnxn = ASTNodeDataType::build<ASTNodeDataType::matrix_t>(Dimension, Dimension);
 
   repository.addAffectation<
-    language::eq_op>(Rnxn, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    language::eq_op>(Rnxn, Z,
                      std::make_shared<AffectationFromZeroProcessorBuilder<language::eq_op, TinyMatrix<Dimension>>>());
 
   repository
-    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rnxn),
-                                     ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rnxn), Z,
                                      std::make_shared<AffectationToTupleProcessorBuilder<TinyMatrix<Dimension>>>());
 }
 
@@ -30,43 +31,80 @@ AffectationRegisterForRnxn<1>::_register_eq_op()
 
   OperatorRepository& repository = OperatorRepository::instance();
 
-  auto Rnxn = ASTNodeDataType::build<ASTNodeDataType::matrix_t>(Dimension, Dimension);
+  auto B = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
+  auto N = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
+  auto R = ASTNodeDataType::build<ASTNodeDataType::double_t>();
+
+  auto R1x1 = ASTNodeDataType::build<ASTNodeDataType::matrix_t>(Dimension, Dimension);
 
   repository.addAffectation<
-    language::eq_op>(Rnxn, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+    language::eq_op>(R1x1, B,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, TinyMatrix<Dimension>, bool>>());
 
   repository.addAffectation<
-    language::eq_op>(Rnxn, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+    language::eq_op>(R1x1, N,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, TinyMatrix<Dimension>, uint64_t>>());
 
   repository.addAffectation<
-    language::eq_op>(Rnxn, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    language::eq_op>(R1x1, Z,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, TinyMatrix<Dimension>, int64_t>>());
 
   repository.addAffectation<
-    language::eq_op>(Rnxn, ASTNodeDataType::build<ASTNodeDataType::double_t>(),
+    language::eq_op>(R1x1, R,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, TinyMatrix<Dimension>, double>>());
 
   repository
-    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rnxn),
-                                     ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1x1), B,
                                      std::make_shared<AffectationToTupleProcessorBuilder<TinyMatrix<Dimension>>>());
 
   repository
-    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rnxn),
-                                     ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1x1), N,
                                      std::make_shared<AffectationToTupleProcessorBuilder<TinyMatrix<Dimension>>>());
 
   repository
-    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rnxn),
-                                     ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1x1), Z,
                                      std::make_shared<AffectationToTupleProcessorBuilder<TinyMatrix<Dimension>>>());
 
   repository
-    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Rnxn),
-                                     ASTNodeDataType::build<ASTNodeDataType::double_t>(),
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1x1), R,
                                      std::make_shared<AffectationToTupleProcessorBuilder<TinyMatrix<Dimension>>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1x1),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<TinyMatrix<1>>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1x1),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<TinyMatrix<1>>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1x1),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<TinyMatrix<1>>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1x1),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<TinyMatrix<1>>>());
+
+  repository
+    .addAffectation<language::eq_op>(R1x1, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                     std::make_shared<AffectationFromTupleProcessorBuilder<TinyMatrix<Dimension>>>());
+
+  repository
+    .addAffectation<language::eq_op>(R1x1, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                     std::make_shared<AffectationFromTupleProcessorBuilder<TinyMatrix<Dimension>>>());
+
+  repository
+    .addAffectation<language::eq_op>(R1x1, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                     std::make_shared<AffectationFromTupleProcessorBuilder<TinyMatrix<Dimension>>>());
+
+  repository
+    .addAffectation<language::eq_op>(R1x1, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
+                                     std::make_shared<AffectationFromTupleProcessorBuilder<TinyMatrix<Dimension>>>());
 }
 
 template <size_t Dimension>
@@ -103,21 +141,26 @@ AffectationRegisterForRnxn<Dimension>::_register_multiplyeq_op()
 {
   OperatorRepository& repository = OperatorRepository::instance();
 
+  auto B = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
+  auto N = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
+  auto R = ASTNodeDataType::build<ASTNodeDataType::double_t>();
+
   auto Rnxn = ASTNodeDataType::build<ASTNodeDataType::matrix_t>(Dimension, Dimension);
 
-  repository.addAffectation<language::multiplyeq_op>(Rnxn, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+  repository.addAffectation<language::multiplyeq_op>(Rnxn, B,
                                                      std::make_shared<AffectationProcessorBuilder<
                                                        language::multiplyeq_op, TinyMatrix<Dimension>, bool>>());
 
-  repository.addAffectation<language::multiplyeq_op>(Rnxn, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+  repository.addAffectation<language::multiplyeq_op>(Rnxn, N,
                                                      std::make_shared<AffectationProcessorBuilder<
                                                        language::multiplyeq_op, TinyMatrix<Dimension>, uint64_t>>());
 
-  repository.addAffectation<language::multiplyeq_op>(Rnxn, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+  repository.addAffectation<language::multiplyeq_op>(Rnxn, Z,
                                                      std::make_shared<AffectationProcessorBuilder<
                                                        language::multiplyeq_op, TinyMatrix<Dimension>, int64_t>>());
 
-  repository.addAffectation<language::multiplyeq_op>(Rnxn, ASTNodeDataType::build<ASTNodeDataType::double_t>(),
+  repository.addAffectation<language::multiplyeq_op>(Rnxn, R,
                                                      std::make_shared<AffectationProcessorBuilder<
                                                        language::multiplyeq_op, TinyMatrix<Dimension>, double>>());
 }
diff --git a/src/language/utils/AffectationRegisterForString.cpp b/src/language/utils/AffectationRegisterForString.cpp
index 7946aeb45fc72e7569e5d8299e184c220baa7106..d9177dc7fc72c65f058a9537e4c1ee12c71d29cb 100644
--- a/src/language/utils/AffectationRegisterForString.cpp
+++ b/src/language/utils/AffectationRegisterForString.cpp
@@ -9,87 +9,166 @@ AffectationRegisterForString::_register_eq_op()
 {
   OperatorRepository& repository = OperatorRepository::instance();
 
+  auto B    = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
+  auto N    = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z    = ASTNodeDataType::build<ASTNodeDataType::int_t>();
+  auto R    = ASTNodeDataType::build<ASTNodeDataType::double_t>();
+  auto R1   = ASTNodeDataType::build<ASTNodeDataType::vector_t>(1);
+  auto R2   = ASTNodeDataType::build<ASTNodeDataType::vector_t>(2);
+  auto R3   = ASTNodeDataType::build<ASTNodeDataType::vector_t>(3);
+  auto R1x1 = ASTNodeDataType::build<ASTNodeDataType::matrix_t>(1, 1);
+  auto R2x2 = ASTNodeDataType::build<ASTNodeDataType::matrix_t>(2, 2);
+  auto R3x3 = ASTNodeDataType::build<ASTNodeDataType::matrix_t>(3, 3);
+
   auto string_t = ASTNodeDataType::build<ASTNodeDataType::string_t>();
 
   repository.addAffectation<
-    language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
-                     std::make_shared<AffectationProcessorBuilder<language::eq_op, std::string, bool>>());
+    language::eq_op>(string_t, B, std::make_shared<AffectationProcessorBuilder<language::eq_op, std::string, bool>>());
 
   repository.addAffectation<
-    language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+    language::eq_op>(string_t, N,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, std::string, uint64_t>>());
 
   repository.addAffectation<
-    language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    language::eq_op>(string_t, Z,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, std::string, int64_t>>());
 
   repository.addAffectation<
-    language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::double_t>(),
+    language::eq_op>(string_t, R,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, std::string, double_t>>());
 
   repository.addAffectation<
-    language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::vector_t>(1),
+    language::eq_op>(string_t, R1,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, std::string, TinyVector<1>>>());
 
   repository.addAffectation<
-    language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::vector_t>(2),
+    language::eq_op>(string_t, R2,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, std::string, TinyVector<2>>>());
 
   repository.addAffectation<
-    language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::vector_t>(3),
+    language::eq_op>(string_t, R3,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, std::string, TinyVector<3>>>());
 
   repository.addAffectation<
-    language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::matrix_t>(1, 1),
+    language::eq_op>(string_t, R1x1,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, std::string, TinyMatrix<1>>>());
 
   repository.addAffectation<
-    language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::matrix_t>(2, 2),
+    language::eq_op>(string_t, R2x2,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, std::string, TinyMatrix<2>>>());
 
   repository.addAffectation<
-    language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::matrix_t>(3, 3),
+    language::eq_op>(string_t, R3x3,
                      std::make_shared<AffectationProcessorBuilder<language::eq_op, std::string, TinyMatrix<3>>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
-                                             ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t), B,
                                              std::make_shared<AffectationToTupleProcessorBuilder<std::string>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
-                                             ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t), N,
                                              std::make_shared<AffectationToTupleProcessorBuilder<std::string>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
-                                             ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t), Z,
                                              std::make_shared<AffectationToTupleProcessorBuilder<std::string>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
-                                             ASTNodeDataType::build<ASTNodeDataType::double_t>(),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t), R,
                                              std::make_shared<AffectationToTupleProcessorBuilder<std::string>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
-                                             ASTNodeDataType::build<ASTNodeDataType::vector_t>(1),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t), R1,
                                              std::make_shared<AffectationToTupleProcessorBuilder<std::string>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
-                                             ASTNodeDataType::build<ASTNodeDataType::vector_t>(2),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t), R2,
                                              std::make_shared<AffectationToTupleProcessorBuilder<std::string>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
-                                             ASTNodeDataType::build<ASTNodeDataType::vector_t>(3),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t), R3,
                                              std::make_shared<AffectationToTupleProcessorBuilder<std::string>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
-                                             ASTNodeDataType::build<ASTNodeDataType::matrix_t>(1, 1),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t), R1x1,
                                              std::make_shared<AffectationToTupleProcessorBuilder<std::string>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
-                                             ASTNodeDataType::build<ASTNodeDataType::matrix_t>(2, 2),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t), R2x2,
                                              std::make_shared<AffectationToTupleProcessorBuilder<std::string>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
-                                             ASTNodeDataType::build<ASTNodeDataType::matrix_t>(3, 3),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t), R3x3,
                                              std::make_shared<AffectationToTupleProcessorBuilder<std::string>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<std::string>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<std::string>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<std::string>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<std::string>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<std::string>>());
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R2),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<std::string>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R3),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<std::string>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1x1),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<std::string>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R2x2),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<std::string>>());
+
+  repository
+    .addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(string_t),
+                                     ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R3x3),
+                                     std::make_shared<AffectationToTupleFromTupleProcessorBuilder<std::string>>());
+
+  repository.addAffectation<language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<std::string>>());
+
+  repository.addAffectation<language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<std::string>>());
+
+  repository.addAffectation<language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<std::string>>());
+
+  repository.addAffectation<language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<std::string>>());
+
+  repository.addAffectation<language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<std::string>>());
+
+  repository.addAffectation<language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R2),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<std::string>>());
+
+  repository.addAffectation<language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R3),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<std::string>>());
+
+  repository.addAffectation<language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R1x1),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<std::string>>());
+
+  repository.addAffectation<language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R2x2),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<std::string>>());
+
+  repository.addAffectation<language::eq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(R3x3),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<std::string>>());
 }
 
 void
@@ -97,6 +176,17 @@ AffectationRegisterForString::_register_pluseq_op()
 {
   OperatorRepository& repository = OperatorRepository::instance();
 
+  auto B    = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
+  auto N    = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
+  auto Z    = ASTNodeDataType::build<ASTNodeDataType::int_t>();
+  auto R    = ASTNodeDataType::build<ASTNodeDataType::double_t>();
+  auto R1   = ASTNodeDataType::build<ASTNodeDataType::vector_t>(1);
+  auto R2   = ASTNodeDataType::build<ASTNodeDataType::vector_t>(2);
+  auto R3   = ASTNodeDataType::build<ASTNodeDataType::vector_t>(3);
+  auto R1x1 = ASTNodeDataType::build<ASTNodeDataType::matrix_t>(1, 1);
+  auto R2x2 = ASTNodeDataType::build<ASTNodeDataType::matrix_t>(2, 2);
+  auto R3x3 = ASTNodeDataType::build<ASTNodeDataType::matrix_t>(3, 3);
+
   auto string_t = ASTNodeDataType::build<ASTNodeDataType::string_t>();
 
   repository.addAffectation<language::pluseq_op>(string_t, string_t,
@@ -104,42 +194,42 @@ AffectationRegisterForString::_register_pluseq_op()
                                                    language::pluseq_op, std::string, std::string>>());
 
   repository.addAffectation<
-    language::pluseq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+    language::pluseq_op>(string_t, B,
                          std::make_shared<AffectationProcessorBuilder<language::pluseq_op, std::string, bool>>());
 
   repository.addAffectation<
-    language::pluseq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+    language::pluseq_op>(string_t, N,
                          std::make_shared<AffectationProcessorBuilder<language::pluseq_op, std::string, uint64_t>>());
 
   repository.addAffectation<
-    language::pluseq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::int_t>(),
+    language::pluseq_op>(string_t, Z,
                          std::make_shared<AffectationProcessorBuilder<language::pluseq_op, std::string, int64_t>>());
 
   repository.addAffectation<
-    language::pluseq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::double_t>(),
+    language::pluseq_op>(string_t, R,
                          std::make_shared<AffectationProcessorBuilder<language::pluseq_op, std::string, double>>());
 
-  repository.addAffectation<language::pluseq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::vector_t>(1),
+  repository.addAffectation<language::pluseq_op>(string_t, R1,
                                                  std::make_shared<AffectationProcessorBuilder<
                                                    language::pluseq_op, std::string, TinyVector<1>>>());
 
-  repository.addAffectation<language::pluseq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::vector_t>(2),
+  repository.addAffectation<language::pluseq_op>(string_t, R2,
                                                  std::make_shared<AffectationProcessorBuilder<
                                                    language::pluseq_op, std::string, TinyVector<2>>>());
 
-  repository.addAffectation<language::pluseq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::vector_t>(3),
+  repository.addAffectation<language::pluseq_op>(string_t, R3,
                                                  std::make_shared<AffectationProcessorBuilder<
                                                    language::pluseq_op, std::string, TinyVector<3>>>());
 
-  repository.addAffectation<language::pluseq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::matrix_t>(1, 1),
+  repository.addAffectation<language::pluseq_op>(string_t, R1x1,
                                                  std::make_shared<AffectationProcessorBuilder<
                                                    language::pluseq_op, std::string, TinyMatrix<1>>>());
 
-  repository.addAffectation<language::pluseq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::matrix_t>(2, 2),
+  repository.addAffectation<language::pluseq_op>(string_t, R2x2,
                                                  std::make_shared<AffectationProcessorBuilder<
                                                    language::pluseq_op, std::string, TinyMatrix<2>>>());
 
-  repository.addAffectation<language::pluseq_op>(string_t, ASTNodeDataType::build<ASTNodeDataType::matrix_t>(3, 3),
+  repository.addAffectation<language::pluseq_op>(string_t, R3x3,
                                                  std::make_shared<AffectationProcessorBuilder<
                                                    language::pluseq_op, std::string, TinyMatrix<3>>>());
 }
diff --git a/src/language/utils/AffectationRegisterForZ.cpp b/src/language/utils/AffectationRegisterForZ.cpp
index 09c493e711cf96b913607da732da7dd9ef937bc5..648c680c23eb7a0c51d61d44aca2c75ee170c4ab 100644
--- a/src/language/utils/AffectationRegisterForZ.cpp
+++ b/src/language/utils/AffectationRegisterForZ.cpp
@@ -9,23 +9,36 @@ AffectationRegisterForZ::_register_eq_op()
 {
   OperatorRepository& repository = OperatorRepository::instance();
 
+  auto B = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
+  auto N = ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>();
   auto Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
 
   repository
-    .addAffectation<language::eq_op>(Z, ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+    .addAffectation<language::eq_op>(Z, B,
                                      std::make_shared<AffectationProcessorBuilder<language::eq_op, int64_t, bool>>());
 
   repository.addAffectation<
-    language::eq_op>(Z, ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
-                     std::make_shared<AffectationProcessorBuilder<language::eq_op, int64_t, uint64_t>>());
+    language::eq_op>(Z, N, std::make_shared<AffectationProcessorBuilder<language::eq_op, int64_t, uint64_t>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
-                                             ASTNodeDataType::build<ASTNodeDataType::bool_t>(),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z), B,
                                              std::make_shared<AffectationToTupleProcessorBuilder<int64_t>>());
 
-  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
-                                             ASTNodeDataType::build<ASTNodeDataType::unsigned_int_t>(),
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z), N,
                                              std::make_shared<AffectationToTupleProcessorBuilder<int64_t>>());
+
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                             ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                             std::make_shared<AffectationToTupleFromTupleProcessorBuilder<int64_t>>());
+
+  repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(Z),
+                                             ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                             std::make_shared<AffectationToTupleFromTupleProcessorBuilder<int64_t>>());
+
+  repository.addAffectation<language::eq_op>(Z, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(B),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<int64_t>>());
+
+  repository.addAffectation<language::eq_op>(Z, ASTNodeDataType::build<ASTNodeDataType::tuple_t>(N),
+                                             std::make_shared<AffectationFromTupleProcessorBuilder<int64_t>>());
 }
 
 void
diff --git a/src/language/utils/BasicAffectationRegistrerFor.hpp b/src/language/utils/BasicAffectationRegistrerFor.hpp
index ccae4c32b596b39ce7b663da38e31e70c8dfbebc..72569803014412bc9e7e556fb5ea8fb6e024ff54 100644
--- a/src/language/utils/BasicAffectationRegistrerFor.hpp
+++ b/src/language/utils/BasicAffectationRegistrerFor.hpp
@@ -29,7 +29,11 @@ class BasicAffectationRegisterFor
 
     repository.addAffectation<language::eq_op>(ASTNodeDataType::build<ASTNodeDataType::tuple_t>(ast_node_data_type),
                                                ASTNodeDataType::build<ASTNodeDataType::tuple_t>(ast_node_data_type),
-                                               std::make_shared<AffectationToTupleFromListProcessorBuilder<T>>());
+                                               std::make_shared<AffectationToTupleFromTupleProcessorBuilder<T>>());
+
+    repository.addAffectation<language::eq_op>(ast_node_data_type,
+                                               ASTNodeDataType::build<ASTNodeDataType::tuple_t>(ast_node_data_type),
+                                               std::make_shared<AffectationFromTupleProcessorBuilder<T>>());
   }
 };
 
diff --git a/src/language/utils/CMakeLists.txt b/src/language/utils/CMakeLists.txt
index a81ffa8aecdf7af0085296b181f2b3ec4d63238f..db3e7856b758b6a4822d97ac0330958b3b367176 100644
--- a/src/language/utils/CMakeLists.txt
+++ b/src/language/utils/CMakeLists.txt
@@ -28,6 +28,7 @@ add_library(PugsLanguageUtils
   FunctionSymbolId.cpp
   IncDecOperatorRegisterForN.cpp
   IncDecOperatorRegisterForZ.cpp
+  ItemArrayVariantFunctionInterpoler.cpp
   ItemValueVariantFunctionInterpoler.cpp
   OFStream.cpp
   OperatorRepository.cpp
diff --git a/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp b/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp
index 3d820c561f623d01967c6a4d947eaa91a531e183..ea842f84c24276323f8aa61865e8d1de470ef10d 100644
--- a/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp
+++ b/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp
@@ -276,7 +276,7 @@ dot(const std::shared_ptr<const DiscreteFunctionVariant>& f, const TinyVector<Ve
           throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f));
         }
       } else {
-        throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f));
+        throw NormalError(EmbeddedDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
       }
     },
     f->discreteFunction());
@@ -301,7 +301,7 @@ dot(const TinyVector<VectorDimension>& a, const std::shared_ptr<const DiscreteFu
           throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f));
         }
       } else {
-        throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(f));
+        throw NormalError(EmbeddedDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
       }
     },
     f->discreteFunction());
@@ -541,7 +541,9 @@ vectorize_to(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>&
     }
 
   } else {
-    throw NormalError("discrete functions are not defined on the same mesh");
+    // LCOV_EXCL_START
+    throw UnexpectedError("discrete functions are not defined on the same mesh");
+    // LCOV_EXCL_STOP
   }
 }
 
diff --git a/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp b/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..02d47d399ed9ec0fdb570fc3877b0b384f7a5429
--- /dev/null
+++ b/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp
@@ -0,0 +1,159 @@
+#include <language/utils/ItemArrayVariantFunctionInterpoler.hpp>
+
+#include <language/utils/InterpolateItemArray.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemArrayVariant.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+#include <utils/Exceptions.hpp>
+
+#include <memory>
+
+template <size_t Dimension, typename DataType, typename ArrayType>
+std::shared_ptr<ItemArrayVariant>
+ItemArrayVariantFunctionInterpoler::_interpolate() const
+{
+  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(m_mesh);
+  using MeshDataType     = MeshData<Dimension>;
+
+  switch (m_item_type) {
+  case ItemType::cell: {
+    MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
+    return std::make_shared<ItemArrayVariant>(
+      InterpolateItemArray<DataType(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(m_function_id_list,
+                                                                                                  mesh_data.xj()));
+  }
+  case ItemType::face: {
+    MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
+    return std::make_shared<ItemArrayVariant>(
+      InterpolateItemArray<DataType(TinyVector<Dimension>)>::template interpolate<ItemType::face>(m_function_id_list,
+                                                                                                  mesh_data.xl()));
+  }
+  case ItemType::edge: {
+    MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
+    return std::make_shared<ItemArrayVariant>(
+      InterpolateItemArray<DataType(TinyVector<Dimension>)>::template interpolate<ItemType::edge>(m_function_id_list,
+                                                                                                  mesh_data.xe()));
+  }
+  case ItemType::node: {
+    return std::make_shared<ItemArrayVariant>(
+      InterpolateItemArray<DataType(TinyVector<Dimension>)>::template interpolate<ItemType::node>(m_function_id_list,
+                                                                                                  p_mesh->xr()));
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("invalid item type");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
+
+template <size_t Dimension>
+std::shared_ptr<ItemArrayVariant>
+ItemArrayVariantFunctionInterpoler::_interpolate() const
+{
+  const ASTNodeDataType data_type = [&] {
+    const auto& function0_descriptor = m_function_id_list[0].descriptor();
+    Assert(function0_descriptor.domainMappingNode().children[1]->m_data_type == ASTNodeDataType::typename_t);
+
+    ASTNodeDataType data_type = function0_descriptor.domainMappingNode().children[1]->m_data_type.contentType();
+
+    for (size_t i = 1; i < m_function_id_list.size(); ++i) {
+      const auto& function_descriptor = m_function_id_list[i].descriptor();
+      Assert(function_descriptor.domainMappingNode().children[1]->m_data_type == ASTNodeDataType::typename_t);
+      if (data_type != function_descriptor.domainMappingNode().children[1]->m_data_type.contentType()) {
+        throw NormalError("functions must have the same type");
+      }
+    }
+
+    return data_type;
+  }();
+
+  switch (data_type) {
+  case ASTNodeDataType::bool_t: {
+    return this->_interpolate<Dimension, bool>();
+  }
+  case ASTNodeDataType::unsigned_int_t: {
+    return this->_interpolate<Dimension, uint64_t>();
+  }
+  case ASTNodeDataType::int_t: {
+    return this->_interpolate<Dimension, int64_t>();
+  }
+  case ASTNodeDataType::double_t: {
+    return this->_interpolate<Dimension, double>();
+  }
+  case ASTNodeDataType::vector_t: {
+    switch (data_type.dimension()) {
+    case 1: {
+      return this->_interpolate<Dimension, TinyVector<1>>();
+    }
+    case 2: {
+      return this->_interpolate<Dimension, TinyVector<2>>();
+    }
+    case 3: {
+      return this->_interpolate<Dimension, TinyVector<3>>();
+    }
+      // LCOV_EXCL_START
+    default: {
+      std::ostringstream os;
+      os << "invalid vector dimension " << rang::fgB::red << data_type.dimension() << rang::style::reset;
+
+      throw UnexpectedError(os.str());
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+  case ASTNodeDataType::matrix_t: {
+    Assert(data_type.numberOfColumns() == data_type.numberOfRows(), "undefined matrix type");
+    switch (data_type.numberOfColumns()) {
+    case 1: {
+      return this->_interpolate<Dimension, TinyMatrix<1>>();
+    }
+    case 2: {
+      return this->_interpolate<Dimension, TinyMatrix<2>>();
+    }
+    case 3: {
+      return this->_interpolate<Dimension, TinyMatrix<3>>();
+    }
+      // LCOV_EXCL_START
+    default: {
+      std::ostringstream os;
+      os << "invalid vector dimension " << rang::fgB::red << data_type.dimension() << rang::style::reset;
+
+      throw UnexpectedError(os.str());
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+    // LCOV_EXCL_START
+  default: {
+    std::ostringstream os;
+    os << "invalid interpolation array type: " << rang::fgB::red << dataTypeName(data_type) << rang::style::reset;
+
+    throw UnexpectedError(os.str());
+  }
+    // LCOV_EXCL_STOP
+  }
+}
+
+std::shared_ptr<ItemArrayVariant>
+ItemArrayVariantFunctionInterpoler::interpolate() const
+{
+  switch (m_mesh->dimension()) {
+  case 1: {
+    return this->_interpolate<1>();
+  }
+  case 2: {
+    return this->_interpolate<2>();
+  }
+  case 3: {
+    return this->_interpolate<3>();
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("invalid dimension");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/language/utils/ItemArrayVariantFunctionInterpoler.hpp b/src/language/utils/ItemArrayVariantFunctionInterpoler.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..eac61b5fb067e83c2cf8f132060e68884316cad4
--- /dev/null
+++ b/src/language/utils/ItemArrayVariantFunctionInterpoler.hpp
@@ -0,0 +1,38 @@
+#ifndef ITEM_ARRAY_VARIANT_FUNCTION_INTERPOLER_HPP
+#define ITEM_ARRAY_VARIANT_FUNCTION_INTERPOLER_HPP
+
+#include <language/utils/FunctionSymbolId.hpp>
+#include <mesh/IMesh.hpp>
+#include <mesh/IZoneDescriptor.hpp>
+#include <mesh/ItemArrayVariant.hpp>
+#include <mesh/ItemType.hpp>
+
+class ItemArrayVariantFunctionInterpoler
+{
+ private:
+  std::shared_ptr<const IMesh> m_mesh;
+  const ItemType m_item_type;
+  const std::vector<FunctionSymbolId> m_function_id_list;
+
+  template <size_t Dimension, typename DataType, typename ArrayType = DataType>
+  std::shared_ptr<ItemArrayVariant> _interpolate() const;
+
+  template <size_t Dimension>
+  std::shared_ptr<ItemArrayVariant> _interpolate() const;
+
+ public:
+  std::shared_ptr<ItemArrayVariant> interpolate() const;
+
+  ItemArrayVariantFunctionInterpoler(const std::shared_ptr<const IMesh>& mesh,
+                                     const ItemType& item_type,
+                                     const std::vector<FunctionSymbolId>& function_id_list)
+    : m_mesh{mesh}, m_item_type{item_type}, m_function_id_list{function_id_list}
+  {}
+
+  ItemArrayVariantFunctionInterpoler(const ItemArrayVariantFunctionInterpoler&) = delete;
+  ItemArrayVariantFunctionInterpoler(ItemArrayVariantFunctionInterpoler&&)      = delete;
+
+  ~ItemArrayVariantFunctionInterpoler() = default;
+};
+
+#endif   // ITEM_ARRAY_VARIANT_FUNCTION_INTERPOLER_HPP
diff --git a/src/language/utils/ItemValueVariantFunctionInterpoler.cpp b/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
index 018dd7a9837997e4faf595898773f7c847f47f20..98dbe1658b45b8b8613892d0cfa72e67ccc67700 100644
--- a/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
+++ b/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
@@ -41,9 +41,11 @@ ItemValueVariantFunctionInterpoler::_interpolate() const
       InterpolateItemValue<DataType(TinyVector<Dimension>)>::template interpolate<ItemType::node>(m_function_id,
                                                                                                   p_mesh->xr()));
   }
+    // LCOV_EXCL_START
   default: {
     throw UnexpectedError("invalid item type");
   }
+    // LCOV_EXCL_STOP
   }
 }
 
@@ -58,13 +60,13 @@ ItemValueVariantFunctionInterpoler::_interpolate() const
 
   switch (data_type) {
   case ASTNodeDataType::bool_t: {
-    return this->_interpolate<Dimension, bool, double>();
+    return this->_interpolate<Dimension, bool>();
   }
   case ASTNodeDataType::unsigned_int_t: {
-    return this->_interpolate<Dimension, uint64_t, double>();
+    return this->_interpolate<Dimension, uint64_t>();
   }
   case ASTNodeDataType::int_t: {
-    return this->_interpolate<Dimension, int64_t, double>();
+    return this->_interpolate<Dimension, int64_t>();
   }
   case ASTNodeDataType::double_t: {
     return this->_interpolate<Dimension, double>();
diff --git a/src/mesh/ItemArrayVariant.hpp b/src/mesh/ItemArrayVariant.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0284cce20965d6568c6272358163e866e70ed393
--- /dev/null
+++ b/src/mesh/ItemArrayVariant.hpp
@@ -0,0 +1,113 @@
+#ifndef ITEM_ARRAY_VARIANT_HPP
+#define ITEM_ARRAY_VARIANT_HPP
+
+#include <algebra/TinyMatrix.hpp>
+#include <algebra/TinyVector.hpp>
+#include <mesh/ItemArray.hpp>
+#include <utils/Exceptions.hpp>
+
+class ItemArrayVariant
+{
+ private:
+  using Variant = std::variant<NodeArray<const bool>,
+                               NodeArray<const long int>,
+                               NodeArray<const unsigned long int>,
+                               NodeArray<const double>,
+                               NodeArray<const TinyVector<1, double>>,
+                               NodeArray<const TinyVector<2, double>>,
+                               NodeArray<const TinyVector<3, double>>,
+                               NodeArray<const TinyMatrix<1, 1, double>>,
+                               NodeArray<const TinyMatrix<2, 2, double>>,
+                               NodeArray<const TinyMatrix<3, 3, double>>,
+
+                               EdgeArray<const bool>,
+                               EdgeArray<const long int>,
+                               EdgeArray<const unsigned long int>,
+                               EdgeArray<const double>,
+                               EdgeArray<const TinyVector<1, double>>,
+                               EdgeArray<const TinyVector<2, double>>,
+                               EdgeArray<const TinyVector<3, double>>,
+                               EdgeArray<const TinyMatrix<1, 1, double>>,
+                               EdgeArray<const TinyMatrix<2, 2, double>>,
+                               EdgeArray<const TinyMatrix<3, 3, double>>,
+
+                               FaceArray<const bool>,
+                               FaceArray<const long int>,
+                               FaceArray<const unsigned long int>,
+                               FaceArray<const double>,
+                               FaceArray<const TinyVector<1, double>>,
+                               FaceArray<const TinyVector<2, double>>,
+                               FaceArray<const TinyVector<3, double>>,
+                               FaceArray<const TinyMatrix<1, 1, double>>,
+                               FaceArray<const TinyMatrix<2, 2, double>>,
+                               FaceArray<const TinyMatrix<3, 3, double>>,
+
+                               CellArray<const bool>,
+                               CellArray<const long int>,
+                               CellArray<const unsigned long int>,
+                               CellArray<const double>,
+                               CellArray<const TinyVector<1, double>>,
+                               CellArray<const TinyVector<2, double>>,
+                               CellArray<const TinyVector<3, double>>,
+                               CellArray<const TinyMatrix<1, 1, double>>,
+                               CellArray<const TinyMatrix<2, 2, double>>,
+                               CellArray<const TinyMatrix<3, 3, double>>>;
+
+  Variant m_item_array;
+
+ public:
+  PUGS_INLINE
+  const Variant&
+  itemArray() const
+  {
+    return m_item_array;
+  }
+
+  template <typename ItemArrayT>
+  PUGS_INLINE auto
+  get() const
+  {
+    using DataType               = typename ItemArrayT::data_type;
+    constexpr ItemType item_type = ItemArrayT::item_t;
+
+    if constexpr (std::is_same_v<ItemArrayT, ItemArray<DataType, item_type>> or
+                  std::is_same_v<ItemArrayT, ItemArray<const DataType, item_type>> or
+                  std::is_same_v<ItemArrayT, WeakItemArray<DataType, item_type>> or
+                  std::is_same_v<ItemArrayT, WeakItemArray<const DataType, item_type>>) {
+      if (not std::holds_alternative<ItemArray<const DataType, item_type>>(this->m_item_array)) {
+        throw NormalError("invalid ItemArray type");
+      }
+      return std::get<ItemArray<const DataType, item_type>>(this->itemArray());
+    } else {
+      static_assert(std::is_same_v<ItemArrayT, ItemArrayT>, "invalid template argument");
+    }
+  }
+
+  template <typename DataType, ItemType item_type>
+  ItemArrayVariant(const ItemArray<DataType, item_type>& item_array)
+    : m_item_array{ItemArray<const DataType, item_type>{item_array}}
+  {
+    static_assert(std::is_same_v<std::remove_const_t<DataType>, bool> or                         //
+                    std::is_same_v<std::remove_const_t<DataType>, long int> or                   //
+                    std::is_same_v<std::remove_const_t<DataType>, unsigned long int> or          //
+                    std::is_same_v<std::remove_const_t<DataType>, double> or                     //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or      //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyVector<2, double>> or      //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyVector<3, double>> or      //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<1, 1, double>> or   //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<2, 2, double>> or   //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<3, 3, double>>,
+                  "ItemArray with this DataType is not allowed in variant");
+  }
+
+  ItemArrayVariant& operator=(ItemArrayVariant&&) = default;
+  ItemArrayVariant& operator=(const ItemArrayVariant&) = default;
+
+  ItemArrayVariant(const ItemArrayVariant&) = default;
+  ItemArrayVariant(ItemArrayVariant&&)      = default;
+
+  ItemArrayVariant()  = delete;
+  ~ItemArrayVariant() = default;
+};
+
+#endif   // ITEM_ARRAY_VARIANT_HPP
diff --git a/src/mesh/SubItemArrayPerItemVariant.hpp b/src/mesh/SubItemArrayPerItemVariant.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ffb0ea673241236a7e6fd1a3ee0566439abcf765
--- /dev/null
+++ b/src/mesh/SubItemArrayPerItemVariant.hpp
@@ -0,0 +1,202 @@
+#ifndef SUB_ITEM_ARRAY_PER_ITEM_VARIANT_HPP
+#define SUB_ITEM_ARRAY_PER_ITEM_VARIANT_HPP
+
+#include <algebra/TinyMatrix.hpp>
+#include <algebra/TinyVector.hpp>
+#include <mesh/SubItemArrayPerItem.hpp>
+#include <utils/Exceptions.hpp>
+
+class SubItemArrayPerItemVariant
+{
+ private:
+  using Variant = std::variant<NodeArrayPerEdge<const bool>,
+                               NodeArrayPerEdge<const long int>,
+                               NodeArrayPerEdge<const unsigned long int>,
+                               NodeArrayPerEdge<const double>,
+                               NodeArrayPerEdge<const TinyVector<1, double>>,
+                               NodeArrayPerEdge<const TinyVector<2, double>>,
+                               NodeArrayPerEdge<const TinyVector<3, double>>,
+                               NodeArrayPerEdge<const TinyMatrix<1, 1, double>>,
+                               NodeArrayPerEdge<const TinyMatrix<2, 2, double>>,
+                               NodeArrayPerEdge<const TinyMatrix<3, 3, double>>,
+
+                               NodeArrayPerFace<const bool>,
+                               NodeArrayPerFace<const long int>,
+                               NodeArrayPerFace<const unsigned long int>,
+                               NodeArrayPerFace<const double>,
+                               NodeArrayPerFace<const TinyVector<1, double>>,
+                               NodeArrayPerFace<const TinyVector<2, double>>,
+                               NodeArrayPerFace<const TinyVector<3, double>>,
+                               NodeArrayPerFace<const TinyMatrix<1, 1, double>>,
+                               NodeArrayPerFace<const TinyMatrix<2, 2, double>>,
+                               NodeArrayPerFace<const TinyMatrix<3, 3, double>>,
+
+                               NodeArrayPerCell<const bool>,
+                               NodeArrayPerCell<const long int>,
+                               NodeArrayPerCell<const unsigned long int>,
+                               NodeArrayPerCell<const double>,
+                               NodeArrayPerCell<const TinyVector<1, double>>,
+                               NodeArrayPerCell<const TinyVector<2, double>>,
+                               NodeArrayPerCell<const TinyVector<3, double>>,
+                               NodeArrayPerCell<const TinyMatrix<1, 1, double>>,
+                               NodeArrayPerCell<const TinyMatrix<2, 2, double>>,
+                               NodeArrayPerCell<const TinyMatrix<3, 3, double>>,
+
+                               EdgeArrayPerNode<const bool>,
+                               EdgeArrayPerNode<const long int>,
+                               EdgeArrayPerNode<const unsigned long int>,
+                               EdgeArrayPerNode<const double>,
+                               EdgeArrayPerNode<const TinyVector<1, double>>,
+                               EdgeArrayPerNode<const TinyVector<2, double>>,
+                               EdgeArrayPerNode<const TinyVector<3, double>>,
+                               EdgeArrayPerNode<const TinyMatrix<1, 1, double>>,
+                               EdgeArrayPerNode<const TinyMatrix<2, 2, double>>,
+                               EdgeArrayPerNode<const TinyMatrix<3, 3, double>>,
+
+                               EdgeArrayPerFace<const bool>,
+                               EdgeArrayPerFace<const long int>,
+                               EdgeArrayPerFace<const unsigned long int>,
+                               EdgeArrayPerFace<const double>,
+                               EdgeArrayPerFace<const TinyVector<1, double>>,
+                               EdgeArrayPerFace<const TinyVector<2, double>>,
+                               EdgeArrayPerFace<const TinyVector<3, double>>,
+                               EdgeArrayPerFace<const TinyMatrix<1, 1, double>>,
+                               EdgeArrayPerFace<const TinyMatrix<2, 2, double>>,
+                               EdgeArrayPerFace<const TinyMatrix<3, 3, double>>,
+
+                               EdgeArrayPerCell<const bool>,
+                               EdgeArrayPerCell<const long int>,
+                               EdgeArrayPerCell<const unsigned long int>,
+                               EdgeArrayPerCell<const double>,
+                               EdgeArrayPerCell<const TinyVector<1, double>>,
+                               EdgeArrayPerCell<const TinyVector<2, double>>,
+                               EdgeArrayPerCell<const TinyVector<3, double>>,
+                               EdgeArrayPerCell<const TinyMatrix<1, 1, double>>,
+                               EdgeArrayPerCell<const TinyMatrix<2, 2, double>>,
+                               EdgeArrayPerCell<const TinyMatrix<3, 3, double>>,
+
+                               FaceArrayPerNode<const bool>,
+                               FaceArrayPerNode<const long int>,
+                               FaceArrayPerNode<const unsigned long int>,
+                               FaceArrayPerNode<const double>,
+                               FaceArrayPerNode<const TinyVector<1, double>>,
+                               FaceArrayPerNode<const TinyVector<2, double>>,
+                               FaceArrayPerNode<const TinyVector<3, double>>,
+                               FaceArrayPerNode<const TinyMatrix<1, 1, double>>,
+                               FaceArrayPerNode<const TinyMatrix<2, 2, double>>,
+                               FaceArrayPerNode<const TinyMatrix<3, 3, double>>,
+
+                               FaceArrayPerEdge<const bool>,
+                               FaceArrayPerEdge<const long int>,
+                               FaceArrayPerEdge<const unsigned long int>,
+                               FaceArrayPerEdge<const double>,
+                               FaceArrayPerEdge<const TinyVector<1, double>>,
+                               FaceArrayPerEdge<const TinyVector<2, double>>,
+                               FaceArrayPerEdge<const TinyVector<3, double>>,
+                               FaceArrayPerEdge<const TinyMatrix<1, 1, double>>,
+                               FaceArrayPerEdge<const TinyMatrix<2, 2, double>>,
+                               FaceArrayPerEdge<const TinyMatrix<3, 3, double>>,
+
+                               FaceArrayPerCell<const bool>,
+                               FaceArrayPerCell<const long int>,
+                               FaceArrayPerCell<const unsigned long int>,
+                               FaceArrayPerCell<const double>,
+                               FaceArrayPerCell<const TinyVector<1, double>>,
+                               FaceArrayPerCell<const TinyVector<2, double>>,
+                               FaceArrayPerCell<const TinyVector<3, double>>,
+                               FaceArrayPerCell<const TinyMatrix<1, 1, double>>,
+                               FaceArrayPerCell<const TinyMatrix<2, 2, double>>,
+                               FaceArrayPerCell<const TinyMatrix<3, 3, double>>,
+
+                               CellArrayPerNode<const bool>,
+                               CellArrayPerNode<const long int>,
+                               CellArrayPerNode<const unsigned long int>,
+                               CellArrayPerNode<const double>,
+                               CellArrayPerNode<const TinyVector<1, double>>,
+                               CellArrayPerNode<const TinyVector<2, double>>,
+                               CellArrayPerNode<const TinyVector<3, double>>,
+                               CellArrayPerNode<const TinyMatrix<1, 1, double>>,
+                               CellArrayPerNode<const TinyMatrix<2, 2, double>>,
+                               CellArrayPerNode<const TinyMatrix<3, 3, double>>,
+
+                               CellArrayPerEdge<const bool>,
+                               CellArrayPerEdge<const long int>,
+                               CellArrayPerEdge<const unsigned long int>,
+                               CellArrayPerEdge<const double>,
+                               CellArrayPerEdge<const TinyVector<1, double>>,
+                               CellArrayPerEdge<const TinyVector<2, double>>,
+                               CellArrayPerEdge<const TinyVector<3, double>>,
+                               CellArrayPerEdge<const TinyMatrix<1, 1, double>>,
+                               CellArrayPerEdge<const TinyMatrix<2, 2, double>>,
+                               CellArrayPerEdge<const TinyMatrix<3, 3, double>>,
+
+                               CellArrayPerFace<const bool>,
+                               CellArrayPerFace<const long int>,
+                               CellArrayPerFace<const unsigned long int>,
+                               CellArrayPerFace<const double>,
+                               CellArrayPerFace<const TinyVector<1, double>>,
+                               CellArrayPerFace<const TinyVector<2, double>>,
+                               CellArrayPerFace<const TinyVector<3, double>>,
+                               CellArrayPerFace<const TinyMatrix<1, 1, double>>,
+                               CellArrayPerFace<const TinyMatrix<2, 2, double>>,
+                               CellArrayPerFace<const TinyMatrix<3, 3, double>>>;
+
+  Variant m_sub_item_array_per_item;
+
+ public:
+  PUGS_INLINE
+  const Variant&
+  itemArray() const
+  {
+    return m_sub_item_array_per_item;
+  }
+
+  template <typename SubItemArrayPerItemT>
+  PUGS_INLINE auto
+  get() const
+  {
+    using DataType        = typename SubItemArrayPerItemT::data_type;
+    using ItemOfItemTypeT = typename SubItemArrayPerItemT::ItemOfItemType;
+
+    if constexpr (std::is_same_v<SubItemArrayPerItemT, SubItemArrayPerItem<DataType, ItemOfItemTypeT>> or
+                  std::is_same_v<SubItemArrayPerItemT, SubItemArrayPerItem<const DataType, ItemOfItemTypeT>> or
+                  std::is_same_v<SubItemArrayPerItemT, WeakSubItemArrayPerItem<DataType, ItemOfItemTypeT>> or
+                  std::is_same_v<SubItemArrayPerItemT, WeakSubItemArrayPerItem<const DataType, ItemOfItemTypeT>>) {
+      if (not std::holds_alternative<SubItemArrayPerItem<const DataType, ItemOfItemTypeT>>(
+            this->m_sub_item_array_per_item)) {
+        throw NormalError("invalid SubItemArrayPerItem type");
+      }
+      return std::get<SubItemArrayPerItem<const DataType, ItemOfItemTypeT>>(this->m_sub_item_array_per_item);
+    } else {
+      static_assert(std::is_same_v<SubItemArrayPerItemT, SubItemArrayPerItemT>, "invalid template argument");
+    }
+  }
+
+  template <typename DataType, typename ItemOfItemTypeT>
+  SubItemArrayPerItemVariant(const SubItemArrayPerItem<DataType, ItemOfItemTypeT>& sub_item_array_per_item)
+    : m_sub_item_array_per_item{SubItemArrayPerItem<const DataType, ItemOfItemTypeT>{sub_item_array_per_item}}
+  {
+    static_assert(std::is_same_v<std::remove_const_t<DataType>, bool> or                         //
+                    std::is_same_v<std::remove_const_t<DataType>, long int> or                   //
+                    std::is_same_v<std::remove_const_t<DataType>, unsigned long int> or          //
+                    std::is_same_v<std::remove_const_t<DataType>, double> or                     //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or      //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyVector<2, double>> or      //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyVector<3, double>> or      //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<1, 1, double>> or   //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<2, 2, double>> or   //
+                    std::is_same_v<std::remove_const_t<DataType>, TinyMatrix<3, 3, double>>,
+                  "SubItemArrayPerItem with this DataType is not allowed in variant");
+  }
+
+  SubItemArrayPerItemVariant& operator=(SubItemArrayPerItemVariant&&) = default;
+  SubItemArrayPerItemVariant& operator=(const SubItemArrayPerItemVariant&) = default;
+
+  SubItemArrayPerItemVariant(const SubItemArrayPerItemVariant&) = default;
+  SubItemArrayPerItemVariant(SubItemArrayPerItemVariant&&)      = default;
+
+  SubItemArrayPerItemVariant()  = delete;
+  ~SubItemArrayPerItemVariant() = default;
+};
+
+#endif   // SUB_ITEM_ARRAY_PER_ITEM_VARIANT_HPP
diff --git a/src/output/INamedDiscreteData.hpp b/src/output/INamedDiscreteData.hpp
index 5d22e82c2e9d599cb695970ec9ba7049efdf08bf..b4fae2b34a5bbbd14ca2127b5cad973beb6d9747 100644
--- a/src/output/INamedDiscreteData.hpp
+++ b/src/output/INamedDiscreteData.hpp
@@ -8,6 +8,7 @@ class INamedDiscreteData
  public:
   enum class Type
   {
+    item_array,
     item_value,
     discrete_function
   };
diff --git a/src/output/NamedItemArrayVariant.hpp b/src/output/NamedItemArrayVariant.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4b92d5585d56aa74067c29c7d3e6945262f642e8
--- /dev/null
+++ b/src/output/NamedItemArrayVariant.hpp
@@ -0,0 +1,47 @@
+#ifndef NAMED_ITEM_ARRAY_VARIANT_HPP
+#define NAMED_ITEM_ARRAY_VARIANT_HPP
+
+#include <output/INamedDiscreteData.hpp>
+
+#include <memory>
+#include <string>
+
+class ItemArrayVariant;
+
+class NamedItemArrayVariant final : public INamedDiscreteData
+{
+ private:
+  std::shared_ptr<const ItemArrayVariant> m_item_array_variant;
+  std::string m_name;
+
+ public:
+  Type
+  type() const final
+  {
+    return INamedDiscreteData::Type::item_array;
+  }
+
+  const std::string&
+  name() const final
+  {
+    return m_name;
+  }
+
+  const std::shared_ptr<const ItemArrayVariant>
+  itemArrayVariant() const
+  {
+    return m_item_array_variant;
+  }
+
+  NamedItemArrayVariant(const std::shared_ptr<const ItemArrayVariant>& item_array_variant, const std::string& name)
+    : m_item_array_variant{item_array_variant}, m_name{name}
+  {}
+
+  NamedItemArrayVariant(const NamedItemArrayVariant&) = default;
+  NamedItemArrayVariant(NamedItemArrayVariant&&)      = default;
+
+  NamedItemArrayVariant()  = default;
+  ~NamedItemArrayVariant() = default;
+};
+
+#endif   // NAMED_ITEM_ARRAY_VARIANT_HPP
diff --git a/src/output/WriterBase.cpp b/src/output/WriterBase.cpp
index 83d890c2ab8bf31e60d7ed09e4e2da7770ebbfb8..a96da1e4e0a5e1eab52c4c3859dc9f0ae2304f08 100644
--- a/src/output/WriterBase.cpp
+++ b/src/output/WriterBase.cpp
@@ -1,8 +1,10 @@
 #include <output/WriterBase.hpp>
 
 #include <mesh/IMesh.hpp>
+#include <mesh/ItemArrayVariant.hpp>
 #include <mesh/ItemValueVariant.hpp>
 #include <output/NamedDiscreteFunction.hpp>
+#include <output/NamedItemArrayVariant.hpp>
 #include <output/NamedItemValueVariant.hpp>
 #include <output/OutputNamedItemValueSet.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
@@ -70,6 +72,75 @@ WriterBase::_checkConnectivity(
   }
 }
 
+void
+WriterBase::_checkSignature(
+  const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const
+{
+  using NameTypeMap = std::map<std::string, std::string>;
+  NameTypeMap name_type_map;
+
+  for (auto named_discrete_data : named_discrete_data_list) {
+    switch (named_discrete_data->type()) {
+    case INamedDiscreteData::Type::item_value: {
+      const NamedItemValueVariant& named_item_value = dynamic_cast<const NamedItemValueVariant&>(*named_discrete_data);
+      std::visit(
+        [&](auto&& item_value) {
+          using ItemValueT = std::decay_t<decltype(item_value)>;
+          using DataType   = std::decay_t<typename ItemValueT::data_type>;
+
+          std::ostringstream type_name;
+          type_name << "item_value(" << dataTypeName(ast_node_data_type_from<DataType>) << ')';
+
+          name_type_map[named_discrete_data->name()] = type_name.str();
+        },
+        named_item_value.itemValueVariant()->itemValue());
+      break;
+    }
+    case INamedDiscreteData::Type::item_array: {
+      const NamedItemArrayVariant& named_item_array = dynamic_cast<const NamedItemArrayVariant&>(*named_discrete_data);
+      std::visit(
+        [&](auto&& item_value) {
+          using ItemValueT = std::decay_t<decltype(item_value)>;
+          using DataType   = std::decay_t<typename ItemValueT::data_type>;
+
+          std::ostringstream type_name;
+          type_name << "item_array(" << dataTypeName(ast_node_data_type_from<DataType>) << ')';
+
+          name_type_map[named_discrete_data->name()] = type_name.str();
+        },
+        named_item_array.itemArrayVariant()->itemArray());
+      break;
+    }
+    case INamedDiscreteData::Type::discrete_function: {
+      const NamedDiscreteFunction& named_discrete_function =
+        dynamic_cast<const NamedDiscreteFunction&>(*named_discrete_data);
+      std::visit(
+        [&](auto&& discrete_function) {
+          std::ostringstream type_name;
+          type_name << "Vh(" << dataTypeName(discrete_function.dataType()) << ')';
+
+          name_type_map[named_discrete_data->name()] = type_name.str();
+        },
+        named_discrete_function.discreteFunctionVariant()->discreteFunction());
+      break;
+    }
+    }
+  }
+
+  std::string signature{"|"};
+  for (auto&& [name, type_name] : name_type_map) {
+    signature += name + std::string{"-"} + type_name + std::string{"|"};
+  }
+
+  if (m_signature.has_value()) {
+    if (m_signature.value() != signature) {
+      throw NormalError("output variable list changed");
+    }
+  } else {
+    m_signature = signature;
+  }
+}
+
 void
 WriterBase::_checkMesh(const std::shared_ptr<const IMesh>& mesh,
                        const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const
@@ -147,6 +218,17 @@ WriterBase::_getMesh(const std::vector<std::shared_ptr<const INamedDiscreteData>
                    auto&&
                      item_value) { connectivity_set[item_value.connectivity_ptr()] = named_item_value_variant.name(); },
                  named_item_value_variant.itemValueVariant()->itemValue());
+      break;
+    }
+    case INamedDiscreteData::Type::item_array: {
+      const NamedItemArrayVariant& named_item_array_variant =
+        dynamic_cast<const NamedItemArrayVariant&>(*named_discrete_data);
+
+      std::visit([&](
+                   auto&&
+                     item_array) { connectivity_set[item_array.connectivity_ptr()] = named_item_array_variant.name(); },
+                 named_item_array_variant.itemArrayVariant()->itemArray());
+      break;
     }
     }
   }
@@ -214,8 +296,26 @@ WriterBase::_getOutputNamedItemDataSet(
                  item_value_variant.itemValue());
       break;
     }
-    default: {
-      throw UnexpectedError("invalid discrete data type");
+    case INamedDiscreteData::Type::item_array: {
+      const NamedItemArrayVariant& named_item_array_variant =
+        dynamic_cast<const NamedItemArrayVariant&>(*named_discrete_data);
+
+      const std::string& name = named_item_array_variant.name();
+
+      const ItemArrayVariant& item_value_variant = *named_item_array_variant.itemArrayVariant();
+
+      std::visit(
+        [&](auto&& item_array) {
+          using ItemArrayType = std::decay_t<decltype(item_array)>;
+          using DataType      = std::decay_t<typename ItemArrayType::data_type>;
+          if constexpr (std::is_arithmetic_v<DataType>) {
+            named_item_data_set.add(NamedItemData{name, item_array});
+          } else {
+            throw NormalError("can only write item_array containing scalar values");
+          }
+        },
+        item_value_variant.itemArray());
+      break;
     }
     }
   }
@@ -223,10 +323,10 @@ WriterBase::_getOutputNamedItemDataSet(
 }
 
 WriterBase::WriterBase(const std::string& base_filename, const double& time_period)
-  : m_base_filename{base_filename}, m_period_manager(time_period)
+  : m_base_filename{base_filename}, m_period_manager(time_period), m_signature(std::nullopt)
 {}
 
-WriterBase::WriterBase(const std::string& base_filename) : m_base_filename{base_filename} {}
+WriterBase::WriterBase(const std::string& base_filename) : m_base_filename{base_filename}, m_signature(std::nullopt) {}
 
 void
 WriterBase::write(const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const
@@ -250,6 +350,7 @@ WriterBase::writeIfNeeded(const std::vector<std::shared_ptr<const INamedDiscrete
 
     if (time >= m_period_manager->nextTime()) {
       std::shared_ptr<const IMesh> mesh = _getMesh(named_discrete_data_list);
+      this->_checkSignature(named_discrete_data_list);
       this->_writeAtTime(*mesh, named_discrete_data_list, time);
       m_period_manager->setSaveTime(time);
     }
@@ -265,7 +366,9 @@ WriterBase::writeForced(const std::vector<std::shared_ptr<const INamedDiscreteDa
   if (m_period_manager.has_value()) {
     if (time == m_period_manager->getLastTime())
       return;   // output already performed
+
     std::shared_ptr<const IMesh> mesh = _getMesh(named_discrete_data_list);
+    this->_checkSignature(named_discrete_data_list);
     this->_writeAtTime(*mesh, named_discrete_data_list, time);
     m_period_manager->setSaveTime(time);
   } else {
@@ -280,8 +383,8 @@ WriterBase::writeOnMesh(const std::shared_ptr<const IMesh>& mesh,
   if (m_period_manager.has_value()) {
     throw NormalError("this writer requires time value");
   } else {
-    _checkMesh(mesh, named_discrete_data_list);
-    _checkConnectivity(mesh, named_discrete_data_list);
+    this->_checkMesh(mesh, named_discrete_data_list);
+    this->_checkConnectivity(mesh, named_discrete_data_list);
     this->_write(*mesh, named_discrete_data_list);
   }
 }
@@ -292,10 +395,12 @@ WriterBase::writeOnMeshIfNeeded(const std::shared_ptr<const IMesh>& mesh,
                                 double time) const
 {
   if (m_period_manager.has_value()) {
-    if (time == m_period_manager->getLastTime())
+    if (time == m_period_manager->getLastTime()) {
       return;   // output already performed
-    _checkMesh(mesh, named_discrete_data_list);
-    _checkConnectivity(mesh, named_discrete_data_list);
+    }
+
+    this->_checkMesh(mesh, named_discrete_data_list);
+    this->_checkConnectivity(mesh, named_discrete_data_list);
     this->_writeAtTime(*mesh, named_discrete_data_list, time);
     m_period_manager->setSaveTime(time);
   } else {
@@ -309,10 +414,11 @@ WriterBase::writeOnMeshForced(const std::shared_ptr<const IMesh>& mesh,
                               double time) const
 {
   if (m_period_manager.has_value()) {
-    if (time == m_period_manager->getLastTime())
+    if (time == m_period_manager->getLastTime()) {
       return;   // output already performed
-    _checkMesh(mesh, named_discrete_data_list);
-    _checkConnectivity(mesh, named_discrete_data_list);
+    }
+    this->_checkMesh(mesh, named_discrete_data_list);
+    this->_checkConnectivity(mesh, named_discrete_data_list);
     this->_writeAtTime(*mesh, named_discrete_data_list, time);
     m_period_manager->setSaveTime(time);
   } else {
diff --git a/src/output/WriterBase.hpp b/src/output/WriterBase.hpp
index 7fae994d78ed0e97be8f10d63097464280c739fc..707ac52d3a3c69c749c46047bd594b0be46c408b 100644
--- a/src/output/WriterBase.hpp
+++ b/src/output/WriterBase.hpp
@@ -85,7 +85,11 @@ class WriterBase : public IWriter
 
   std::optional<PeriodManager> m_period_manager;
 
+  mutable std::optional<std::string> m_signature;
+
  private:
+  void _checkSignature(const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const;
+
   template <typename DiscreteFunctionType>
   static void _registerDiscreteFunction(const std::string& name, const DiscreteFunctionType&, OutputNamedItemDataSet&);
 
diff --git a/src/scheme/FluxingAdvectionSolver.cpp b/src/scheme/FluxingAdvectionSolver.cpp
index b655ff74fe36057766396c12e8e5754a730427da..c4884b62b107489fb5c9d17dfd6069a56013eb67 100644
--- a/src/scheme/FluxingAdvectionSolver.cpp
+++ b/src/scheme/FluxingAdvectionSolver.cpp
@@ -156,10 +156,9 @@ calculateRemapCycles(const std::shared_ptr<const MeshType>& old_mesh,
   const CellValue<size_t> ratio(old_mesh->connectivity());
   parallel_for(
     old_mesh->numberOfCells(),
-    PUGS_LAMBDA(CellId cell_id) { ratio[cell_id] = std::ceil(abs(total_negative_flux[cell_id]) / Vj[cell_id]); });
-  size_t number_of_cycle = max(ratio);
-  std::cout << " number_of_cycle " << number_of_cycle << "\n";
-  return number_of_cycle;
+    PUGS_LAMBDA(CellId cell_id) { ratio[cell_id] = std::ceil(std::abs(total_negative_flux[cell_id]) / Vj[cell_id]); });
+
+  return max(ratio);
 }
 
 template <typename MeshType, typename DataType>
@@ -358,7 +357,9 @@ FluxingAdvectionSolverHandler(const std::shared_ptr<const IMesh> new_mesh,
     FluxingAdvectionSolver<Dimension> solver(old_mesh0, new_mesh0);
 
     FaceValue<double> fluxing_volumes = solver.computeFluxVolume();
-    size_t number_of_cycles           = calculateRemapCycles(old_mesh0, fluxing_volumes);
+    const size_t number_of_cycles     = calculateRemapCycles(old_mesh0, fluxing_volumes);
+
+    std::cout << " number_of_cycle " << number_of_cycles << "\n";
 
     DiscreteFunctionVariant new_variable = std::visit(
       [&](auto&& variable) -> DiscreteFunctionVariant {
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 0e585acac5ba1e5b7656ffa29411567e0f0d54f4..9f1f7d9005d6aa8a583283bf2bfda8dc6e23eedd 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -131,6 +131,7 @@ add_executable (unit_tests
   test_RefItemList.cpp
   test_RevisionInfo.cpp
   test_SmallArray.cpp
+  test_SmallMatrix.cpp
   test_SmallVector.cpp
   test_Socket.cpp
   test_SocketModule.cpp
@@ -177,6 +178,8 @@ add_executable (mpi_unit_tests
   test_InterpolateItemValue.cpp
   test_ItemArray.cpp
   test_ItemArrayUtils.cpp
+  test_ItemArrayVariant.cpp
+  test_ItemArrayVariantFunctionInterpoler.cpp
   test_ItemValue.cpp
   test_ItemValueUtils.cpp
   test_ItemValueVariant.cpp
@@ -194,6 +197,7 @@ add_executable (mpi_unit_tests
   test_OFStream.cpp
   test_Partitioner.cpp
   test_RandomEngine.cpp
+  test_SubItemArrayPerItemVariant.cpp
   test_SubItemValuePerItem.cpp
   test_SubItemValuePerItemVariant.cpp
   test_SubItemValuePerItemUtils.cpp
diff --git a/tests/test_ASTNodeAffectationExpressionBuilder.cpp b/tests/test_ASTNodeAffectationExpressionBuilder.cpp
index 7055452b2b3e99f19fd2ec267c70477569297d4d..f52a33c297bdea413b9f728498eecf8d67e96b30 100644
--- a/tests/test_ASTNodeAffectationExpressionBuilder.cpp
+++ b/tests/test_ASTNodeAffectationExpressionBuilder.cpp
@@ -171,6 +171,10 @@ const auto builtin_data_type = ast_node_data_type_from<std::shared_ptr<const dou
     OperatorRepository::instance().reset();                                                                     \
   }
 
+#ifdef __clang__
+#pragma clang optimize off
+#endif   // __clang__
+
 // clazy:excludeall=non-pod-global-static
 
 TEST_CASE("ASTNodeAffectationExpressionBuilder", "[language]")
@@ -179,9 +183,9 @@ TEST_CASE("ASTNodeAffectationExpressionBuilder", "[language]")
 
   SECTION("Affectations")
   {
-    SECTION("boolean affectation")
+    SECTION("-> B")
     {
-      SECTION("B <- B")
+      SECTION("B -> B")
       {
         std::string_view data = R"(
 let b:B, b=true;
@@ -198,9 +202,9 @@ let b:B, b=true;
       }
     }
 
-    SECTION("unsigned integer affectation")
+    SECTION("-> N")
     {
-      SECTION("N <- B")
+      SECTION("B -> N")
       {
         std::string_view data = R"(
 let n:N, n=true;
@@ -216,7 +220,7 @@ let n:N, n=true;
         CHECK_AST(data, result);
       }
 
-      SECTION("N <- N")
+      SECTION("N -> N")
       {
         std::string_view data = R"(
 let m : N; let n:N, n=m;
@@ -232,7 +236,7 @@ let m : N; let n:N, n=m;
         CHECK_AST(data, result);
       }
 
-      SECTION("N <- Z")
+      SECTION("Z -> N")
       {
         std::string_view data = R"(
 let z:Z; let n :N, n=z;
@@ -249,9 +253,9 @@ let z:Z; let n :N, n=z;
       }
     }
 
-    SECTION("integer affectation")
+    SECTION("-> Z")
     {
-      SECTION("Z <- B")
+      SECTION("B -> Z")
       {
         std::string_view data = R"(
 let z : Z, z=true;
@@ -267,7 +271,7 @@ let z : Z, z=true;
         CHECK_AST(data, result);
       }
 
-      SECTION("Z <- N")
+      SECTION("N -> Z")
       {
         std::string_view data = R"(
 let m : N; let z : Z, z=m;
@@ -283,7 +287,7 @@ let m : N; let z : Z, z=m;
         CHECK_AST(data, result);
       }
 
-      SECTION("Z <- Z")
+      SECTION("Z -> Z")
       {
         std::string_view data = R"(
 let q : Z; let z : Z, z=q;
@@ -300,9 +304,9 @@ let q : Z; let z : Z, z=q;
       }
     }
 
-    SECTION("double affectation")
+    SECTION("-> R")
     {
-      SECTION("R <- B")
+      SECTION("B -> R")
       {
         std::string_view data = R"(
 let r : R, r=true;
@@ -318,7 +322,7 @@ let r : R, r=true;
         CHECK_AST(data, result);
       }
 
-      SECTION("R <- N")
+      SECTION("N -> R")
       {
         std::string_view data = R"(
 let m : N; let r : R, r=m;
@@ -334,7 +338,7 @@ let m : N; let r : R, r=m;
         CHECK_AST(data, result);
       }
 
-      SECTION("R <- Z")
+      SECTION("Z -> R")
       {
         std::string_view data = R"(
 let z : Z; let r : R, r=z;
@@ -350,7 +354,7 @@ let z : Z; let r : R, r=z;
         CHECK_AST(data, result);
       }
 
-      SECTION("R <- R")
+      SECTION("R -> R")
       {
         std::string_view data = R"(
 let s : R; let r : R, r=s;
@@ -367,9 +371,9 @@ let s : R; let r : R, r=s;
       }
     }
 
-    SECTION("R^d affectation")
+    SECTION("-> R^d")
     {
-      SECTION("R^1 <- R^1")
+      SECTION("R^1 -> R^1")
       {
         std::string_view data = R"(
 let x : R^1;
@@ -386,7 +390,7 @@ let y : R^1, y = x;
         CHECK_AST(data, result);
       }
 
-      SECTION("R^1 <- R")
+      SECTION("R -> R^1")
       {
         std::string_view data = R"(
 let x : R^1, x = 1.3;
@@ -402,7 +406,7 @@ let x : R^1, x = 1.3;
         CHECK_AST(data, result);
       }
 
-      SECTION("R^1 <- Z")
+      SECTION("Z -> R^1")
       {
         std::string_view data = R"(
 let x : R^1, x = -1;
@@ -419,7 +423,7 @@ let x : R^1, x = -1;
         CHECK_AST(data, result);
       }
 
-      SECTION("R^1 <- N")
+      SECTION("N -> R^1")
       {
         std::string_view data = R"(
 let n : N;
@@ -436,7 +440,7 @@ let x : R^1, x = n;
         CHECK_AST(data, result);
       }
 
-      SECTION("R^1 <- B")
+      SECTION("B -> R^1")
       {
         std::string_view data = R"(
 let b : B;
@@ -453,7 +457,7 @@ let x : R^1, x = b;
         CHECK_AST(data, result);
       }
 
-      SECTION("R^1 <- 0")
+      SECTION("'0' -> R^1")
       {
         std::string_view data = R"(
 let x : R^1, x = 0;
@@ -469,7 +473,7 @@ let x : R^1, x = 0;
         CHECK_AST(data, result);
       }
 
-      SECTION("R^2 <- R^2")
+      SECTION("R^2 -> R^2 [variable]")
       {
         std::string_view data = R"(
 let x : R^2;
@@ -486,7 +490,7 @@ let y : R^2, y = x;
         CHECK_AST(data, result);
       }
 
-      SECTION("R^2 <- value")
+      SECTION("R^2 -> R^2 [value]")
       {
         std::string_view data = R"(
 let y : R^2, y = [0,1];
@@ -504,7 +508,7 @@ let y : R^2, y = [0,1];
         CHECK_AST(data, result);
       }
 
-      SECTION("R^2 <- 0")
+      SECTION("'0' -> R^2")
       {
         std::string_view data = R"(
 let x : R^2, x = 0;
@@ -520,7 +524,7 @@ let x : R^2, x = 0;
         CHECK_AST(data, result);
       }
 
-      SECTION("R^3 <- R^3")
+      SECTION("R^3 -> R^3")
       {
         std::string_view data = R"(
 let x : R^3;
@@ -537,7 +541,7 @@ let y : R^3, y = x;
         CHECK_AST(data, result);
       }
 
-      SECTION("R^3 <- 0")
+      SECTION("'0' -> R^3")
       {
         std::string_view data = R"(
 let x : R^3, x = 0;
@@ -553,7 +557,7 @@ let x : R^3, x = 0;
         CHECK_AST(data, result);
       }
 
-      SECTION("R^3 <- value")
+      SECTION("R^3 -> R^3 [value]")
       {
         std::string_view data = R"(
 let y : R^3, y = [1,2,3];
@@ -573,9 +577,9 @@ let y : R^3, y = [1,2,3];
       }
     }
 
-    SECTION("string affectation")
+    SECTION("-> string")
     {
-      SECTION("string <- B")
+      SECTION("B -> string")
       {
         std::string_view data = R"(
 let s : string, s=true;
@@ -592,7 +596,7 @@ let s : string, s=true;
         CHECK_AST(data, result);
       }
 
-      SECTION("string <- N")
+      SECTION("N -> string")
       {
         std::string_view data = R"(
 let n : N; let s : string, s=n;
@@ -609,7 +613,7 @@ let n : N; let s : string, s=n;
         CHECK_AST(data, result);
       }
 
-      SECTION("string <- Z")
+      SECTION("Z -> string")
       {
         std::string_view data = R"(
 let z : Z; let s : string, s=z;
@@ -627,7 +631,7 @@ let z : Z; let s : string, s=z;
         CHECK_AST(data, result);
       }
 
-      SECTION("string <- R")
+      SECTION("R -> string")
       {
         std::string_view data = R"(
 let r : R; let s : string, s=r;
@@ -645,7 +649,7 @@ let r : R; let s : string, s=r;
         CHECK_AST(data, result);
       }
 
-      SECTION("string <- string")
+      SECTION("string -> string")
       {
         std::string_view data = R"(
 let s : string, s="foo";
@@ -663,9 +667,9 @@ let s : string, s="foo";
       }
     }
 
-    SECTION("type_id affectation")
+    SECTION("-> type_id")
     {
-      SECTION("type_id <- type_id")
+      SECTION("type_id -> type_id")
       {
         std::string_view data = R"(
 let t : builtin_t, t=a;
@@ -682,9 +686,9 @@ let t : builtin_t, t=a;
       }
     }
 
-    SECTION("tuples")
+    SECTION("list -> tuple")
     {
-      SECTION("B tuples")
+      SECTION("-> (B)")
       {
         std::string_view data = R"(
 let t : (B), t = (true, false);
@@ -702,7 +706,7 @@ let t : (B), t = (true, false);
         CHECK_AST(data, result);
       }
 
-      SECTION("N tuples")
+      SECTION("-> (N)")
       {
         std::string_view data = R"(
 let t : (N), t = (1, 2, 3, 5);
@@ -722,7 +726,7 @@ let t : (N), t = (1, 2, 3, 5);
         CHECK_AST(data, result);
       }
 
-      SECTION("Z tuples")
+      SECTION("-> (Z)")
       {
         std::string_view data = R"(
 let n : N, n = 3;
@@ -745,26 +749,31 @@ let t : (Z), t = (2, n, true);
         CHECK_AST(data, result);
       }
 
-      SECTION("R tuples")
+      SECTION("-> (R)")
       {
         std::string_view data = R"(
-let t : (R), t = (2, 3.1, 5);
+let n : N, n = 2;
+let t : (R), t = (n, 3.1, 5, true);
 )";
 
         std::string_view result = R"(
 (root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
+ |   +-(language::name:n:NameProcessor)
+ |   `-(language::integer:2:ValueProcessor)
  `-(language::eq_op:AffectationToTupleFromListProcessor<double>)
      +-(language::name:t:NameProcessor)
      `-(language::expression_list:ASTNodeExpressionListProcessor)
-         +-(language::integer:2:ValueProcessor)
+         +-(language::name:n:NameProcessor)
          +-(language::real:3.1:ValueProcessor)
-         `-(language::integer:5:ValueProcessor)
+         +-(language::integer:5:ValueProcessor)
+         `-(language::true_kw:ValueProcessor)
 )";
 
         CHECK_AST(data, result);
       }
 
-      SECTION("R^d tuples")
+      SECTION("-> (R^d)")
       {
         std::string_view data = R"(
 let a : R^2, a = [2,3.1];
@@ -803,26 +812,128 @@ let t3 : (R^1), t3 = (1, 2.3, 0);
         CHECK_AST(data, result);
       }
 
-      SECTION("string tuples")
+      SECTION("-> (R^dxd)")
+      {
+        std::string_view data = R"(
+let a : R^2x2, a = [[2, 5],[2, 3.1]];
+let t1 : (R^2x2), t1 = (a, [[1,2],[5,6]], 0);
+let t2 : (R^3x3), t2 = (0, [[1,2,3],[4,5,6],[7,8,9]]);
+let t3 : (R^1x1), t3 = (1, [[2.3]], 0);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, TinyMatrix<2ul, 2ul, double>, TinyMatrix<2ul, 2ul, double> >)
+ |   +-(language::name:a:NameProcessor)
+ |   `-(language::matrix_expression:TinyMatrixExpressionProcessor<2ul, 2ul>)
+ |       +-(language::row_expression:FakeProcessor)
+ |       |   +-(language::integer:2:ValueProcessor)
+ |       |   `-(language::integer:5:ValueProcessor)
+ |       `-(language::row_expression:FakeProcessor)
+ |           +-(language::integer:2:ValueProcessor)
+ |           `-(language::real:3.1:ValueProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<TinyMatrix<2ul, 2ul, double> >)
+ |   +-(language::name:t1:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::name:a:NameProcessor)
+ |       +-(language::matrix_expression:TinyMatrixExpressionProcessor<2ul, 2ul>)
+ |       |   +-(language::row_expression:FakeProcessor)
+ |       |   |   +-(language::integer:1:ValueProcessor)
+ |       |   |   `-(language::integer:2:ValueProcessor)
+ |       |   `-(language::row_expression:FakeProcessor)
+ |       |       +-(language::integer:5:ValueProcessor)
+ |       |       `-(language::integer:6:ValueProcessor)
+ |       `-(language::integer:0:ValueProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<TinyMatrix<3ul, 3ul, double> >)
+ |   +-(language::name:t2:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::integer:0:ValueProcessor)
+ |       `-(language::matrix_expression:TinyMatrixExpressionProcessor<3ul, 3ul>)
+ |           +-(language::row_expression:FakeProcessor)
+ |           |   +-(language::integer:1:ValueProcessor)
+ |           |   +-(language::integer:2:ValueProcessor)
+ |           |   `-(language::integer:3:ValueProcessor)
+ |           +-(language::row_expression:FakeProcessor)
+ |           |   +-(language::integer:4:ValueProcessor)
+ |           |   +-(language::integer:5:ValueProcessor)
+ |           |   `-(language::integer:6:ValueProcessor)
+ |           `-(language::row_expression:FakeProcessor)
+ |               +-(language::integer:7:ValueProcessor)
+ |               +-(language::integer:8:ValueProcessor)
+ |               `-(language::integer:9:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleFromListProcessor<TinyMatrix<1ul, 1ul, double> >)
+     +-(language::name:t3:NameProcessor)
+     `-(language::expression_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::matrix_expression:TinyMatrixExpressionProcessor<1ul, 1ul>)
+         |   `-(language::row_expression:FakeProcessor)
+         |       `-(language::real:2.3:ValueProcessor)
+         `-(language::integer:0:ValueProcessor)
+)";
+        CHECK_AST(data, result);
+      }
+
+      SECTION("-> (string)")
       {
         std::string_view data = R"(
-let t : (string), t = ("foo", "bar");
+let n : N, n = 3;
+let t : (string), t = ("foo", "bar", n, 1, 1.2,
+                       [1], [1,2], [1,2,3],
+                       [[2]], [[1,2],[3,4]], [[1,2,3],[4,5,6],[7,8,9]]);
 )";
 
         std::string result = R"(
 (root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
+ |   +-(language::name:n:NameProcessor)
+ |   `-(language::integer:3:ValueProcessor)
  `-(language::eq_op:AffectationToTupleFromListProcessor<)" +
                              demangled_stdstring + R"( >)
      +-(language::name:t:NameProcessor)
      `-(language::expression_list:ASTNodeExpressionListProcessor)
          +-(language::literal:"foo":ValueProcessor)
-         `-(language::literal:"bar":ValueProcessor)
+         +-(language::literal:"bar":ValueProcessor)
+         +-(language::name:n:NameProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::real:1.2:ValueProcessor)
+         +-(language::vector_expression:TinyVectorExpressionProcessor<1ul>)
+         |   `-(language::integer:1:ValueProcessor)
+         +-(language::vector_expression:TinyVectorExpressionProcessor<2ul>)
+         |   +-(language::integer:1:ValueProcessor)
+         |   `-(language::integer:2:ValueProcessor)
+         +-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+         |   +-(language::integer:1:ValueProcessor)
+         |   +-(language::integer:2:ValueProcessor)
+         |   `-(language::integer:3:ValueProcessor)
+         +-(language::matrix_expression:TinyMatrixExpressionProcessor<1ul, 1ul>)
+         |   `-(language::row_expression:FakeProcessor)
+         |       `-(language::integer:2:ValueProcessor)
+         +-(language::matrix_expression:TinyMatrixExpressionProcessor<2ul, 2ul>)
+         |   +-(language::row_expression:FakeProcessor)
+         |   |   +-(language::integer:1:ValueProcessor)
+         |   |   `-(language::integer:2:ValueProcessor)
+         |   `-(language::row_expression:FakeProcessor)
+         |       +-(language::integer:3:ValueProcessor)
+         |       `-(language::integer:4:ValueProcessor)
+         `-(language::matrix_expression:TinyMatrixExpressionProcessor<3ul, 3ul>)
+             +-(language::row_expression:FakeProcessor)
+             |   +-(language::integer:1:ValueProcessor)
+             |   +-(language::integer:2:ValueProcessor)
+             |   `-(language::integer:3:ValueProcessor)
+             +-(language::row_expression:FakeProcessor)
+             |   +-(language::integer:4:ValueProcessor)
+             |   +-(language::integer:5:ValueProcessor)
+             |   `-(language::integer:6:ValueProcessor)
+             `-(language::row_expression:FakeProcessor)
+                 +-(language::integer:7:ValueProcessor)
+                 +-(language::integer:8:ValueProcessor)
+                 `-(language::integer:9:ValueProcessor)
 )";
 
         CHECK_AST(data, result);
       }
 
-      SECTION("type_id tuples")
+      SECTION("-> (builtin_t)")
       {
         std::string_view data = R"(
 let t : (builtin_t), t= (a,b,a);
@@ -842,48 +953,108 @@ let t : (builtin_t), t= (a,b,a);
       }
     }
 
-    SECTION("tuples from singleton")
+    SECTION("value -> tuple")
     {
-      SECTION("B tuples")
+      SECTION(" -> (B)")
       {
-        std::string_view data = R"(
+        SECTION("B -> (B)")
+        {
+          std::string_view data = R"(
 let t : (B), t = true;
 )";
 
-        std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::eq_op:AffectationToTupleProcessor<bool>)
      +-(language::name:t:NameProcessor)
      `-(language::true_kw:ValueProcessor)
 )";
 
-        CHECK_AST(data, result);
+          CHECK_AST(data, result);
+        }
       }
 
-      SECTION("N tuples")
+      SECTION("-> (N)")
       {
-        std::string_view data = R"(
+        SECTION("B -> (N)")
+        {
+          std::string_view data = R"(
+let t : (N), t = true;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<unsigned long>)
+     +-(language::name:t:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("N -> (N)")
+        {
+          std::string_view data = R"(
+let n : N, n = 2;
+let t : (N), t = n;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
+ |   +-(language::name:n:NameProcessor)
+ |   `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<unsigned long>)
+     +-(language::name:t:NameProcessor)
+     `-(language::name:n:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z -> (N)")
+        {
+          std::string_view data = R"(
 let t : (N), t = 1;
 )";
 
-        std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::eq_op:AffectationToTupleProcessor<unsigned long>)
      +-(language::name:t:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-        CHECK_AST(data, result);
+          CHECK_AST(data, result);
+        }
       }
 
-      SECTION("Z tuples")
+      SECTION("-> (Z)")
       {
-        std::string_view data = R"(
+        SECTION("B -> (Z)")
+        {
+          std::string_view data = R"(
+let t : (Z), t = true;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<long>)
+     +-(language::name:t:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("N -> (Z)")
+        {
+          std::string_view data = R"(
 let n : N, n = 3;
 let t : (Z), t = n;
 )";
 
-        std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
  |   +-(language::name:n:NameProcessor)
@@ -893,35 +1064,178 @@ let t : (Z), t = n;
      `-(language::name:n:NameProcessor)
 )";
 
-        CHECK_AST(data, result);
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z -> (Z)")
+        {
+          std::string_view data = R"(
+let t : (Z), t = -2;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<long>)
+     +-(language::name:t:NameProcessor)
+     `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
       }
 
-      SECTION("R tuples")
+      SECTION("-> (R)")
       {
-        std::string_view data = R"(
+        SECTION("B -> (R)")
+        {
+          std::string_view data = R"(
+let t : (R), t = true;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<double>)
+     +-(language::name:t:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("N -> (R)")
+        {
+          std::string_view data = R"(
+let n : N, n = 2;
+let t : (R), t = n;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
+ |   +-(language::name:n:NameProcessor)
+ |   `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<double>)
+     +-(language::name:t:NameProcessor)
+     `-(language::name:n:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z -> (R)")
+        {
+          std::string_view data = R"(
+let t : (R), t = 3;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<double>)
+     +-(language::name:t:NameProcessor)
+     `-(language::integer:3:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("R -> (R)")
+        {
+          std::string_view data = R"(
 let t : (R), t = 3.1;
 )";
 
-        std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::eq_op:AffectationToTupleProcessor<double>)
      +-(language::name:t:NameProcessor)
      `-(language::real:3.1:ValueProcessor)
 )";
 
-        CHECK_AST(data, result);
+          CHECK_AST(data, result);
+        }
       }
 
-      SECTION("R^d tuples")
+      SECTION("-> R^d")
       {
-        std::string_view data = R"(
+        SECTION("B -> (R^1)")
+        {
+          std::string_view data = R"(
+let t : (R^1), t = false;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<TinyVector<1ul, double> >)
+     +-(language::name:t:NameProcessor)
+     `-(language::false_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("N -> (R^1)")
+        {
+          std::string_view data = R"(
+let n : N, n = 3;
+let t : (R^1), t = n;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
+ |   +-(language::name:n:NameProcessor)
+ |   `-(language::integer:3:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<TinyVector<1ul, double> >)
+     +-(language::name:t:NameProcessor)
+     `-(language::name:n:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z -> (R^1)")
+        {
+          std::string_view data = R"(
+let t : (R^1), t = 3;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<TinyVector<1ul, double> >)
+     +-(language::name:t:NameProcessor)
+     `-(language::integer:3:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("R -> (R^1)")
+        {
+          std::string_view data = R"(
+let t : (R^1), t = 3.3;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<TinyVector<1ul, double> >)
+     +-(language::name:t:NameProcessor)
+     `-(language::real:3.3:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("R^d -> (R^d)")
+        {
+          std::string_view data = R"(
 let a : R^2, a = [2,3.1];
 let t1 : (R^2), t1 = a;
 let t2 : (R^3), t2 = 0;
 let t3 : (R^1), t3 = 2.3;
 )";
 
-        std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  +-(language::eq_op:AffectationProcessor<language::eq_op, TinyVector<2ul, double>, TinyVector<2ul, double> >)
  |   +-(language::name:a:NameProcessor)
@@ -939,919 +1253,6281 @@ let t3 : (R^1), t3 = 2.3;
      `-(language::real:2.3:ValueProcessor)
 )";
 
-        CHECK_AST(data, result);
+          CHECK_AST(data, result);
+        }
       }
 
-      SECTION("string tuples")
+      SECTION("-> R^dxd")
       {
-        std::string_view data = R"(
-let t : (string), t = "foo";
+        SECTION("B -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let t : (R^1x1), t = false;
 )";
 
-        std::string result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::eq_op:AffectationToTupleProcessor<)" +
-                             demangled_stdstring + R"( >)
+ `-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
      +-(language::name:t:NameProcessor)
-     `-(language::literal:"foo":ValueProcessor)
+     `-(language::false_kw:ValueProcessor)
 )";
 
-        CHECK_AST(data, result);
-      }
+          CHECK_AST(data, result);
+        }
 
-      SECTION("type_id tuples")
-      {
-        std::string_view data = R"(
-let t : (builtin_t), t = a;
+        SECTION("N -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let n : N, n = 3;
+let t : (R^1x1), t = n;
 )";
 
-        std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::eq_op:AffectationToTupleProcessor<EmbeddedData>)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
+ |   +-(language::name:n:NameProcessor)
+ |   `-(language::integer:3:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
      +-(language::name:t:NameProcessor)
-     `-(language::name:a:NameProcessor)
+     `-(language::name:n:NameProcessor)
 )";
 
-        CHECK_AST_WITH_BUILTIN(data, result);
-      }
-    }
-  }
+          CHECK_AST(data, result);
+        }
 
-  SECTION("+=")
-  {
-    SECTION("N += N")
-    {
-      std::string_view data = R"(
-let n : N, n=1; n+=n;
+        SECTION("Z -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let t : (R^1x1), t = 3;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
- |   +-(language::name:n:NameProcessor)
- |   `-(language::integer:1:ValueProcessor)
- `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, unsigned long, unsigned long>)
-     +-(language::name:n:NameProcessor)
-     `-(language::name:n:NameProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
+     +-(language::name:t:NameProcessor)
+     `-(language::integer:3:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R += N")
-    {
-      std::string_view data = R"(
-let x : R, x=1; x+=2;
+        SECTION("R -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let t : (R^1x1), t = 3.3;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
- |   +-(language::name:x:NameProcessor)
- |   `-(language::integer:1:ValueProcessor)
- `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, double, long>)
-     +-(language::name:x:NameProcessor)
-     `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
+     +-(language::name:t:NameProcessor)
+     `-(language::real:3.3:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("string += N")
-    {
-      std::string_view data = R"(
-let s : string, s="foo"; s+=2;
+        SECTION("R^dxd -> (R^dxd)")
+        {
+          std::string_view data = R"(
+let a : R^2x2, a = [[2,3.1],[2,3.2]];
+let t1 : (R^2x2), t1 = a;
+let t2 : (R^3x3), t2 = 0;
+let t3 : (R^1x1), t3 = 2.3;
 )";
 
-      std::string result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, )" +
-                           demangled_stdstring + ", " + demangled_stdstring + R"( >)
- |   +-(language::name:s:NameProcessor)
- |   `-(language::literal:"foo":ValueProcessor)
- `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, )" +
-                           demangled_stdstring +
-                           R"(, long>)
-     +-(language::name:s:NameProcessor)
-     `-(language::integer:2:ValueProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, TinyMatrix<2ul, 2ul, double>, TinyMatrix<2ul, 2ul, double> >)
+ |   +-(language::name:a:NameProcessor)
+ |   `-(language::matrix_expression:TinyMatrixExpressionProcessor<2ul, 2ul>)
+ |       +-(language::row_expression:FakeProcessor)
+ |       |   +-(language::integer:2:ValueProcessor)
+ |       |   `-(language::real:3.1:ValueProcessor)
+ |       `-(language::row_expression:FakeProcessor)
+ |           +-(language::integer:2:ValueProcessor)
+ |           `-(language::real:3.2:ValueProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<2ul, 2ul, double> >)
+ |   +-(language::name:t1:NameProcessor)
+ |   `-(language::name:a:NameProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<3ul, 3ul, double> >)
+ |   +-(language::name:t2:NameProcessor)
+ |   `-(language::integer:0:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
+     +-(language::name:t3:NameProcessor)
+     `-(language::real:2.3:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
+      }
 
-    SECTION("R^1 += R^1")
-    {
-      std::string_view data = R"(
-let x : R^1;
-let y : R^1;
-x += y;
+      SECTION("-> (string)")
+      {
+        SECTION("B -> (string)")
+        {
+          std::string_view data = R"(
+let t : (string), t = true;
 )";
 
-      std::string result = R"(
+          std::string result = R"(
 (root:ASTNodeListProcessor)
- `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, TinyVector<1ul, double>, TinyVector<1ul, double> >)
-     +-(language::name:x:NameProcessor)
-     `-(language::name:y:NameProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:t:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^2 += R^2")
-    {
-      std::string_view data = R"(
-let x : R^2;
-let y : R^2;
-x += y;
+        SECTION("N -> (string)")
+        {
+          std::string_view data = R"(
+let n : N, n = 2;
+let t : (string), t = true;
 )";
 
-      std::string result = R"(
+          std::string result = R"(
 (root:ASTNodeListProcessor)
- `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, TinyVector<2ul, double>, TinyVector<2ul, double> >)
-     +-(language::name:x:NameProcessor)
-     `-(language::name:y:NameProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
+ |   +-(language::name:n:NameProcessor)
+ |   `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:t:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^3 += R^3")
-    {
-      std::string_view data = R"(
-let x : R^3;
-let y : R^3;
-x += y;
+        SECTION("Z -> (string)")
+        {
+          std::string_view data = R"(
+let t : (string), t = -2;
 )";
 
-      std::string result = R"(
+          std::string result = R"(
 (root:ASTNodeListProcessor)
- `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, TinyVector<3ul, double>, TinyVector<3ul, double> >)
-     +-(language::name:x:NameProcessor)
-     `-(language::name:y:NameProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:t:NameProcessor)
+     `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
-  }
+          CHECK_AST(data, result);
+        }
 
-  SECTION("-=")
-  {
-    SECTION("Z -= Z")
-    {
-      std::string_view data = R"(
-let z : Z, z=1; z-=2;
+        SECTION("R -> (string)")
+        {
+          std::string_view data = R"(
+let t : (string), t = 3.2;
 )";
 
-      std::string_view result = R"(
+          std::string result = R"(
 (root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, long, long>)
- |   +-(language::name:z:NameProcessor)
- |   `-(language::integer:1:ValueProcessor)
- `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, long, long>)
-     +-(language::name:z:NameProcessor)
-     `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:t:NameProcessor)
+     `-(language::real:3.2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R -= R")
-    {
-      std::string_view data = R"(
-let x : R, x=1; x-=2.3;
+        SECTION("R^1 -> (string)")
+        {
+          std::string_view data = R"(
+let t : (string), t = [1.5];
 )";
 
-      std::string_view result = R"(
+          std::string result = R"(
 (root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
- |   +-(language::name:x:NameProcessor)
- |   `-(language::integer:1:ValueProcessor)
- `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, double, double>)
-     +-(language::name:x:NameProcessor)
-     `-(language::real:2.3:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:t:NameProcessor)
+     `-(language::vector_expression:TinyVectorExpressionProcessor<1ul>)
+         `-(language::real:1.5:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^1 -= R^1")
-    {
-      std::string_view data = R"(
-let x : R^1;
-let y : R^1;
-x -= y;
+        SECTION("R^2 -> (string)")
+        {
+          std::string_view data = R"(
+let t : (string), t = [1.5, 2];
 )";
 
-      std::string result = R"(
+          std::string result = R"(
 (root:ASTNodeListProcessor)
- `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, TinyVector<1ul, double>, TinyVector<1ul, double> >)
-     +-(language::name:x:NameProcessor)
-     `-(language::name:y:NameProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:t:NameProcessor)
+     `-(language::vector_expression:TinyVectorExpressionProcessor<2ul>)
+         +-(language::real:1.5:ValueProcessor)
+         `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^2 -= R^2")
-    {
-      std::string_view data = R"(
-let x : R^2;
-let y : R^2;
-x -= y;
+        SECTION("R^3 -> (string)")
+        {
+          std::string_view data = R"(
+let t : (string), t = [1.5, 2, 1];
 )";
 
-      std::string result = R"(
+          std::string result = R"(
 (root:ASTNodeListProcessor)
- `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, TinyVector<2ul, double>, TinyVector<2ul, double> >)
-     +-(language::name:x:NameProcessor)
-     `-(language::name:y:NameProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:t:NameProcessor)
+     `-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+         +-(language::real:1.5:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^3 -= R^3")
-    {
-      std::string_view data = R"(
-let x : R^3;
-let y : R^3;
-x -= y;
+        SECTION("R^1x1 -> (string)")
+        {
+          std::string_view data = R"(
+let t : (string), t = [[1.5]];
 )";
 
-      std::string result = R"(
+          std::string result = R"(
 (root:ASTNodeListProcessor)
- `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, TinyVector<3ul, double>, TinyVector<3ul, double> >)
-     +-(language::name:x:NameProcessor)
-     `-(language::name:y:NameProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:t:NameProcessor)
+     `-(language::matrix_expression:TinyMatrixExpressionProcessor<1ul, 1ul>)
+         `-(language::row_expression:FakeProcessor)
+             `-(language::real:1.5:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
-  }
+          CHECK_AST(data, result);
+        }
 
-  SECTION("*=")
-  {
-    SECTION("Z *= Z")
-    {
-      std::string_view data = R"(
-let z : Z, z=1; z*=2;
+        SECTION("R^2x2 -> (string)")
+        {
+          std::string_view data = R"(
+let t : (string), t = [[1.5, 1],[-2, 3]];
 )";
 
-      std::string_view result = R"(
+          std::string result = R"(
 (root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, long, long>)
- |   +-(language::name:z:NameProcessor)
- |   `-(language::integer:1:ValueProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, long, long>)
-     +-(language::name:z:NameProcessor)
-     `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:t:NameProcessor)
+     `-(language::matrix_expression:TinyMatrixExpressionProcessor<2ul, 2ul>)
+         +-(language::row_expression:FakeProcessor)
+         |   +-(language::real:1.5:ValueProcessor)
+         |   `-(language::integer:1:ValueProcessor)
+         `-(language::row_expression:FakeProcessor)
+             +-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+             |   `-(language::integer:2:ValueProcessor)
+             `-(language::integer:3:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R *= R")
-    {
-      std::string_view data = R"(
-let x : R, x=1; x*=2.3;
+        SECTION("R^3x3 -> (string)")
+        {
+          std::string_view data = R"(
+let t : (string), t = [[1.5, 1, -1],[-2, 3, 1],[-5, 1, 6]];
 )";
 
-      std::string_view result = R"(
+          std::string result = R"(
 (root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
- |   +-(language::name:x:NameProcessor)
- |   `-(language::integer:1:ValueProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, double, double>)
-     +-(language::name:x:NameProcessor)
-     `-(language::real:2.3:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:t:NameProcessor)
+     `-(language::matrix_expression:TinyMatrixExpressionProcessor<3ul, 3ul>)
+         +-(language::row_expression:FakeProcessor)
+         |   +-(language::real:1.5:ValueProcessor)
+         |   +-(language::integer:1:ValueProcessor)
+         |   `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         |       `-(language::integer:1:ValueProcessor)
+         +-(language::row_expression:FakeProcessor)
+         |   +-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         |   |   `-(language::integer:2:ValueProcessor)
+         |   +-(language::integer:3:ValueProcessor)
+         |   `-(language::integer:1:ValueProcessor)
+         `-(language::row_expression:FakeProcessor)
+             +-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+             |   `-(language::integer:5:ValueProcessor)
+             +-(language::integer:1:ValueProcessor)
+             `-(language::integer:6:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("string -> (string)")
+        {
+          std::string_view data = R"(
+let t : (string), t = "foo";
 )";
 
-      CHECK_AST(data, result);
-    }
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:t:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
 
-    SECTION("R^1 *= R")
-    {
-      std::string_view data = R"(
-let x : R^1; x*=2.3;
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("-> (type_id)")
+      {
+        std::string_view data = R"(
+let t : (builtin_t), t = a;
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, double>)
-     +-(language::name:x:NameProcessor)
-     `-(language::real:2.3:ValueProcessor)
+ `-(language::eq_op:AffectationToTupleProcessor<EmbeddedData>)
+     +-(language::name:t:NameProcessor)
+     `-(language::name:a:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
+        CHECK_AST_WITH_BUILTIN(data, result);
+      }
     }
 
-    SECTION("R^2 *= R")
+    SECTION("tuple -> value")
     {
-      std::string_view data = R"(
-let x : R^2; x*= 6.2;
+      SECTION(" -> B")
+      {
+        SECTION("(B) -> B")
+        {
+          std::string_view data = R"(
+let t : (B), t = true;
+let b : B, b = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, double>)
-     +-(language::name:x:NameProcessor)
-     `-(language::real:6.2:ValueProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<bool>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::true_kw:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<bool>)
+     +-(language::name:b:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
+      }
 
-    SECTION("R^3 *= R")
-    {
-      std::string_view data = R"(
-let x : R^3; x*= 3.1;
+      SECTION("-> N")
+      {
+        SECTION("(B) -> N")
+        {
+          std::string_view data = R"(
+let t : (B), t = true;
+let n : N, n = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, double>)
-     +-(language::name:x:NameProcessor)
-     `-(language::real:3.1:ValueProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<bool>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::true_kw:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<unsigned long>)
+     +-(language::name:n:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R *= Z")
-    {
-      std::string_view data = R"(
-let x : R, x=1; x*=2;
+        SECTION("(N) -> N")
+        {
+          std::string_view data = R"(
+let t : (N), t = 1;
+let n : N, n = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
- |   +-(language::name:x:NameProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<unsigned long>)
+ |   +-(language::name:t:NameProcessor)
  |   `-(language::integer:1:ValueProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, double, long>)
-     +-(language::name:x:NameProcessor)
-     `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<unsigned long>)
+     +-(language::name:n:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^1 *= Z")
-    {
-      std::string_view data = R"(
-let x : R^1; x *= 3;
+        SECTION("(Z) -> N")
+        {
+          std::string_view data = R"(
+let t : (Z), t = 1;
+let n : N, n = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, long>)
-     +-(language::name:x:NameProcessor)
-     `-(language::integer:3:ValueProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::integer:1:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<unsigned long>)
+     +-(language::name:n:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
+      }
 
-    SECTION("R^2 *= Z")
-    {
-      std::string_view data = R"(
-let x : R^2; x *= 6;
+      SECTION("-> Z")
+      {
+        SECTION("(B) -> Z")
+        {
+          std::string_view data = R"(
+let t : (B), t = true;
+let z : Z, z = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, long>)
-     +-(language::name:x:NameProcessor)
-     `-(language::integer:6:ValueProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<bool>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::true_kw:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<long>)
+     +-(language::name:z:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^3 *= Z")
-    {
-      std::string_view data = R"(
-let x : R^3; x *= 4;
+        SECTION("(N) -> Z")
+        {
+          std::string_view data = R"(
+let t : (N), t = 2;
+let z : Z, z = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, long>)
-     +-(language::name:x:NameProcessor)
-     `-(language::integer:4:ValueProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<unsigned long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<long>)
+     +-(language::name:z:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R *= N")
-    {
-      std::string_view data = R"(
-let n : N, n=2; let x : R, x=1; x *= n;
+        SECTION("(Z) -> Z")
+        {
+          std::string_view data = R"(
+let t : (Z), t = -2;
+let z : Z, z = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
- |   +-(language::name:n:NameProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+ |       `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<long>)
+     +-(language::name:z:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("-> R")
+      {
+        SECTION("(B) -> R")
+        {
+          std::string_view data = R"(
+let t : (B), t = true;
+let x: R, x = t;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<bool>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::true_kw:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<double>)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(N) -> R")
+        {
+          std::string_view data = R"(
+let t : (N), t = 2;
+let x : R, x = 2;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<unsigned long>)
+ |   +-(language::name:t:NameProcessor)
  |   `-(language::integer:2:ValueProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
- |   +-(language::name:x:NameProcessor)
- |   `-(language::integer:1:ValueProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, double, unsigned long>)
+ `-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
      +-(language::name:x:NameProcessor)
-     `-(language::name:n:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^1 *= N")
-    {
-      std::string_view data = R"(
-let n : N;
-let x : R^1; x *= n;
+        SECTION("(Z) -> R")
+        {
+          std::string_view data = R"(
+let t : (Z), t = 3;
+let x : R, x = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, unsigned long>)
+ +-(language::eq_op:AffectationToTupleProcessor<long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::integer:3:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<double>)
      +-(language::name:x:NameProcessor)
-     `-(language::name:n:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^2 *= N")
-    {
-      std::string_view data = R"(
-let n : N;
-let x : R^2; x *= n;
+        SECTION("R -> (R)")
+        {
+          std::string_view data = R"(
+let t : (R), t = 3.1;
+let x : R, x = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, unsigned long>)
+ +-(language::eq_op:AffectationToTupleProcessor<double>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::real:3.1:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<double>)
      +-(language::name:x:NameProcessor)
-     `-(language::name:n:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
+      }
 
-    SECTION("R^3 *= N")
-    {
-      std::string_view data = R"(
-let n : N;
-let x : R^3; x *= n;
+      SECTION("-> R^d")
+      {
+        SECTION("(B) -> R^1")
+        {
+          std::string_view data = R"(
+let t : (B), t = false;
+let x : R^1, x = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, unsigned long>)
+ +-(language::eq_op:AffectationToTupleProcessor<bool>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::false_kw:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyVector<1ul, double> >)
      +-(language::name:x:NameProcessor)
-     `-(language::name:n:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R *= B")
-    {
-      std::string_view data = R"(
-let x : R, x=1; x *= true;
+        SECTION("(N) -> R^1")
+        {
+          std::string_view data = R"(
+let t : (N), t = 3;
+let x : R^1, x = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
- |   +-(language::name:x:NameProcessor)
- |   `-(language::integer:1:ValueProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, double, bool>)
+ +-(language::eq_op:AffectationToTupleProcessor<unsigned long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::integer:3:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyVector<1ul, double> >)
      +-(language::name:x:NameProcessor)
-     `-(language::true_kw:ValueProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^1 *= B")
-    {
-      std::string_view data = R"(
-let x : R^1; x *= true;
+        SECTION("(Z) -> R^1")
+        {
+          std::string_view data = R"(
+let t : (Z), t = 3;
+let x : R^1, x = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, bool>)
+ +-(language::eq_op:AffectationToTupleProcessor<long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::integer:3:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyVector<1ul, double> >)
      +-(language::name:x:NameProcessor)
-     `-(language::true_kw:ValueProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^2 *= B")
-    {
-      std::string_view data = R"(
-let x : R^2; x *= false;
+        SECTION("(R) -> R^1")
+        {
+          std::string_view data = R"(
+let t : (R), t = 3.3;
+let x : R^1, x = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, bool>)
+ +-(language::eq_op:AffectationToTupleProcessor<double>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::real:3.3:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyVector<1ul, double> >)
      +-(language::name:x:NameProcessor)
-     `-(language::false_kw:ValueProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R^3 *= B")
-    {
-      std::string_view data = R"(
-let b : B; let x : R^3; x *= b;
+        SECTION("(R^1) -> R^1")
+        {
+          std::string_view data = R"(
+let t : (R^1), t = 2.3;
+let x : R^1, x = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, bool>)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyVector<1ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::real:2.3:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyVector<1ul, double> >)
      +-(language::name:x:NameProcessor)
-     `-(language::name:b:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
-  }
+          CHECK_AST(data, result);
+        }
 
-  SECTION("/=")
-  {
-    SECTION("Z /= Z")
-    {
-      std::string_view data = R"(
-let z : Z, z=6; z/=2;
+        SECTION("(R^2) -> R^2")
+        {
+          std::string_view data = R"(
+let t : (R^2), t = [2.3, 1];
+let x : R^2, x = t;
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, long, long>)
- |   +-(language::name:z:NameProcessor)
- |   `-(language::integer:6:ValueProcessor)
- `-(language::divideeq_op:AffectationProcessor<language::divideeq_op, long, long>)
-     +-(language::name:z:NameProcessor)
-     `-(language::integer:2:ValueProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyVector<2ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::vector_expression:TinyVectorExpressionProcessor<2ul>)
+ |       +-(language::real:2.3:ValueProcessor)
+ |       `-(language::integer:1:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyVector<2ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R^3) -> R^3")
+        {
+          std::string_view data = R"(
+let t : (R^3), t = [2.3, 1, 1];
+let x : R^3, x = t;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyVector<3ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+ |       +-(language::real:2.3:ValueProcessor)
+ |       +-(language::integer:1:ValueProcessor)
+ |       `-(language::integer:1:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyVector<3ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("-> R^dxd")
+      {
+        SECTION("(B) -> R^1x1")
+        {
+          std::string_view data = R"(
+let t : (B), t = false;
+let x : R^1x1, x = t;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<bool>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::false_kw:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(N) -> R^1x1")
+        {
+          std::string_view data = R"(
+let t : (N), t = 3;
+let x : R^1x1, x = t;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<unsigned long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::integer:3:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(Z) -> R^1x1")
+        {
+          std::string_view data = R"(
+let t : (Z), t = 3;
+let x : R^1x1, x = t;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::integer:3:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R) -> R^1x1")
+        {
+          std::string_view data = R"(
+let t : (R), t = 3.3;
+let x : R^1x1, x = t;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<double>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::real:3.3:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R^1x1) -> R^1x1")
+        {
+          std::string_view data = R"(
+let t : (R^1x1), t = 2.3;
+let x : R^1x1, x = t;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::real:2.3:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R^2x2) -> R^2x2")
+        {
+          std::string_view data = R"(
+let t : (R^2x2), t = [[2.3, 1], [6,5]];
+let x : R^2x2, x = t;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<2ul, 2ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::matrix_expression:TinyMatrixExpressionProcessor<2ul, 2ul>)
+ |       +-(language::row_expression:FakeProcessor)
+ |       |   +-(language::real:2.3:ValueProcessor)
+ |       |   `-(language::integer:1:ValueProcessor)
+ |       `-(language::row_expression:FakeProcessor)
+ |           +-(language::integer:6:ValueProcessor)
+ |           `-(language::integer:5:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyMatrix<2ul, 2ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R^3x3) -> R^3x3")
+        {
+          std::string_view data = R"(
+let t : (R^3x3), t = [[2.3, 1, 1],[1, 2, 3], [2,1,2]];
+let x : R^3x3, x = t;
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<3ul, 3ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::matrix_expression:TinyMatrixExpressionProcessor<3ul, 3ul>)
+ |       +-(language::row_expression:FakeProcessor)
+ |       |   +-(language::real:2.3:ValueProcessor)
+ |       |   +-(language::integer:1:ValueProcessor)
+ |       |   `-(language::integer:1:ValueProcessor)
+ |       +-(language::row_expression:FakeProcessor)
+ |       |   +-(language::integer:1:ValueProcessor)
+ |       |   +-(language::integer:2:ValueProcessor)
+ |       |   `-(language::integer:3:ValueProcessor)
+ |       `-(language::row_expression:FakeProcessor)
+ |           +-(language::integer:2:ValueProcessor)
+ |           +-(language::integer:1:ValueProcessor)
+ |           `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<TinyMatrix<3ul, 3ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("-> string")
+      {
+        SECTION("(B) -> string")
+        {
+          std::string_view data = R"(
+let t : (B), t = true;
+let s : string, s = t;
+)";
+
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<bool>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::true_kw:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(N) -> string")
+        {
+          std::string_view data = R"(
+let t : (N), t = true;
+let s : string, s = t;
+)";
+
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<unsigned long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::true_kw:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(Z) -> string")
+        {
+          std::string_view data = R"(
+let t : (Z), t = -2;
+let s : string, s = t;
+)";
+
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+ |       `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R) -> string")
+        {
+          std::string_view data = R"(
+let t : (R), t = 3.2;
+let s : string, s = t;
+)";
+
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<double>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::real:3.2:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R^1) -> string")
+        {
+          std::string_view data = R"(
+let t : (R^1), t = [1.5];
+let s : string, s = t;
+)";
+
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyVector<1ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::vector_expression:TinyVectorExpressionProcessor<1ul>)
+ |       `-(language::real:1.5:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R^2) -> string")
+        {
+          std::string_view data = R"(
+let t : (R^2), t = [1.5, 2];
+let s : string, s = t;
+)";
+
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyVector<2ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::vector_expression:TinyVectorExpressionProcessor<2ul>)
+ |       +-(language::real:1.5:ValueProcessor)
+ |       `-(language::integer:2:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R^3) -> string")
+        {
+          std::string_view data = R"(
+let t : (R^3), t = [1.5, 2, 1];
+let s : string, s = t;
+)";
+
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyVector<3ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+ |       +-(language::real:1.5:ValueProcessor)
+ |       +-(language::integer:2:ValueProcessor)
+ |       `-(language::integer:1:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R^1x1) -> string")
+        {
+          std::string_view data = R"(
+let t : (R^1x1), t = [[1.5]];
+let s : string, s = t;
+)";
+
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<1ul, 1ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::matrix_expression:TinyMatrixExpressionProcessor<1ul, 1ul>)
+ |       `-(language::row_expression:FakeProcessor)
+ |           `-(language::real:1.5:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R^2x2) -> string")
+        {
+          std::string_view data = R"(
+let t : (R^2x2), t = [[1.5, 1],[-2, 3]];
+let s : string, s = t;
+)";
+
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<2ul, 2ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::matrix_expression:TinyMatrixExpressionProcessor<2ul, 2ul>)
+ |       +-(language::row_expression:FakeProcessor)
+ |       |   +-(language::real:1.5:ValueProcessor)
+ |       |   `-(language::integer:1:ValueProcessor)
+ |       `-(language::row_expression:FakeProcessor)
+ |           +-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+ |           |   `-(language::integer:2:ValueProcessor)
+ |           `-(language::integer:3:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("(R^3x3) -> string")
+        {
+          std::string_view data = R"(
+let t : (R^3x3), t = [[1.5, 1, -1],[-2, 3, 1],[-5, 1, 6]];
+let s : string, s = t;
+)";
+
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<TinyMatrix<3ul, 3ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::matrix_expression:TinyMatrixExpressionProcessor<3ul, 3ul>)
+ |       +-(language::row_expression:FakeProcessor)
+ |       |   +-(language::real:1.5:ValueProcessor)
+ |       |   +-(language::integer:1:ValueProcessor)
+ |       |   `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+ |       |       `-(language::integer:1:ValueProcessor)
+ |       +-(language::row_expression:FakeProcessor)
+ |       |   +-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+ |       |   |   `-(language::integer:2:ValueProcessor)
+ |       |   +-(language::integer:3:ValueProcessor)
+ |       |   `-(language::integer:1:ValueProcessor)
+ |       `-(language::row_expression:FakeProcessor)
+ |           +-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+ |           |   `-(language::integer:5:ValueProcessor)
+ |           +-(language::integer:1:ValueProcessor)
+ |           `-(language::integer:6:ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("string -> (string)")
+        {
+          std::string_view data = R"(
+let t : (string), t = "foo";
+let s : string, s = t;
+)";
+
+          std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::literal:"foo":ValueProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<)" +
+                               demangled_stdstring + R"( >)
+     +-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("-> type_id")
+      {
+        std::string_view data = R"(
+let t : (builtin_t), t = a;
+let bt: builtin_t, bt = t;
+)";
+
+        std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleProcessor<EmbeddedData>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::name:a:NameProcessor)
+ `-(language::eq_op:AffectationFromTupleProcessor<EmbeddedData>)
+     +-(language::name:bt:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+        CHECK_AST_WITH_BUILTIN(data, result);
+      }
+    }
+  }
+
+  SECTION("+=")
+  {
+    SECTION("N += N")
+    {
+      std::string_view data = R"(
+let n : N, n=1; n+=n;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
+ |   +-(language::name:n:NameProcessor)
+ |   `-(language::integer:1:ValueProcessor)
+ `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, unsigned long, unsigned long>)
+     +-(language::name:n:NameProcessor)
+     `-(language::name:n:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R += N")
+    {
+      std::string_view data = R"(
+let x : R, x=1; x+=2;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
+ |   +-(language::name:x:NameProcessor)
+ |   `-(language::integer:1:ValueProcessor)
+ `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, double, long>)
+     +-(language::name:x:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("string += N")
+    {
+      std::string_view data = R"(
+let s : string, s="foo"; s+=2;
+)";
+
+      std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, )" +
+                           demangled_stdstring + ", " + demangled_stdstring + R"( >)
+ |   +-(language::name:s:NameProcessor)
+ |   `-(language::literal:"foo":ValueProcessor)
+ `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, )" +
+                           demangled_stdstring +
+                           R"(, long>)
+     +-(language::name:s:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^1 += R^1")
+    {
+      std::string_view data = R"(
+let x : R^1;
+let y : R^1;
+x += y;
+)";
+
+      std::string result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, TinyVector<1ul, double>, TinyVector<1ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:y:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^2 += R^2")
+    {
+      std::string_view data = R"(
+let x : R^2;
+let y : R^2;
+x += y;
+)";
+
+      std::string result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, TinyVector<2ul, double>, TinyVector<2ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:y:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^3 += R^3")
+    {
+      std::string_view data = R"(
+let x : R^3;
+let y : R^3;
+x += y;
+)";
+
+      std::string result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::pluseq_op:AffectationProcessor<language::pluseq_op, TinyVector<3ul, double>, TinyVector<3ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:y:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+  }
+
+  SECTION("-=")
+  {
+    SECTION("Z -= Z")
+    {
+      std::string_view data = R"(
+let z : Z, z=1; z-=2;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, long, long>)
+ |   +-(language::name:z:NameProcessor)
+ |   `-(language::integer:1:ValueProcessor)
+ `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, long, long>)
+     +-(language::name:z:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R -= R")
+    {
+      std::string_view data = R"(
+let x : R, x=1; x-=2.3;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
+ |   +-(language::name:x:NameProcessor)
+ |   `-(language::integer:1:ValueProcessor)
+ `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, double, double>)
+     +-(language::name:x:NameProcessor)
+     `-(language::real:2.3:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^1 -= R^1")
+    {
+      std::string_view data = R"(
+let x : R^1;
+let y : R^1;
+x -= y;
+)";
+
+      std::string result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, TinyVector<1ul, double>, TinyVector<1ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:y:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^2 -= R^2")
+    {
+      std::string_view data = R"(
+let x : R^2;
+let y : R^2;
+x -= y;
+)";
+
+      std::string result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, TinyVector<2ul, double>, TinyVector<2ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:y:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^3 -= R^3")
+    {
+      std::string_view data = R"(
+let x : R^3;
+let y : R^3;
+x -= y;
+)";
+
+      std::string result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::minuseq_op:AffectationProcessor<language::minuseq_op, TinyVector<3ul, double>, TinyVector<3ul, double> >)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:y:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+  }
+
+  SECTION("*=")
+  {
+    SECTION("Z *= Z")
+    {
+      std::string_view data = R"(
+let z : Z, z=1; z*=2;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, long, long>)
+ |   +-(language::name:z:NameProcessor)
+ |   `-(language::integer:1:ValueProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, long, long>)
+     +-(language::name:z:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R *= R")
+    {
+      std::string_view data = R"(
+let x : R, x=1; x*=2.3;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
+ |   +-(language::name:x:NameProcessor)
+ |   `-(language::integer:1:ValueProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, double, double>)
+     +-(language::name:x:NameProcessor)
+     `-(language::real:2.3:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^1 *= R")
+    {
+      std::string_view data = R"(
+let x : R^1; x*=2.3;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, double>)
+     +-(language::name:x:NameProcessor)
+     `-(language::real:2.3:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^2 *= R")
+    {
+      std::string_view data = R"(
+let x : R^2; x*= 6.2;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, double>)
+     +-(language::name:x:NameProcessor)
+     `-(language::real:6.2:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^3 *= R")
+    {
+      std::string_view data = R"(
+let x : R^3; x*= 3.1;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, double>)
+     +-(language::name:x:NameProcessor)
+     `-(language::real:3.1:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R *= Z")
+    {
+      std::string_view data = R"(
+let x : R, x=1; x*=2;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
+ |   +-(language::name:x:NameProcessor)
+ |   `-(language::integer:1:ValueProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, double, long>)
+     +-(language::name:x:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^1 *= Z")
+    {
+      std::string_view data = R"(
+let x : R^1; x *= 3;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, long>)
+     +-(language::name:x:NameProcessor)
+     `-(language::integer:3:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^2 *= Z")
+    {
+      std::string_view data = R"(
+let x : R^2; x *= 6;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, long>)
+     +-(language::name:x:NameProcessor)
+     `-(language::integer:6:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^3 *= Z")
+    {
+      std::string_view data = R"(
+let x : R^3; x *= 4;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, long>)
+     +-(language::name:x:NameProcessor)
+     `-(language::integer:4:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R *= N")
+    {
+      std::string_view data = R"(
+let n : N, n=2; let x : R, x=1; x *= n;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, unsigned long, long>)
+ |   +-(language::name:n:NameProcessor)
+ |   `-(language::integer:2:ValueProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
+ |   +-(language::name:x:NameProcessor)
+ |   `-(language::integer:1:ValueProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, double, unsigned long>)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:n:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^1 *= N")
+    {
+      std::string_view data = R"(
+let n : N;
+let x : R^1; x *= n;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, unsigned long>)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:n:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^2 *= N")
+    {
+      std::string_view data = R"(
+let n : N;
+let x : R^2; x *= n;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, unsigned long>)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:n:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^3 *= N")
+    {
+      std::string_view data = R"(
+let n : N;
+let x : R^3; x *= n;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, unsigned long>)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:n:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R *= B")
+    {
+      std::string_view data = R"(
+let x : R, x=1; x *= true;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
+ |   +-(language::name:x:NameProcessor)
+ |   `-(language::integer:1:ValueProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, double, bool>)
+     +-(language::name:x:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^1 *= B")
+    {
+      std::string_view data = R"(
+let x : R^1; x *= true;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<1ul, double>, bool>)
+     +-(language::name:x:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^2 *= B")
+    {
+      std::string_view data = R"(
+let x : R^2; x *= false;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<2ul, double>, bool>)
+     +-(language::name:x:NameProcessor)
+     `-(language::false_kw:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R^3 *= B")
+    {
+      std::string_view data = R"(
+let b : B; let x : R^3; x *= b;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::multiplyeq_op:AffectationProcessor<language::multiplyeq_op, TinyVector<3ul, double>, bool>)
+     +-(language::name:x:NameProcessor)
+     `-(language::name:b:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+  }
+
+  SECTION("/=")
+  {
+    SECTION("Z /= Z")
+    {
+      std::string_view data = R"(
+let z : Z, z=6; z/=2;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, long, long>)
+ |   +-(language::name:z:NameProcessor)
+ |   `-(language::integer:6:ValueProcessor)
+ `-(language::divideeq_op:AffectationProcessor<language::divideeq_op, long, long>)
+     +-(language::name:z:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("R /= R")
+    {
+      std::string_view data = R"(
+let x : R, x=1; x/=2.3;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
+ |   +-(language::name:x:NameProcessor)
+ |   `-(language::integer:1:ValueProcessor)
+ `-(language::divideeq_op:AffectationProcessor<language::divideeq_op, double, double>)
+     +-(language::name:x:NameProcessor)
+     `-(language::real:2.3:ValueProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+  }
+
+  SECTION("Errors")
+  {
+    SECTION("Invalid affectation operator")
+    {
+      auto ast         = std::make_unique<ASTNode>();
+      ast->m_data_type = ASTNodeDataType::build<ASTNodeDataType::void_t>();
+      {
+        auto child_0         = std::make_unique<ASTNode>();
+        child_0->m_data_type = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
+        auto child_1         = std::make_unique<ASTNode>();
+        child_1->m_data_type = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
+        ast->children.emplace_back(std::move(child_0));
+        ast->children.emplace_back(std::move(child_1));
+      }
+      REQUIRE_THROWS_WITH(ASTNodeAffectationExpressionBuilder{*ast},
+                          "unexpected error: undefined affectation operator");
+    }
+
+    SECTION("Invalid string rhs")
+    {
+      auto ast = std::make_unique<ASTNode>();
+      ast->set_type<language::eq_op>();
+
+      ast->children.emplace_back(std::make_unique<ASTNode>());
+      ast->children[0]->m_data_type = ASTNodeDataType::build<ASTNodeDataType::string_t>();
+      ast->children.emplace_back(std::make_unique<ASTNode>());
+      REQUIRE_THROWS_WITH(ASTNodeAffectationExpressionBuilder{*ast}, "undefined affectation type: string = undefined");
+    }
+
+    SECTION("Invalid string affectation operator")
+    {
+      SECTION("string -= string")
+      {
+        std::string_view data = R"(
+let s : string, s="foo"; s-="bar";
+)";
+
+        std::string error_message = "undefined affectation type: string -= string";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("string *= Z")
+      {
+        std::string_view data = R"(
+let s : string, s="foo"; s*=2;
+)";
+
+        std::string error_message = "undefined affectation type: string *= Z";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("string /= string")
+      {
+        std::string_view data = R"(
+ let s : string, s="foo"; s/="bar";
+)";
+
+        std::string error_message = "undefined affectation type: string /= string";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+    }
+
+    SECTION("type_id operator")
+    {
+      std::string_view data = R"(
+ let s :builtin_t, s = a; s *= b;
+)";
+
+      std::string error_message = "undefined affectation type: builtin_t *= builtin_t";
+
+      CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+    }
+
+    SECTION("Invalid tuple operator")
+    {
+      std::string_view data = R"(
+ let s :(R), s=(1,2,3); s *= 4;
+)";
+
+      std::string error_message = "undefined affectation type: (R) *= Z";
+
+      CHECK_AST_THROWS_WITH(data, error_message);
+    }
+
+    SECTION("Invalid tuple operator 2")
+    {
+      std::string_view data = R"(
+ let s : (builtin_t), s =(a,b); s *= b;
+)";
+
+      std::string error_message = "undefined affectation type: (builtin_t) *= builtin_t";
+
+      CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+    }
+
+    SECTION("Invalid R^n -> R^m affectation")
+    {
+      SECTION("R^3 <- R^1")
+      {
+        std::string_view data = R"(
+let x : R^3; let y : R^1; x = y;
+)";
+
+        std::string error_message = "undefined affectation type: R^3 = R^1";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("R^3 <- R^2")
+      {
+        std::string_view data = R"(
+let x : R^3; let y : R^2; x = y;
+)";
+
+        std::string error_message = "undefined affectation type: R^3 = R^2";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("R^2 <- R^1")
+      {
+        std::string_view data = R"(
+let x : R^2; let y : R^1; x = y;
+)";
+
+        std::string error_message = "undefined affectation type: R^2 = R^1";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("R^2 <- R^3")
+      {
+        std::string_view data = R"(
+let x : R^2; let y : R^3; x = y;
+)";
+
+        std::string error_message = "undefined affectation type: R^2 = R^3";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("R^1 <- R^2")
+      {
+        std::string_view data = R"(
+let x : R^1; let y : R^2; x = y;
+)";
+
+        std::string error_message = "undefined affectation type: R^1 = R^2";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("R^1 <- R^3")
+      {
+        std::string_view data = R"(
+let x : R^1; let y : R^3; x = y;
+)";
+
+        std::string error_message = "undefined affectation type: R^1 = R^3";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+    }
+
+    SECTION("Invalid Z -> R^m affectation [non-zero]")
+    {
+      SECTION("R^3 <- Z")
+      {
+        std::string_view data = R"(
+let x : R^3, x = 3;
+)";
+
+        std::string error_message = "invalid integral value (0 is the solely valid value)";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("R^2 <- Z")
+      {
+        std::string_view data = R"(
+let x : R^2, x = 2;
+)";
+
+        std::string error_message = "invalid integral value (0 is the solely valid value)";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+    }
+
+    SECTION("Invalid R^d -> R^d affectation operator")
+    {
+      SECTION("R^3 <- R^3")
+      {
+        std::string_view data = R"(
+let x : R^3; let y : R^3; x /= y;
+)";
+
+        std::string error_message = "undefined affectation type: R^3 /= R^3";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("R^2 <- R^2")
+      {
+        std::string_view data = R"(
+let x : R^2; let y : R^2; x /= y;
+)";
+
+        std::string error_message = "undefined affectation type: R^2 /= R^2";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("R^1 <- R^1")
+      {
+        std::string_view data = R"(
+let x : R^1; let y : R^1; x /= y;
+)";
+
+        std::string error_message = "undefined affectation type: R^1 /= R^1";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+    }
+
+    SECTION("Invalid R^d -> R^d *= operand")
+    {
+      SECTION("R^3 <- R^3")
+      {
+        std::string_view data = R"(
+let x : R^3; let y : R^3; x *= y;
+)";
+
+        std::string error_message = "undefined affectation type: R^3 *= R^3";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("R^2 <- R^2")
+      {
+        std::string_view data = R"(
+let x : R^2; let y : R^2; x *= y;
+)";
+
+        std::string error_message = "undefined affectation type: R^2 *= R^2";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+
+      SECTION("R^1 <- R^1")
+      {
+        std::string_view data = R"(
+let x : R^1; let y : R^1; x *= y;
+)";
+
+        std::string error_message = "undefined affectation type: R^1 *= R^1";
+
+        CHECK_AST_THROWS_WITH(data, error_message);
+      }
+    }
+
+    SECTION("incorrect declarative/definition number of symbols")
+    {
+      std::string_view data = R"(
+let (x,y,z):R*R*R, (x,y) = (2,3);
+)";
+
+      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+      OperatorRepository::instance().reset();
+      ASTModulesImporter{*ast};
+
+      ASTSymbolTableBuilder{*ast};
+      REQUIRE_THROWS_WITH(ASTSymbolInitializationChecker{*ast},
+                          std::string{"invalid number of definition identifiers, expecting 3 found 2"});
+    }
+
+    SECTION("incorrect identifier/expression number of symbols")
+    {
+      std::string_view data = R"(
+let y:R;
+let x:R, (x,y) = (2,3);
+)";
+
+      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+      OperatorRepository::instance().reset();
+      ASTModulesImporter{*ast};
+
+      ASTSymbolTableBuilder{*ast};
+      REQUIRE_THROWS_WITH(ASTSymbolInitializationChecker{*ast},
+                          std::string{"unexpected variable list, expecting one identifier"});
+    }
+
+    SECTION("incorrect definition variable identifier")
+    {
+      std::string_view data = R"(
+let y:R;
+let x:R, y = 3;
+)";
+
+      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+      OperatorRepository::instance().reset();
+      ASTModulesImporter{*ast};
+
+      ASTSymbolTableBuilder{*ast};
+      REQUIRE_THROWS_WITH(ASTSymbolInitializationChecker{*ast}, std::string{"invalid identifier, expecting 'x'"});
+    }
+
+    SECTION("invalid definition variable identifier order")
+    {
+      std::string_view data = R"(
+let (x,y):R, (y,x) = (3,2);
+)";
+
+      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+      OperatorRepository::instance().reset();
+      ASTModulesImporter{*ast};
+
+      ASTSymbolTableBuilder{*ast};
+      REQUIRE_THROWS_WITH(ASTSymbolInitializationChecker{*ast}, std::string{"invalid identifier, expecting 'x'"});
+    }
+
+    SECTION("undefined affectations =")
+    {
+      SECTION("-> value")
+      {
+        SECTION("value -> value")
+        {
+          SECTION("-> B")
+          {
+            SECTION("N -> B")
+            {
+              std::string_view data = R"(
+let n:N, n = 1;
+let b:B, b = n;
+)";
+
+              std::string error_message = "undefined affectation type: B = N";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("Z -> B")
+            {
+              std::string_view data = R"(
+let b:B, b = 1;
+)";
+
+              std::string error_message = "undefined affectation type: B = Z";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R -> B")
+            {
+              std::string_view data = R"(
+let b:B, b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: B = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> B")
+            {
+              std::string_view data = R"(
+let b:B, b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: B = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> B")
+            {
+              std::string_view data = R"(
+let b:B, b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: B = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> B")
+            {
+              std::string_view data = R"(
+let b:B, b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: B = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> B")
+            {
+              std::string_view data = R"(
+let b:B, b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: B = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> B")
+            {
+              std::string_view data = R"(
+let b:B, b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: B = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> B")
+            {
+              std::string_view data = R"(
+let b:B, b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: B = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> B")
+            {
+              std::string_view data = R"(
+let b:B, b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: B = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> B")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : B, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: B = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> N")
+          {
+            SECTION("R -> N")
+            {
+              std::string_view data = R"(
+let b:N, b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: N = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> N")
+            {
+              std::string_view data = R"(
+let b:N, b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: N = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> N")
+            {
+              std::string_view data = R"(
+let b:N, b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: N = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> N")
+            {
+              std::string_view data = R"(
+let b:N, b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: N = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> N")
+            {
+              std::string_view data = R"(
+let b:N, b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: N = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> N")
+            {
+              std::string_view data = R"(
+let b:N, b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: N = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> N")
+            {
+              std::string_view data = R"(
+let b:N, b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: N = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> N")
+            {
+              std::string_view data = R"(
+let b:N, b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: N = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> N")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : N, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: N = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> Z")
+          {
+            SECTION("R -> Z")
+            {
+              std::string_view data = R"(
+let b:Z, b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: Z = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> Z")
+            {
+              std::string_view data = R"(
+let b:Z, b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: Z = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> Z")
+            {
+              std::string_view data = R"(
+let b:Z, b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: Z = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> Z")
+            {
+              std::string_view data = R"(
+let b:Z, b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: Z = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> Z")
+            {
+              std::string_view data = R"(
+let b:Z, b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: Z = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> Z")
+            {
+              std::string_view data = R"(
+let b:Z, b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: Z = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> Z")
+            {
+              std::string_view data = R"(
+let b:Z, b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: Z = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> Z")
+            {
+              std::string_view data = R"(
+let b:Z, b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: Z = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> Z")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : Z, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: Z = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R")
+          {
+            SECTION("R^1 -> R")
+            {
+              std::string_view data = R"(
+let b:R, b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: R = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> R")
+            {
+              std::string_view data = R"(
+let b:R, b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: R = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> R")
+            {
+              std::string_view data = R"(
+let b:R, b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: R = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> R")
+            {
+              std::string_view data = R"(
+let b:R, b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: R = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> R")
+            {
+              std::string_view data = R"(
+let b:R, b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: R = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> R")
+            {
+              std::string_view data = R"(
+let b:R, b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: R = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> R")
+            {
+              std::string_view data = R"(
+let b:R, b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: R = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> R")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : R, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^1")
+          {
+            SECTION("R^2 -> R^1")
+            {
+              std::string_view data = R"(
+let b:R^1, b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> R^1")
+            {
+              std::string_view data = R"(
+let b:R^1, b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> R^1")
+            {
+              std::string_view data = R"(
+let b:R^1, b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> R^1")
+            {
+              std::string_view data = R"(
+let b:R^1, b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> R^1")
+            {
+              std::string_view data = R"(
+let b:R^1, b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> R^1")
+            {
+              std::string_view data = R"(
+let b:R^1, b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> R^1")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : R^1, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^2")
+          {
+            SECTION("N -> R^2")
+            {
+              std::string_view data = R"(
+let n:N, n = 1;
+let b:R^2, b = n;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = N";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R -> R^2")
+            {
+              std::string_view data = R"(
+let b:R^2, b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> R^2")
+            {
+              std::string_view data = R"(
+let b:R^2, b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> R^2")
+            {
+              std::string_view data = R"(
+let b:R^2, b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> R^2")
+            {
+              std::string_view data = R"(
+let b:R^2, b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> R^2")
+            {
+              std::string_view data = R"(
+let b:R^2, b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> R^2")
+            {
+              std::string_view data = R"(
+let b:R^2, b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> R^2")
+            {
+              std::string_view data = R"(
+let b:R^2, b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> R^2")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : R^2, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^3")
+          {
+            SECTION("N -> R^3")
+            {
+              std::string_view data = R"(
+let n:N, n = 1;
+let b:R^3, b = n;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = N";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R -> R^3")
+            {
+              std::string_view data = R"(
+let b:R^3, b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> R^3")
+            {
+              std::string_view data = R"(
+let b:R^3, b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> R^3")
+            {
+              std::string_view data = R"(
+let b:R^3, b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> R^3")
+            {
+              std::string_view data = R"(
+let b:R^3, b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> R^3")
+            {
+              std::string_view data = R"(
+let b:R^3, b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> R^3")
+            {
+              std::string_view data = R"(
+let b:R^3, b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> R^3")
+            {
+              std::string_view data = R"(
+let b:R^3, b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> R^3")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : R^3, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^1x1")
+          {
+            SECTION("R^1 -> R^1x1")
+            {
+              std::string_view data = R"(
+let b:R^1x1, b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> R^1x1")
+            {
+              std::string_view data = R"(
+let b:R^1x1, b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> R^1x1")
+            {
+              std::string_view data = R"(
+let b:R^1x1, b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> R^1x1")
+            {
+              std::string_view data = R"(
+let b:R^1x1, b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> R^1x1")
+            {
+              std::string_view data = R"(
+let b:R^1x1, b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> R^1x1")
+            {
+              std::string_view data = R"(
+let b:R^1x1, b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> R^1x1")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : R^1x1, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^2x2")
+          {
+            SECTION("N -> R^2x2")
+            {
+              std::string_view data = R"(
+let n:N, n = 1;
+let b:R^2x2, b = n;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = N";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R -> R^2x2")
+            {
+              std::string_view data = R"(
+let b:R^2x2, b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> R^2x2")
+            {
+              std::string_view data = R"(
+let b:R^2x2, b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> R^2x2")
+            {
+              std::string_view data = R"(
+let b:R^2x2, b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> R^2x2")
+            {
+              std::string_view data = R"(
+let b:R^2x2, b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> R^2x2")
+            {
+              std::string_view data = R"(
+let b:R^2x2, b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> R^2x2")
+            {
+              std::string_view data = R"(
+let b:R^2x2, b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> R^2x2")
+            {
+              std::string_view data = R"(
+let b:R^2x2, b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> R^2x2")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : R^2x2, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^3x3")
+          {
+            SECTION("N -> R^3x3")
+            {
+              std::string_view data = R"(
+let n:N, n = 1;
+let b:R^3x3, b = n;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = N";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R -> R^3x3")
+            {
+              std::string_view data = R"(
+let b:R^3x3, b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> R^3x3")
+            {
+              std::string_view data = R"(
+let b:R^3x3, b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> R^3x3")
+            {
+              std::string_view data = R"(
+let b:R^3x3, b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> R^3x3")
+            {
+              std::string_view data = R"(
+let b:R^3x3, b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> R^3x3")
+            {
+              std::string_view data = R"(
+let b:R^3x3, b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> R^3x3")
+            {
+              std::string_view data = R"(
+let b:R^3x3, b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> R^3x3")
+            {
+              std::string_view data = R"(
+let b:R^3x3, b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> R^3")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : R^3x3, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+        }
+
+        SECTION("tuple -> value")
+        {
+          SECTION("-> B")
+          {
+            SECTION("(N) -> B")
+            {
+              std::string_view data = R"(
+let t:(N), t = 1;
+let b:B, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: B = (N)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(Z) -> B")
+            {
+              std::string_view data = R"(
+let t:(Z), t = 1;
+let b:B, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: B = (Z)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R) -> B")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1;
+let b:B, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: B = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> B")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = 1;
+let b:B, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: B = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> B")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:B, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: B = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> B")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1, 2, 3];
+let b:B, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: B = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> B")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:B, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: B = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> B")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:B, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: B = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> B")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:B, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: B = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> B")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:B, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: B = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> B")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v : B, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: B = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> N")
+          {
+            SECTION("(R) -> N")
+            {
+              std::string_view data = R"(
+let t:(R), t =  1.2;
+let b:N, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: N = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> N")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:N, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: N = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> N")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:N, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: N = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> N")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:N, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: N = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> N")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:N, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: N = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> N")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:N, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: N = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> N")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:N, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: N = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> N")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:N, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: N = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> N")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v : N, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: N = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> Z")
+          {
+            SECTION("(R) -> Z")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1.2;
+let b:Z, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: Z = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> Z")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:Z, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: Z = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> Z")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:Z, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: Z = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> Z")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:Z, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: Z = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> Z")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:Z, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: Z = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> Z")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:Z, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: Z = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> Z")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:Z, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: Z = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> Z")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:Z, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: Z = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> Z")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v : Z, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: Z = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R")
+          {
+            SECTION("(R^1) -> R")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:R, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> R")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:R, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> R")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:R, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> R")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:R, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> R")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:R, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> R")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:R, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> R")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:R, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> R")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v : R, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^1")
+          {
+            SECTION("(R^2) -> R^1")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:R^1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> R^1")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:R^1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> R^1")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:R^1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> R^1")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:R^1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> R^1")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:R^1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> R^1")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:R^1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> R^1")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v : R^1, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^1 = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^2")
+          {
+            SECTION("(N) -> R^2")
+            {
+              std::string_view data = R"(
+let n:(N), n = 1;
+let b:R^2, b = n;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = (N)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R) -> R^2")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1.2;
+let b:R^2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> R^2")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:R^2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> R^2")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:R^2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> R^2")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:R^2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> R^2")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:R^2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> R^2")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:R^2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> R^2")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:R^2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> R^2")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v : R^2, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^2 = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^3")
+          {
+            SECTION("(N) -> R^3")
+            {
+              std::string_view data = R"(
+let n:(N), n = 1;
+let b:R^3, b = n;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = (N)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R) -> R^3")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1.2;
+let b:R^3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> R^3")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:R^3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> R^3")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:R^3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> R^3")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:R^3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> R^3")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:R^3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> R^3")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:R^3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> R^3")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:R^3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> R^3")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v : R^3, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^3 = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^1x1")
+          {
+            SECTION("(R^1) -> R^1x1")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:R^1x1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> R^1x1")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:R^1x1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> R^1x1")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:R^1x1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> R^1x1")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:R^1x1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> R^1x1")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:R^1x1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> R^1x1")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:R^1x1, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> R^1x1")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v : R^1x1, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^1x1 = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^2x2")
+          {
+            SECTION("(N) -> R^2x2")
+            {
+              std::string_view data = R"(
+let n:(N), n = 1;
+let b:R^2x2, b = n;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = (N)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R) -> R^2x2")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1.2;
+let b:R^2x2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> R^2x2")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:R^2x2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> R^2x2")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:R^2x2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> R^2x2")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:R^2x2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> R^2x2")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:R^2x2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> R^2x2")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:R^2x2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> R^2x2")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:R^2x2, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> R^2x2")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v : R^2x2, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^2x2 = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> R^3x3")
+          {
+            SECTION("(N) -> R^3x3")
+            {
+              std::string_view data = R"(
+let n:(N), n = 1;
+let b:R^3x3, b = n;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = (N)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R) -> R^3x3")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1.2;
+let b:R^3x3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> R^3x3")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:R^3x3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> R^3x3")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:R^3x3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> R^3x3")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:R^3x3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> R^3x3")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:R^3x3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> R^3x3")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:R^3x3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> R^3x3")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:R^3x3, b = t;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> R^3x3")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v : R^3x3, v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: R^3x3 = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+        }
+      }
+
+      SECTION("-> tuple")
+      {
+        SECTION("value -> tuple")
+        {
+          SECTION("-> (B)")
+          {
+            SECTION("N -> (B)")
+            {
+              std::string_view data = R"(
+let n:N, n = 1;
+let b:(B), b = n;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = N";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("Z -> (B)")
+            {
+              std::string_view data = R"(
+let b:(B), b = 1;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = Z";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R -> (B)")
+            {
+              std::string_view data = R"(
+let b:(B), b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> (B)")
+            {
+              std::string_view data = R"(
+let b:(B), b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: (B) = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> (B)")
+            {
+              std::string_view data = R"(
+let b:(B), b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: (B) = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> (B)")
+            {
+              std::string_view data = R"(
+let b:(B), b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: (B) = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> (B)")
+            {
+              std::string_view data = R"(
+let b:(B), b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: (B) = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> (B)")
+            {
+              std::string_view data = R"(
+let b:(B), b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: (B) = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> (B)")
+            {
+              std::string_view data = R"(
+let b:(B), b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: (B) = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> (B)")
+            {
+              std::string_view data = R"(
+let b:(B), b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: (B) = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> (B)")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v:(B), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (N)")
+          {
+            SECTION("R -> (N)")
+            {
+              std::string_view data = R"(
+let b:(N), b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: (N) = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> (N)")
+            {
+              std::string_view data = R"(
+let b:(N), b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: (N) = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> (N)")
+            {
+              std::string_view data = R"(
+let b:(N), b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: (N) = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> (N)")
+            {
+              std::string_view data = R"(
+let b:(N), b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: (N) = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> (N)")
+            {
+              std::string_view data = R"(
+let b:(N), b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: (N) = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> (N)")
+            {
+              std::string_view data = R"(
+let b:(N), b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: (N) = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> (N)")
+            {
+              std::string_view data = R"(
+let b:(N), b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: (N) = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> (N)")
+            {
+              std::string_view data = R"(
+let b:(N), b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: (N) = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> (N)")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : (N), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (N) = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (Z)")
+          {
+            SECTION("R -> (Z)")
+            {
+              std::string_view data = R"(
+let b:(Z), b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> (Z)")
+            {
+              std::string_view data = R"(
+let b:(Z), b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> (Z)")
+            {
+              std::string_view data = R"(
+let b:(Z), b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> (Z)")
+            {
+              std::string_view data = R"(
+let b:(Z), b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> (Z)")
+            {
+              std::string_view data = R"(
+let b:(Z), b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> (Z)")
+            {
+              std::string_view data = R"(
+let b:(Z), b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> (Z)")
+            {
+              std::string_view data = R"(
+let b:(Z), b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> (Z)")
+            {
+              std::string_view data = R"(
+let b:(Z), b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> (Z)")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : (Z), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R)")
+          {
+            SECTION("R^1 -> (R)")
+            {
+              std::string_view data = R"(
+let b:(R), b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: (R) = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> (R)")
+            {
+              std::string_view data = R"(
+let b:(R), b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: (R) = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> (R)")
+            {
+              std::string_view data = R"(
+let b:(R), b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: (R) = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> (R)")
+            {
+              std::string_view data = R"(
+let b:(R), b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: (R) = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> (R)")
+            {
+              std::string_view data = R"(
+let b:(R), b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: (R) = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> (R)")
+            {
+              std::string_view data = R"(
+let b:(R), b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: (R) = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> (R)")
+            {
+              std::string_view data = R"(
+let b:(R), b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: (R) = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> (R)")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v :(R), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (R) = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R^1)")
+          {
+            SECTION("R^2 -> (R^1)")
+            {
+              std::string_view data = R"(
+let b:(R^1), b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> (R^1)")
+            {
+              std::string_view data = R"(
+let b:(R^1), b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> (R^1)")
+            {
+              std::string_view data = R"(
+let b:(R^1), b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> (R^1)")
+            {
+              std::string_view data = R"(
+let b:(R^1), b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> (R^1)")
+            {
+              std::string_view data = R"(
+let b:(R^1), b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> (R^1)")
+            {
+              std::string_view data = R"(
+let b:(R^1), b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> (R^1)")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : (R^1), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R^2)")
+          {
+            SECTION("N -> (R^2)")
+            {
+              std::string_view data = R"(
+let n:N, n = 1;
+let b:(R^2), b = n;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = N";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R -> (R^2)")
+            {
+              std::string_view data = R"(
+let b:(R^2), b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> (R^2)")
+            {
+              std::string_view data = R"(
+let b:(R^2), b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> (R^2)")
+            {
+              std::string_view data = R"(
+let b:(R^2), b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> (R^2)")
+            {
+              std::string_view data = R"(
+let b:(R^2), b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> (R^2)")
+            {
+              std::string_view data = R"(
+let b:(R^2), b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> (R^2)")
+            {
+              std::string_view data = R"(
+let b:(R^2), b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> (R^2)")
+            {
+              std::string_view data = R"(
+let b:(R^2), b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> (R^2)")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : (R^2), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R^3)")
+          {
+            SECTION("N -> (R^3)")
+            {
+              std::string_view data = R"(
+let n:N, n = 1;
+let b:(R^3), b = n;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = N";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R -> (R^3)")
+            {
+              std::string_view data = R"(
+let b:(R^3), b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> (R^3)")
+            {
+              std::string_view data = R"(
+let b:(R^3), b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> (R^3)")
+            {
+              std::string_view data = R"(
+let b:(R^3), b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> (R^3)")
+            {
+              std::string_view data = R"(
+let b:(R^3), b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> (R^3)")
+            {
+              std::string_view data = R"(
+let b:(R^3), b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> (R^3)")
+            {
+              std::string_view data = R"(
+let b:(R^3), b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> (R^3)")
+            {
+              std::string_view data = R"(
+let b:(R^3), b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> (R^3)")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : (R^3), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R^1x1)")
+          {
+            SECTION("R^1 -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let b:(R^1x1), b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: (R^1x1) = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let b:(R^1x1), b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: (R^1x1) = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let b:(R^1x1), b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: (R^1x1) = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let b:(R^1x1), b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^1x1) = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let b:(R^1x1), b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^1x1) = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let b:(R^1x1), b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: (R^1x1) = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : (R^1x1), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (R^1x1) = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R^2x2)")
+          {
+            SECTION("N -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let n:N, n = 1;
+let b:(R^2x2), b = n;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2x2) = N";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let b:(R^2x2), b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2x2) = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let b:(R^2x2), b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: (R^2x2) = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let b:(R^2x2), b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: (R^2x2) = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let b:(R^2x2), b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: (R^2x2) = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let b:(R^2x2), b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^2x2) = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3x3 -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let b:(R^2x2), b = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^2x2) = R^3x3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let b:(R^2x2), b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: (R^2x2) = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : (R^2x2), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2x2) = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R^3x3)")
+          {
+            SECTION("N -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let n:N, n = 1;
+let b:(R^3x3), b = n;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3x3) = N";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let b:(R^3x3), b = 1.2;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3x3) = R";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1 -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let b:(R^3x3), b = [1.2];
+)";
+
+              std::string error_message = "undefined affectation type: (R^3x3) = R^1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2 -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let b:(R^3x3), b = [1.2, 3];
+)";
+
+              std::string error_message = "undefined affectation type: (R^3x3) = R^2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^3 -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let b:(R^3x3), b = [1.2, 3, 1];
+)";
+
+              std::string error_message = "undefined affectation type: (R^3x3) = R^3";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^1x1 -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let b:(R^3x3), b = [[1.2]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^3x3) = R^1x1";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("R^2x2 -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let b:(R^3x3), b = [[1.2, 3],[3,4]];
+)";
+
+              std::string error_message = "undefined affectation type: (R^3x3) = R^2x2";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("string -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let b:(R^3x3), b = "foo";
+)";
+
+              std::string error_message = "undefined affectation type: (R^3x3) = string";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("builtin_t -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let bt:builtin_t, bt = a;
+let v : (R^3x3), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3x3) = builtin_t";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+        }
+
+        SECTION("tuple -> tuple")
+        {
+          SECTION("-> (B)")
+          {
+            SECTION("(N) -> (B)")
+            {
+              std::string_view data = R"(
+let t:(N), t = 1;
+let b:(B), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = (N)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(Z) -> (B)")
+            {
+              std::string_view data = R"(
+let t:(Z), t = 1;
+let b:(B), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = (Z)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R) -> (B)")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1;
+let b:(B), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> (B)")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = 1;
+let b:(B), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> (B)")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:(B), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> (B)")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1, 2, 3];
+let b:(B), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> (B)")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:(B), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> (B)")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:(B), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> (B)")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:(B), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> (B)")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:(B), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> (B)")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v :(B), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (B) = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (N)")
+          {
+            SECTION("(R) -> (N)")
+            {
+              std::string_view data = R"(
+let t:(R), t =  1.2;
+let b:(N), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (N) = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> (N)")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:(N), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (N) = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> (N)")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:(N), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (N) = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> (N)")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:(N), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (N) = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> (N)")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:(N), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (N) = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> (N)")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:(N), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (N) = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> (N)")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:(N), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (N) = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> (N)")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:(N), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (N) = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> (N)")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v :(N), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (N) = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (Z)")
+          {
+            SECTION("(R) -> (Z)")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1.2;
+let b:(Z), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> (Z)")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:(Z), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> (Z)")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:(Z), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> (Z)")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:(Z), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> (Z)")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:(Z), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> (Z)")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:(Z), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> (Z)")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:(Z), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> (Z)")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:(Z), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> (Z)")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v :(Z), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (Z) = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R)")
+          {
+            SECTION("(R^1) -> (R)")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:(R), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R) = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> (R)")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:(R), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R) = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> (R)")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:(R), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R) = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> (R)")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:(R), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R) = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> (R)")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:(R), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R) = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> (R)")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:(R), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R) = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> (R)")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:(R), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R) = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> (R)")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v :(R), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (R) = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R^1)")
+          {
+            SECTION("(R^2) -> (R^1)")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:(R^1), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> (R^1)")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:(R^1), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> (R^1)")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:(R^1), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> (R^1)")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:(R^1), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> (R^1)")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:(R^1), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> (R^1)")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:(R^1), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> (R^1)")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v :(R^1), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (R^1) = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R^2)")
+          {
+            SECTION("(N) -> (R^2)")
+            {
+              std::string_view data = R"(
+let n:(N), n = 1;
+let b:(R^2), b = n;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = (N)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R) -> (R^2)")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1.2;
+let b:(R^2), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> (R^2)")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:(R^2), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3) -> (R^2)")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:(R^2), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = (R^3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> (R^2)")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:(R^2), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> (R^2)")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:(R^2), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> (R^2)")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:(R^2), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> (R^2)")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:(R^2), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> (R^2)")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v : (R^2), v = bt;
+)";
+
+              std::string error_message = "undefined affectation type: (R^2) = (builtin_t)";
+
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R^3)")
+          {
+            SECTION("(N) -> (R^3)")
+            {
+              std::string_view data = R"(
+let n:(N), n = 1;
+let b:(R^3), b = n;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = (N)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R) -> (R^3)")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1.2;
+let b:(R^3), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = (R)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1) -> (R^3)")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:(R^3), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> (R^3)")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:(R^3), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = (R^2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^1x1) -> (R^3)")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:(R^3), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = (R^1x1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> (R^3)")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:(R^3), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = (R^2x2)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^3x3) -> (R^3)")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:(R^3), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = (R^3x3)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(string) -> (R^3)")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:(R^3), b = t;
+)";
+
+              std::string error_message = "undefined affectation type: (R^3) = (string)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(builtin_t) -> (R^3)")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v :(R^3), v = bt;
 )";
 
-      CHECK_AST(data, result);
-    }
+              std::string error_message = "undefined affectation type: (R^3) = (builtin_t)";
 
-    SECTION("R /= R")
-    {
-      std::string_view data = R"(
-let x : R, x=1; x/=2.3;
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+
+          SECTION("-> (R^1x1)")
+          {
+            SECTION("(R^1) -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:(R^1x1), b = t;
 )";
 
-      std::string_view result = R"(
-(root:ASTNodeListProcessor)
- +-(language::eq_op:AffectationProcessor<language::eq_op, double, long>)
- |   +-(language::name:x:NameProcessor)
- |   `-(language::integer:1:ValueProcessor)
- `-(language::divideeq_op:AffectationProcessor<language::divideeq_op, double, double>)
-     +-(language::name:x:NameProcessor)
-     `-(language::real:2.3:ValueProcessor)
+              std::string error_message = "undefined affectation type: (R^1x1) = (R^1)";
+
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2) -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:(R^1x1), b = t;
 )";
 
-      CHECK_AST(data, result);
-    }
-  }
+              std::string error_message = "undefined affectation type: (R^1x1) = (R^2)";
 
-  SECTION("Errors")
-  {
-    SECTION("Invalid affectation operator")
-    {
-      auto ast         = std::make_unique<ASTNode>();
-      ast->m_data_type = ASTNodeDataType::build<ASTNodeDataType::void_t>();
-      {
-        auto child_0         = std::make_unique<ASTNode>();
-        child_0->m_data_type = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
-        auto child_1         = std::make_unique<ASTNode>();
-        child_1->m_data_type = ASTNodeDataType::build<ASTNodeDataType::bool_t>();
-        ast->children.emplace_back(std::move(child_0));
-        ast->children.emplace_back(std::move(child_1));
-      }
-      REQUIRE_THROWS_WITH(ASTNodeAffectationExpressionBuilder{*ast},
-                          "unexpected error: undefined affectation operator");
-    }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-    SECTION("Invalid string rhs")
-    {
-      auto ast = std::make_unique<ASTNode>();
-      ast->set_type<language::eq_op>();
+            SECTION("(R^3) -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:(R^1x1), b = t;
+)";
 
-      ast->children.emplace_back(std::make_unique<ASTNode>());
-      ast->children[0]->m_data_type = ASTNodeDataType::build<ASTNodeDataType::string_t>();
-      ast->children.emplace_back(std::make_unique<ASTNode>());
-      REQUIRE_THROWS_WITH(ASTNodeAffectationExpressionBuilder{*ast}, "undefined affectation type: string = undefined");
-    }
+              std::string error_message = "undefined affectation type: (R^1x1) = (R^3)";
 
-    SECTION("Invalid string affectation operator")
-    {
-      SECTION("string -= string")
-      {
-        std::string_view data = R"(
-let s : string, s="foo"; s-="bar";
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
+
+            SECTION("(R^2x2) -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:(R^1x1), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: string -= string";
+              std::string error_message = "undefined affectation type: (R^1x1) = (R^2x2)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-      SECTION("string *= Z")
-      {
-        std::string_view data = R"(
-let s : string, s="foo"; s*=2;
+            SECTION("(R^3x3) -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:(R^1x1), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: string *= Z";
+              std::string error_message = "undefined affectation type: (R^1x1) = (R^3x3)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-      SECTION("string /= string")
-      {
-        std::string_view data = R"(
- let s : string, s="foo"; s/="bar";
+            SECTION("(string) -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:(R^1x1), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: string /= string";
+              std::string error_message = "undefined affectation type: (R^1x1) = (string)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
-    }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-    SECTION("type_id operator")
-    {
-      std::string_view data = R"(
- let s :builtin_t, s = a; s *= b;
+            SECTION("(builtin_t) -> (R^1x1)")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v :(R^1x1), v = bt;
 )";
 
-      std::string error_message = "undefined affectation type: builtin_t *= builtin_t";
+              std::string error_message = "undefined affectation type: (R^1x1) = (builtin_t)";
 
-      CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
-    }
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
 
-    SECTION("Invalid tuple operator")
-    {
-      std::string_view data = R"(
- let s :(R), s=(1,2,3); s *= 4;
+          SECTION("-> (R^2x2)")
+          {
+            SECTION("(N) -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let n:(N), n = 1;
+let b:(R^2x2), b = n;
 )";
 
-      std::string error_message = "undefined affectation type: (R) *= Z";
+              std::string error_message = "undefined affectation type: (R^2x2) = (N)";
 
-      CHECK_AST_THROWS_WITH(data, error_message);
-    }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-    SECTION("Invalid tuple operator 2")
-    {
-      std::string_view data = R"(
- let s : (builtin_t), s =(a,b); s *= b;
+            SECTION("(R) -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1.2;
+let b:(R^2x2), b = t;
 )";
 
-      std::string error_message = "undefined affectation type: (builtin_t) *= builtin_t";
+              std::string error_message = "undefined affectation type: (R^2x2) = (R)";
 
-      CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
-    }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-    SECTION("Invalid R^n -> R^m affectation")
-    {
-      SECTION("R^3 <- R^1")
-      {
-        std::string_view data = R"(
-let x : R^3; let y : R^1; x = y;
+            SECTION("(R^1) -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:(R^2x2), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^3 = R^1";
+              std::string error_message = "undefined affectation type: (R^2x2) = (R^1)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-      SECTION("R^3 <- R^2")
-      {
-        std::string_view data = R"(
-let x : R^3; let y : R^2; x = y;
+            SECTION("(R^2) -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:(R^2x2), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^3 = R^2";
+              std::string error_message = "undefined affectation type: (R^2x2) = (R^2)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-      SECTION("R^2 <- R^1")
-      {
-        std::string_view data = R"(
-let x : R^2; let y : R^1; x = y;
+            SECTION("(R^3) -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:(R^2x2), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^2 = R^1";
+              std::string error_message = "undefined affectation type: (R^2x2) = (R^3)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-      SECTION("R^2 <- R^3")
-      {
-        std::string_view data = R"(
-let x : R^2; let y : R^3; x = y;
+            SECTION("(R^1x1) -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:(R^2x2), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^2 = R^3";
+              std::string error_message = "undefined affectation type: (R^2x2) = (R^1x1)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-      SECTION("R^1 <- R^2")
-      {
-        std::string_view data = R"(
-let x : R^1; let y : R^2; x = y;
+            SECTION("(R^3x3) -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let t:(R^3x3), t = [[1.2, 3, 1],[1,2,3],[4,2,1]];
+let b:(R^2x2), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^1 = R^2";
+              std::string error_message = "undefined affectation type: (R^2x2) = (R^3x3)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-      SECTION("R^1 <- R^3")
-      {
-        std::string_view data = R"(
-let x : R^1; let y : R^3; x = y;
+            SECTION("(string) -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:(R^2x2), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^1 = R^3";
+              std::string error_message = "undefined affectation type: (R^2x2) = (string)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
-    }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-    SECTION("Invalid Z -> R^m affectation [non-zero]")
-    {
-      SECTION("R^3 <- Z")
-      {
-        std::string_view data = R"(
-let x : R^3, x = 3;
+            SECTION("(builtin_t) -> (R^2x2)")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v :(R^2x2), v = bt;
 )";
 
-        std::string error_message = "invalid integral value (0 is the solely valid value)";
+              std::string error_message = "undefined affectation type: (R^2x2) = (builtin_t)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
 
-      SECTION("R^2 <- Z")
-      {
-        std::string_view data = R"(
-let x : R^2, x = 2;
+          SECTION("-> (R^3x3)")
+          {
+            SECTION("(N) -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let n:(N), n = 1;
+let b:(R^3x3), b = n;
 )";
 
-        std::string error_message = "invalid integral value (0 is the solely valid value)";
+              std::string error_message = "undefined affectation type: (R^3x3) = (N)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
-    }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-    SECTION("Invalid R^d -> R^d affectation operator")
-    {
-      SECTION("R^3 <- R^3")
-      {
-        std::string_view data = R"(
-let x : R^3; let y : R^3; x /= y;
+            SECTION("(R) -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let t:(R), t = 1.2;
+let b:(R^3x3), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^3 /= R^3";
+              std::string error_message = "undefined affectation type: (R^3x3) = (R)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-      SECTION("R^2 <- R^2")
-      {
-        std::string_view data = R"(
-let x : R^2; let y : R^2; x /= y;
+            SECTION("(R^1) -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let t:(R^1), t = [1.2];
+let b:(R^3x3), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^2 /= R^2";
+              std::string error_message = "undefined affectation type: (R^3x3) = (R^1)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-      SECTION("R^1 <- R^1")
-      {
-        std::string_view data = R"(
-let x : R^1; let y : R^1; x /= y;
+            SECTION("(R^2) -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let t:(R^2), t = [1.2, 3];
+let b:(R^3x3), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^1 /= R^1";
+              std::string error_message = "undefined affectation type: (R^3x3) = (R^2)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
-    }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-    SECTION("Invalid R^d -> R^d *= operand")
-    {
-      SECTION("R^3 <- R^3")
-      {
-        std::string_view data = R"(
-let x : R^3; let y : R^3; x *= y;
+            SECTION("(R^3) -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let t:(R^3), t = [1.2, 3, 1];
+let b:(R^3x3), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^3 *= R^3";
+              std::string error_message = "undefined affectation type: (R^3x3) = (R^3)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-      SECTION("R^2 <- R^2")
-      {
-        std::string_view data = R"(
-let x : R^2; let y : R^2; x *= y;
+            SECTION("(R^1x1) -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let t:(R^1x1), t = [[1.2]];
+let b:(R^3x3), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^2 *= R^2";
+              std::string error_message = "undefined affectation type: (R^3x3) = (R^1x1)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-      SECTION("R^1 <- R^1")
-      {
-        std::string_view data = R"(
-let x : R^1; let y : R^1; x *= y;
+            SECTION("(R^2x2) -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let t:(R^2x2), t = [[1.2, 3],[3,4]];
+let b:(R^3x3), b = t;
 )";
 
-        std::string error_message = "undefined affectation type: R^1 *= R^1";
+              std::string error_message = "undefined affectation type: (R^3x3) = (R^2x2)";
 
-        CHECK_AST_THROWS_WITH(data, error_message);
-      }
-    }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-    SECTION("incorrect declarative/definition number of symbols")
-    {
-      std::string_view data = R"(
-let (x,y,z):R*R*R, (x,y) = (2,3);
+            SECTION("(string) -> (R^3x3)")
+            {
+              std::string_view data = R"(
+let t:(string), t = "foo";
+let b:(R^3x3), b = t;
 )";
 
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-      auto ast = ASTBuilder::build(input);
-      OperatorRepository::instance().reset();
-      ASTModulesImporter{*ast};
+              std::string error_message = "undefined affectation type: (R^3x3) = (string)";
 
-      ASTSymbolTableBuilder{*ast};
-      REQUIRE_THROWS_WITH(ASTSymbolInitializationChecker{*ast},
-                          std::string{"invalid number of definition identifiers, expecting 3 found 2"});
-    }
+              CHECK_AST_THROWS_WITH(data, error_message);
+            }
 
-    SECTION("incorrect identifier/expression number of symbols")
-    {
-      std::string_view data = R"(
-let y:R;
-let x:R, (x,y) = (2,3);
+            SECTION("(builtin_t) -> (R^3)")
+            {
+              std::string_view data = R"(
+let bt:(builtin_t), bt = a;
+let v :(R^3x3), v = bt;
 )";
 
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-      auto ast = ASTBuilder::build(input);
-      OperatorRepository::instance().reset();
-      ASTModulesImporter{*ast};
+              std::string error_message = "undefined affectation type: (R^3x3) = (builtin_t)";
 
-      ASTSymbolTableBuilder{*ast};
-      REQUIRE_THROWS_WITH(ASTSymbolInitializationChecker{*ast},
-                          std::string{"unexpected variable list, expecting one identifier"});
+              CHECK_AST_WITH_BUILTIN_THROWS_WITH(data, error_message);
+            }
+          }
+        }
+      }
     }
 
-    SECTION("incorrect definition variable identifier")
+    SECTION("void as a type")
     {
-      std::string_view data = R"(
-let y:R;
-let x:R, y = 3;
+      SECTION("declaration")
+      {
+        std::string_view data     = R"(
+let a:void;
 )";
+        std::string error_message = "'void' keyword does not define a type";
 
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-      auto ast = ASTBuilder::build(input);
-      OperatorRepository::instance().reset();
-      ASTModulesImporter{*ast};
+        {
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          auto ast = ASTBuilder::build(input);
+          OperatorRepository::instance().reset();
+          ASTModulesImporter{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      REQUIRE_THROWS_WITH(ASTSymbolInitializationChecker{*ast}, std::string{"invalid identifier, expecting 'x'"});
-    }
+          ASTSymbolTableBuilder{*ast};
+          REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, error_message);
+        }
+      }
 
-    SECTION("invalid definition variable identifier order")
-    {
-      std::string_view data = R"(
-let (x,y):R, (y,x) = (3,2);
+      SECTION("definition")
+      {
+        std::string_view data = R"(
+let a:void, a = 3;
 )";
 
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-      auto ast = ASTBuilder::build(input);
-      OperatorRepository::instance().reset();
-      ASTModulesImporter{*ast};
+        std::string error_message = "'void' keyword does not define a type";
 
-      ASTSymbolTableBuilder{*ast};
-      REQUIRE_THROWS_WITH(ASTSymbolInitializationChecker{*ast}, std::string{"invalid identifier, expecting 'x'"});
+        {
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          auto ast = ASTBuilder::build(input);
+          OperatorRepository::instance().reset();
+          ASTModulesImporter{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, error_message);
+        }
+      }
     }
   }
 }
+
+#ifdef __clang__
+#pragma clang optimize on
+#endif   // __clang__
diff --git a/tests/test_ASTNodeDataTypeBuilder.cpp b/tests/test_ASTNodeDataTypeBuilder.cpp
index 5c449d7abf9bc9f083bd7c8206d377360f0f229c..2a5777b32272c8c10ba39543c30b103d4f629439 100644
--- a/tests/test_ASTNodeDataTypeBuilder.cpp
+++ b/tests/test_ASTNodeDataTypeBuilder.cpp
@@ -414,7 +414,7 @@ let square : R -> R^3, x -> (x, 2);
         ASTSymbolTableBuilder{*ast};
 
         REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast},
-                            "expecting 3 scalar expressions or an R^3, found 2 scalar expressions");
+                            "number of image spaces (1) R^3 differs from number of expressions (2) (x, 2)");
       }
     }
   }
@@ -1126,7 +1126,7 @@ let f : R -> R^2x2, x -> (x, 2*x, 2);
         ASTSymbolTableBuilder{*ast};
 
         REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast},
-                            "expecting 4 scalar expressions or an R^2x2, found 3 scalar expressions");
+                            "number of image spaces (1) R^2x2 differs from number of expressions (3) (x, 2*x, 2)");
       }
 
       SECTION("undefined type identifier")
@@ -1180,6 +1180,30 @@ let f: R -> X, x -> x;
         REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast},
                             "invalid type identifier, 'X' was previously defined as a 'R'");
       }
+
+      SECTION("invalid tuple type domain")
+      {
+        std::string_view data = R"(
+let f: (R) -> R, x -> x;
+)";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+        auto ast = ASTBuilder::build(input);
+        ASTSymbolTableBuilder{*ast};
+
+        REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "cannot use tuple (R) as a domain for user functions");
+      }
+
+      SECTION("invalid tuple type in compound domain")
+      {
+        std::string_view data = R"(
+let f: N*(R^2) -> R, (x,t) -> x;
+)";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+        auto ast = ASTBuilder::build(input);
+        ASTSymbolTableBuilder{*ast};
+
+        REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, "cannot use tuple (R^2) as a domain for user functions");
+      }
     }
   }
 
diff --git a/tests/test_ASTNodeFunctionExpressionBuilder.cpp b/tests/test_ASTNodeFunctionExpressionBuilder.cpp
index 762202a671b1f7708b193ca8cd6c7aa599bfc780..7d760d2224fe4bbfb836b409a035830efd18b6fa 100644
--- a/tests/test_ASTNodeFunctionExpressionBuilder.cpp
+++ b/tests/test_ASTNodeFunctionExpressionBuilder.cpp
@@ -115,107 +115,111 @@ const auto builtin_data_type = ast_node_data_type_from<std::shared_ptr<const dou
 
 TEST_CASE("ASTNodeFunctionExpressionBuilder", "[language]")
 {
-  SECTION("return a B")
+  SECTION("value or compounds of values")
   {
-    SECTION("B argument")
+    SECTION("without conversion")
     {
-      SECTION("B parameter")
+      SECTION("return a B")
       {
-        std::string_view data = R"(
+        SECTION("B argument")
+        {
+          SECTION("B parameter")
+          {
+            std::string_view data = R"(
 let not_v : B -> B, a -> not a;
 not_v(true);
 )";
 
-        std::string_view result = R"(
+            std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:not_v:NameProcessor)
      `-(language::true_kw:ValueProcessor)
 )";
 
-        CHECK_AST(data, result);
-      }
-    }
+            CHECK_AST(data, result);
+          }
+        }
 
-    SECTION("N argument")
-    {
-      std::string_view data = R"(
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
 let test : N -> B, n -> n<10;
 test(2);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:test:NameProcessor)
      `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("Z argument")
-    {
-      std::string_view data = R"(
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
 let test : Z -> B, z -> z>3;
 test(2);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:test:NameProcessor)
      `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R argument")
-    {
-      std::string_view data = R"(
+        SECTION("R argument")
+        {
+          std::string_view data = R"(
 let test : R -> B, x -> x>2.3;
 test(2.1);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:test:NameProcessor)
      `-(language::real:2.1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
-  }
+          CHECK_AST(data, result);
+        }
+      }
 
-  SECTION("return a N")
-  {
-    SECTION("N argument")
-    {
-      std::string_view data = R"(
+      SECTION("return a N")
+      {
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
 let test : N -> N, n -> n+2;
 test(2);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:test:NameProcessor)
      `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("Z argument")
-    {
-      std::string_view data = R"(
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
 let absolute : Z -> N, z -> (z>0)*z -(z<=0)*z;
 absolute(-2);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:absolute:NameProcessor)
@@ -223,37 +227,37 @@ absolute(-2);
          `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
-  }
+          CHECK_AST(data, result);
+        }
+      }
 
-  SECTION("return a Z")
-  {
-    SECTION("N argument")
-    {
-      std::string_view data = R"(
+      SECTION("return a Z")
+      {
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
 let minus : N -> Z, n -> -n;
 minus(true);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:minus:NameProcessor)
      `-(language::true_kw:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("Z argument")
-    {
-      std::string_view data = R"(
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
 let times_2 : Z -> Z, z -> z*2;
 times_2(-2);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:times_2:NameProcessor)
@@ -261,20 +265,20 @@ times_2(-2);
          `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
-  }
+          CHECK_AST(data, result);
+        }
+      }
 
-  SECTION("return a string")
-  {
-    SECTION("string argument")
-    {
-      std::string_view data = R"(
+      SECTION("return a string")
+      {
+        SECTION("string argument")
+        {
+          std::string_view data = R"(
 let cat : string*string -> string, (s1,s2) -> s1+s2;
 cat("foo", "bar");
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:cat:NameProcessor)
@@ -283,17 +287,17 @@ cat("foo", "bar");
          `-(language::literal:"bar":ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("B argument conversion")
-    {
-      std::string_view data = R"(
+        SECTION("B argument conversion")
+        {
+          std::string_view data = R"(
 let cat : string*string -> string, (s1,s2) -> s1+s2;
 cat("foo", true);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:cat:NameProcessor)
@@ -302,18 +306,18 @@ cat("foo", true);
          `-(language::true_kw:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("N argument conversion")
-    {
-      std::string_view data = R"(
+        SECTION("N argument conversion")
+        {
+          std::string_view data = R"(
 let cat : string*string -> string, (s1,s2) -> s1+s2;
 let n : N, n = 2;
 cat("foo", n);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:cat:NameProcessor)
@@ -322,17 +326,17 @@ cat("foo", n);
          `-(language::name:n:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("Z argument conversion")
-    {
-      std::string_view data = R"(
+        SECTION("Z argument conversion")
+        {
+          std::string_view data = R"(
 let cat : string*string -> string, (s1,s2) -> s1+s2;
 cat("foo", -1);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:cat:NameProcessor)
@@ -342,17 +346,17 @@ cat("foo", -1);
              `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
 
-    SECTION("R argument conversion")
-    {
-      std::string_view data = R"(
+        SECTION("R argument conversion")
+        {
+          std::string_view data = R"(
 let cat : string*string -> string, (s1,s2) -> s1+s2;
 cat("foo", 2.5e-3);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:cat:NameProcessor)
@@ -361,12 +365,13 @@ cat("foo", 2.5e-3);
          `-(language::real:2.5e-3:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+          CHECK_AST(data, result);
+        }
+      }
 
-    SECTION("Return R^1 -> R^1")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^1")
+      {
+        std::string_view data = R"(
 let f : R^1 -> R^1, x -> x+x;
 let x : R^1, x = 1;
 f(x);
@@ -377,7 +382,7 @@ f(2);
 f(1.4);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  +-(language::function_evaluation:FunctionProcessor)
  |   +-(language::name:f:NameProcessor)
@@ -396,48 +401,48 @@ f(1.4);
      `-(language::real:1.4:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return R^2 -> R^2")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^2")
+      {
+        std::string_view data = R"(
 let f : R^2 -> R^2, x -> x+x;
 let x : R^2, x = (1,2);
 f(x);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::name:x:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return R^3 -> R^3")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^3")
+      {
+        std::string_view data = R"(
 let f : R^3 -> R^3, x -> x+x;
 let x : R^3, x = (1,2,3);
 f(x);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::name:x:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return R^1x1 -> R^1x1")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^1x1")
+      {
+        std::string_view data = R"(
 let f : R^1x1 -> R^1x1, x -> x+x;
 let x : R^1x1, x = 1;
 f(x);
@@ -448,7 +453,7 @@ f(2);
 f(1.4);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  +-(language::function_evaluation:FunctionProcessor)
  |   +-(language::name:f:NameProcessor)
@@ -467,70 +472,70 @@ f(1.4);
      `-(language::real:1.4:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return R^2x2 -> R^2x2")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^2x2")
+      {
+        std::string_view data = R"(
 let f : R^2x2 -> R^2x2, x -> x+x;
 let x : R^2x2, x = (1,2,3,4);
 f(x);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::name:x:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return R^3x3 -> R^3x3")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^3x3")
+      {
+        std::string_view data = R"(
 let f : R^3x3 -> R^3x3, x -> x+x;
 let x : R^3x3, x = (1,2,3,4,5,6,7,8,9);
 f(x);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::name:x:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return scalar -> R^1")
-    {
-      std::string_view data = R"(
-let f : R -> R^1, x -> x+1;
+      SECTION("Return a R^1")
+      {
+        std::string_view data = R"(
+let f : R -> R^1, x -> [x+1];
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return tuple -> R^2")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^2")
+      {
+        std::string_view data = R"(
 let f : R*R -> R^2, (x,y) -> [x,y];
 f(1,2);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
@@ -539,17 +544,17 @@ f(1,2);
          `-(language::integer:2:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return tuple -> R^3")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^3")
+      {
+        std::string_view data = R"(
 let f : R*R*R -> R^3, (x,y,z) -> [x,y,z];
 f(1,2,3);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
@@ -559,34 +564,34 @@ f(1,2,3);
          `-(language::integer:3:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return scalar -> R^1x1")
-    {
-      std::string_view data = R"(
-let f : R -> R^1x1, x -> x+1;
+      SECTION("Return a R^1x1")
+      {
+        std::string_view data = R"(
+let f : R -> R^1x1, x -> [[x+1]];
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return tuple -> R^2x2")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^2x2")
+      {
+        std::string_view data = R"(
 let f : R*R*R*R -> R^2x2, (x,y,z,t) -> [[x,y],[z,t]];
 f(1,2,3,4);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
@@ -597,17 +602,17 @@ f(1,2,3,4);
          `-(language::integer:4:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return tuple -> R^3x3")
-    {
-      std::string_view data = R"(
+      SECTION("Return a R^3x3")
+      {
+        std::string_view data = R"(
 let f : R^3*R^3*R^3 -> R^3x3, (x,y,z) -> [[x[0],x[1],x[2]],[y[0],y[1],y[2]],[z[0],z[1],z[2]]];
 f([1,2,3],[4,5,6],[7,8,9]);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
@@ -626,298 +631,360 @@ f([1,2,3],[4,5,6],[7,8,9]);
              `-(language::integer:9:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return '0' -> R^1")
-    {
-      std::string_view data = R"(
-let f : R -> R^1, x -> 0;
-f(1);
+      SECTION("return a builtin_t")
+      {
+        SECTION("builtin_t argument")
+        {
+          std::string_view data = R"(
+let foo : builtin_t -> builtin_t, b -> b;
+let b0 : builtin_t;
+foo(b0);
 )";
 
-      std::string_view result = R"(
+          std::string_view result = R"(
 (root:ASTNodeListProcessor)
- `-(language::function_evaluation:FunctionExpressionProcessor<TinyVector<1ul, double>, ZeroType>)
-     +-(language::name:f:NameProcessor)
-     `-(language::integer:1:ValueProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:foo:NameProcessor)
+     `-(language::name:b0:NameProcessor)
 )";
 
-      CHECK_AST(data, result);
+          CHECK_AST(data, result);
+        }
+      }
     }
 
-    SECTION("Return '0' -> R^2")
+    SECTION("with return conversion")
     {
-      std::string_view data = R"(
+      SECTION("Return scalar -> R^1")
+      {
+        std::string_view data = R"(
+let f : R -> R^1, x -> x+1;
+f(1);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return scalar -> R^1x1")
+      {
+        std::string_view data = R"(
+let f : R -> R^1x1, x -> x+1;
+f(1);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return '0' -> R^1")
+      {
+        std::string_view data = R"(
+let f : R -> R^1, x -> 0;
+f(1);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionExpressionProcessor<TinyVector<1ul, double>, ZeroType>)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return '0' -> R^2")
+      {
+        std::string_view data = R"(
 let f : R -> R^2, x -> 0;
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionExpressionProcessor<TinyVector<2ul, double>, ZeroType>)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return '0' -> R^3")
-    {
-      std::string_view data = R"(
+      SECTION("Return '0' -> R^3")
+      {
+        std::string_view data = R"(
 let f : R -> R^3, x -> 0;
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionExpressionProcessor<TinyVector<3ul, double>, ZeroType>)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return '0' -> R^1x1")
-    {
-      std::string_view data = R"(
+      SECTION("Return '0' -> R^1x1")
+      {
+        std::string_view data = R"(
 let f : R -> R^1x1, x -> 0;
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionExpressionProcessor<TinyMatrix<1ul, 1ul, double>, ZeroType>)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return '0' -> R^2x2")
-    {
-      std::string_view data = R"(
+      SECTION("Return '0' -> R^2x2")
+      {
+        std::string_view data = R"(
 let f : R -> R^2x2, x -> 0;
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionExpressionProcessor<TinyMatrix<2ul, 2ul, double>, ZeroType>)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return '0' -> R^3x3")
-    {
-      std::string_view data = R"(
+      SECTION("Return '0' -> R^3x3")
+      {
+        std::string_view data = R"(
 let f : R -> R^3x3, x -> 0;
 f(1);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionExpressionProcessor<TinyMatrix<3ul, 3ul, double>, ZeroType>)
      +-(language::name:f:NameProcessor)
      `-(language::integer:1:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return embedded R^d compound")
-    {
-      std::string_view data = R"(
-let f : R*R*R*R -> R*R^1*R^2*R^3, (x,y,z,t) -> (t, [x], [x,y], [x,y,z]);
-f(1,2,3,4);
+      SECTION("Arguments '0' -> R^1")
+      {
+        std::string_view data = R"(
+let f : R^1 -> R^1, x -> x;
+f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
-         +-(language::integer:1:ValueProcessor)
-         +-(language::integer:2:ValueProcessor)
-         +-(language::integer:3:ValueProcessor)
-         `-(language::integer:4:ValueProcessor)
+     `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return embedded R^dxd compound")
-    {
-      std::string_view data = R"(
-let f : R*R*R*R -> R*R^1x1*R^2x2*R^3x3, (x,y,z,t) -> (t, [[x]], [[x,y],[z,t]], [[x,y,z], [x,x,x], [t,t,t]]);
-f(1,2,3,4);
+      SECTION("Arguments '0' -> R^2")
+      {
+        std::string_view data = R"(
+let f : R^2 -> R^2, x -> x;
+f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
-         +-(language::integer:1:ValueProcessor)
-         +-(language::integer:2:ValueProcessor)
-         +-(language::integer:3:ValueProcessor)
-         `-(language::integer:4:ValueProcessor)
+     `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return embedded R^d compound with '0'")
-    {
-      std::string_view data = R"(
-let f : R*R*R*R -> R*R^1*R^2*R^3, (x,y,z,t) -> (t, 0, 0, [x,y,z]);
-f(1,2,3,4);
+      SECTION("Arguments '0' -> R^3")
+      {
+        std::string_view data = R"(
+let f : R^3 -> R^3, x -> x;
+f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
-         +-(language::integer:1:ValueProcessor)
-         +-(language::integer:2:ValueProcessor)
-         +-(language::integer:3:ValueProcessor)
-         `-(language::integer:4:ValueProcessor)
+     `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Return embedded R^dxd compound with '0'")
-    {
-      std::string_view data = R"(
-let f : R*R*R*R -> R*R^1x1*R^2x2*R^3x3, (x,y,z,t) -> (t, 0, 0, [[x, y, z], [t, x, y], [z, t, x]]);
-f(1,2,3,4);
+      SECTION("Arguments '0' -> R^1x1")
+      {
+        std::string_view data = R"(
+let f : R^1x1 -> R^1x1, x -> x;
+f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
-         +-(language::integer:1:ValueProcessor)
-         +-(language::integer:2:ValueProcessor)
-         +-(language::integer:3:ValueProcessor)
-         `-(language::integer:4:ValueProcessor)
+     `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Arguments '0' -> R^1")
-    {
-      std::string_view data = R"(
-let f : R^1 -> R^1, x -> x;
+      SECTION("Arguments '0' -> R^2x2")
+      {
+        std::string_view data = R"(
+let f : R^2x2 -> R^2x2, x -> x;
 f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
-    }
+        CHECK_AST(data, result);
+      }
 
-    SECTION("Arguments '0' -> R^2")
-    {
-      std::string_view data = R"(
-let f : R^2 -> R^2, x -> x;
+      SECTION("Arguments '0' -> R^3x3")
+      {
+        std::string_view data = R"(
+let f : R^3x3 -> R^3x3, x -> x;
 f(0);
 )";
 
-      std::string_view result = R"(
+        std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
      `-(language::integer:0:ValueProcessor)
 )";
 
-      CHECK_AST(data, result);
+        CHECK_AST(data, result);
+      }
     }
+  }
 
-    SECTION("Arguments '0' -> R^3")
+  SECTION("compound return type")
+  {
+    SECTION("Return compound with R^d")
     {
       std::string_view data = R"(
-let f : R^3 -> R^3, x -> x;
-f(0);
+let f : R*R*R*R -> R*R^1*R^2*R^3, (x,y,z,t) -> (t, [x], [x,y], [x,y,z]);
+f(1,2,3,4);
 )";
 
       std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::integer:0:ValueProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
 )";
 
       CHECK_AST(data, result);
     }
 
-    SECTION("Arguments '0' -> R^1x1")
+    SECTION("Return compound with R^dxd")
     {
       std::string_view data = R"(
-let f : R^1x1 -> R^1x1, x -> x;
-f(0);
+let f : R*R*R*R -> R*R^1x1*R^2x2*R^3x3, (x,y,z,t) -> (t, [[x]], [[x,y],[z,t]], [[x,y,z], [x,x,x], [t,t,t]]);
+f(1,2,3,4);
 )";
 
       std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::integer:0:ValueProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
 )";
 
       CHECK_AST(data, result);
     }
 
-    SECTION("Arguments '0' -> R^2x2")
+    SECTION("Return R^d compound with '0'")
     {
       std::string_view data = R"(
-let f : R^2x2 -> R^2x2, x -> x;
-f(0);
+let f : R*R*R*R -> R*R^1*R^2*R^3, (x,y,z,t) -> (t, 0, 0, [x,y,z]);
+f(1,2,3,4);
 )";
 
       std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::integer:0:ValueProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
 )";
 
       CHECK_AST(data, result);
     }
 
-    SECTION("Arguments '0' -> R^3x3")
+    SECTION("Return R^dxd compound with '0'")
     {
       std::string_view data = R"(
-let f : R^3x3 -> R^3x3, x -> x;
-f(0);
+let f : R*R*R*R -> R*R^1x1*R^2x2*R^3x3, (x,y,z,t) -> (t, 0, 0, [[x, y, z], [t, x, y], [z, t, x]]);
+f(1,2,3,4);
 )";
 
       std::string_view result = R"(
 (root:ASTNodeListProcessor)
  `-(language::function_evaluation:FunctionProcessor)
      +-(language::name:f:NameProcessor)
-     `-(language::integer:0:ValueProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
 )";
 
       CHECK_AST(data, result);
     }
 
-    SECTION("Arguments tuple -> R^d")
+    SECTION("Arguments R^d")
     {
       std::string_view data = R"(
 let f: R^3 -> R, x -> x[0]+x[1]+x[2];
@@ -966,7 +1033,7 @@ f([[1,2,3],[4,5,6],[7,8,9]]);
       CHECK_AST(data, result);
     }
 
-    SECTION("Arguments compound with tuple")
+    SECTION("Arguments compound with R^d and R^dxd")
     {
       std::string_view data = R"(
 let f: R*R^3*R^2x2->R, (t,x,y) -> t*(x[0]+x[1]+x[2])*y[0,0]+y[1,1];
@@ -995,480 +1062,3006 @@ f(2,[1,2,3],[[2,3],[-1,1.3]]);
 
       CHECK_AST(data, result);
     }
-  }
 
-  SECTION("return a builtin_t")
-  {
-    SECTION("builtin_t argument")
+    SECTION("errors")
     {
-      std::string_view data = R"(
-let foo : builtin_t -> builtin_t, b -> b;
-let b0 : builtin_t;
-foo(b0);
+      SECTION("wrong argument number")
+      {
+        std::string_view data = R"(
+let Id : Z -> Z, z -> z;
+Id(2,3);
 )";
 
-      std::string_view result = R"(
-(root:ASTNodeListProcessor)
- `-(language::function_evaluation:FunctionProcessor)
-     +-(language::name:foo:NameProcessor)
-     `-(language::name:b0:NameProcessor)
-)";
-
-      CHECK_AST(data, result);
-    }
-  }
-
-  SECTION("errors")
-  {
-    SECTION("wrong argument number")
-    {
-      std::string_view data = R"(
-let Id : Z -> Z, z -> z;
-Id(2,3);
-)";
-
-      CHECK_AST_THROWS(data);
-    }
+        CHECK_AST_THROWS(data);
+      }
 
-    SECTION("wrong argument number 2")
-    {
-      std::string_view data = R"(
+      SECTION("wrong argument number 2")
+      {
+        std::string_view data = R"(
 let sum : R*R -> R, (x,y) -> x+y;
 sum(2);
 )";
 
-      CHECK_AST_THROWS(data);
-    }
+        CHECK_AST_THROWS(data);
+      }
 
-    SECTION("invalid return implicit conversion")
-    {
-      SECTION("string -> R")
+      SECTION("invalid return implicit conversion")
       {
-        std::string_view data = R"(
+        SECTION("string -> R")
+        {
+          std::string_view data = R"(
 let bad_conv : string -> R, s -> s;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
-      }
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
+        }
 
-      SECTION("R -> B")
-      {
-        std::string_view data = R"(
+        SECTION("R -> B")
+        {
+          std::string_view data = R"(
 let bad_B : R -> B, x -> x;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
-      }
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
 
-      SECTION("R -> N")
-      {
-        std::string_view data = R"(
+        SECTION("R -> N")
+        {
+          std::string_view data = R"(
 let next : R -> N, x -> x;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
-      }
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
 
-      SECTION("R -> Z")
-      {
-        std::string_view data = R"(
+        SECTION("R -> Z")
+        {
+          std::string_view data = R"(
 let prev : R -> Z, x -> x;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
-      }
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
 
-      SECTION("N -> B")
-      {
-        std::string_view data = R"(
+        SECTION("N -> B")
+        {
+          std::string_view data = R"(
 let bad_B : N -> B, n -> n;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
-      }
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
 
-      SECTION("Z -> B")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> B")
+        {
+          std::string_view data = R"(
 let bad_B : Z -> B, n -> n;
 )";
 
-        CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
       }
-    }
 
-    SECTION("invalid argument implicit conversion")
-    {
-      SECTION("N -> B")
+      SECTION("invalid argument implicit conversion")
       {
-        std::string_view data = R"(
+        SECTION("N -> B")
+        {
+          std::string_view data = R"(
 let negate : B -> B, b -> not b;
 let n : N, n = 2;
 negate(n);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
 
-      SECTION("Z -> B")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> B")
+        {
+          std::string_view data = R"(
 let negate : B -> B, b -> not b;
 negate(3-4);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
 
-      SECTION("R -> B")
-      {
-        std::string_view data = R"(
+        SECTION("R -> B")
+        {
+          std::string_view data = R"(
 let negate : B -> B, b -> not b;
 negate(3.24);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
 
-      SECTION("R -> N")
-      {
-        std::string_view data = R"(
+        SECTION("R -> N")
+        {
+          std::string_view data = R"(
 let next : N -> N, n -> n+1;
 next(3.24);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
 
-      SECTION("R -> Z")
-      {
-        std::string_view data = R"(
+        SECTION("R -> Z")
+        {
+          std::string_view data = R"(
 let prev : Z -> Z, z -> z-1;
 prev(3 + .24);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
 
-      SECTION("B -> R^2")
-      {
-        std::string_view data = R"(
+        SECTION("B -> R^2")
+        {
+          std::string_view data = R"(
 let f : R^2 -> R^2, x -> x;
 f(true);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2"});
+        }
 
-      SECTION("N -> R^2")
-      {
-        std::string_view data = R"(
+        SECTION("N -> R^2")
+        {
+          std::string_view data = R"(
 let f : R^2 -> R^2, x -> x;
 let n : N, n = 2;
 f(n);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2"});
+        }
 
-      SECTION("Z -> R^2")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> R^2")
+        {
+          std::string_view data = R"(
 let f : R^2 -> R^2, x -> x;
 f(-2);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2"});
+        }
 
-      SECTION("R -> R^2")
-      {
-        std::string_view data = R"(
+        SECTION("R -> R^2")
+        {
+          std::string_view data = R"(
 let f : R^2 -> R^2, x -> x;
 f(1.3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2"});
+        }
 
-      SECTION("B -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("B -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R^3, x -> x;
 f(true);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3"});
+        }
 
-      SECTION("N -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("N -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R^3, x -> x;
 let n : N, n = 2;
 f(n);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3"});
+        }
 
-      SECTION("Z -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R^3, x -> x;
 f(-2);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3"});
+        }
 
-      SECTION("R -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("R -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R^3, x -> x;
 f(1.3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3"});
+        }
 
-      SECTION("B -> R^2x2")
-      {
-        std::string_view data = R"(
+        SECTION("B -> R^2x2")
+        {
+          std::string_view data = R"(
 let f : R^2x2 -> R^2x2, x -> x;
 f(true);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2x2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2x2"});
+        }
 
-      SECTION("N -> R^2x2")
-      {
-        std::string_view data = R"(
+        SECTION("N -> R^2x2")
+        {
+          std::string_view data = R"(
 let f : R^2x2 -> R^2x2, x -> x;
 let n : N, n = 2;
 f(n);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2x2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2x2"});
+        }
 
-      SECTION("Z -> R^2x2")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> R^2x2")
+        {
+          std::string_view data = R"(
 let f : R^2x2 -> R^2x2, x -> x;
 f(-2);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2x2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2x2"});
+        }
 
-      SECTION("R -> R^2x2")
-      {
-        std::string_view data = R"(
+        SECTION("R -> R^2x2")
+        {
+          std::string_view data = R"(
 let f : R^2x2 -> R^2x2, x -> x;
 f(1.3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2x2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2x2"});
+        }
 
-      SECTION("B -> R^3x3")
-      {
-        std::string_view data = R"(
+        SECTION("B -> R^3x3")
+        {
+          std::string_view data = R"(
 let f : R^3x3 -> R^3x3, x -> x;
 f(true);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3x3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3x3"});
+        }
 
-      SECTION("N -> R^3x3")
-      {
-        std::string_view data = R"(
+        SECTION("N -> R^3x3")
+        {
+          std::string_view data = R"(
 let f : R^3x3 -> R^3x3, x -> x;
 let n : N, n = 2;
 f(n);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3x3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3x3"});
+        }
 
-      SECTION("Z -> R^3x3")
-      {
-        std::string_view data = R"(
+        SECTION("Z -> R^3x3")
+        {
+          std::string_view data = R"(
 let f : R^3x3 -> R^3x3, x -> x;
 f(-2);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3x3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3x3"});
+        }
 
-      SECTION("R -> R^3x3")
-      {
-        std::string_view data = R"(
+        SECTION("R -> R^3x3")
+        {
+          std::string_view data = R"(
 let f : R^3x3 -> R^3x3, x -> x;
 f(1.3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3x3"});
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3x3"});
+        }
       }
-    }
 
-    SECTION("arguments invalid tuple -> R^d conversion")
-    {
-      SECTION("tuple[2] -> R^2")
+      SECTION("arguments invalid tuple -> R^d conversion")
       {
-        std::string_view data = R"(
+        SECTION("tuple[2] -> R^2")
+        {
+          std::string_view data = R"(
 let f : R^2 -> R, x->x[0];
 f((1,2));
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
+        }
 
-      SECTION("tuple[3] -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("tuple[3] -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R, x->x[0];
 f((1,2,3));
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
+        }
 
-      SECTION("compound tuple[2] -> R^2")
-      {
-        std::string_view data = R"(
+        SECTION("compound -> R^2")
+        {
+          std::string_view data = R"(
 let f : R*R^2 -> R, (t,x)->x[0];
 f(1,(1,2));
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
+        }
 
-      SECTION("compound tuple[2] -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("compound -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3*R^2 -> R, (x,y)->x[0]*y[1];
 f((1,2,3),[3,4]);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
+        }
 
-      SECTION("list instead of tuple -> R^3")
-      {
-        std::string_view data = R"(
+        SECTION("list instead of tuple -> R^3")
+        {
+          std::string_view data = R"(
 let f : R^3 -> R, x -> x[0]*x[1];
 f(1,2,3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 1, provided 3"});
-      }
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 1, provided 3"});
+        }
 
-      SECTION("list instead of tuple -> R^3*R^2")
-      {
-        std::string_view data = R"(
+        SECTION("list instead of tuple -> R^3*R^2")
+        {
+          std::string_view data = R"(
 let f : R^3*R^2 -> R, (x,y) -> x[0]*x[1]-y[0];
 f([1,2,3],2,3);
 )";
 
-        CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 2, provided 3"});
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 2, provided 3"});
+        }
       }
-    }
 
-    SECTION("non pure function")
-    {
-      SECTION("argument modification")
+      SECTION("non pure function")
       {
-        SECTION("++ argument")
+        SECTION("argument modification")
         {
-          std::string_view data = R"(
+          SECTION("++ argument")
+          {
+            std::string_view data = R"(
 let non_pure : N -> N, x -> 3 * ++x;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
-        }
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
 
-        SECTION("argument ++")
-        {
-          std::string_view data = R"(
+          SECTION("argument ++")
+          {
+            std::string_view data = R"(
 let non_pure : N -> N, x -> 1 + x ++;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
-        }
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
 
-        SECTION("-- argument")
-        {
-          std::string_view data = R"(
+          SECTION("-- argument")
+          {
+            std::string_view data = R"(
 let non_pure : Z -> Z, x -> 3 * --x;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
-        }
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
 
-        SECTION("argument --")
-        {
-          std::string_view data = R"(
+          SECTION("argument --")
+          {
+            std::string_view data = R"(
 let non_pure : Z -> Z, x -> 1 + x --;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
         }
-      }
 
-      SECTION("outer variable modification")
-      {
-        SECTION("++ outer variable")
+        SECTION("outer variable modification")
         {
-          std::string_view data = R"(
+          SECTION("++ outer variable")
+          {
+            std::string_view data = R"(
 let a:N, a = 4;
 let non_pure : Z -> Z, x -> x * ++a;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
-        }
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
 
-        SECTION("outer variable ++")
-        {
-          std::string_view data = R"(
+          SECTION("outer variable ++")
+          {
+            std::string_view data = R"(
 let a:N, a = 4;
 let non_pure : N -> N, x -> x + a++;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
-        }
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
 
-        SECTION("-- outer variable")
-        {
-          std::string_view data = R"(
+          SECTION("-- outer variable")
+          {
+            std::string_view data = R"(
 let a:Z, a = 4;
 let non_pure : Z -> Z, x -> x * --a;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
-        }
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
 
-        SECTION("outer variable --")
-        {
-          std::string_view data = R"(
+          SECTION("outer variable --")
+          {
+            std::string_view data = R"(
 let a:Z, a = 4;
 let non_pure : Z -> Z, x -> x + a--;
 )";
 
-          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{
-                                                       "invalid function definition. Function data must be constant!"});
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
         }
       }
     }
   }
+
+  SECTION("tuples")
+  {
+    SECTION("from list of values")
+    {
+      SECTION("return a (B)")
+      {
+        SECTION("B argument")
+        {
+          SECTION("B parameter")
+          {
+            std::string_view data = R"(
+let not_v : B -> (B), a -> (not a, a, true);
+not_v(true);
+)";
+
+            std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:not_v:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+            CHECK_AST(data, result);
+          }
+        }
+
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
+let test : N -> (B), n -> (n<10, n>1, false);
+test(2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:test:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
+let test : Z -> (B), z -> (z>3, z<5);
+test(2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:test:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("R argument")
+        {
+          std::string_view data = R"(
+let test : R -> (B), x -> (x>2.3, 2*x>3);
+test(2.1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:test:NameProcessor)
+     `-(language::real:2.1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("return a (N)")
+      {
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
+let test : N -> (N), n -> (n+2, n, true);
+test(2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:test:NameProcessor)
+     `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
+let absolute : Z -> (N), z -> ((z>0)*z -(z<=0)*z, 3, true);
+absolute(-2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:absolute:NameProcessor)
+     `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("return a (Z)")
+      {
+        SECTION("B argument")
+        {
+          std::string_view data = R"(
+let minus : B -> (Z), b -> (-b, b, true);
+minus(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:minus:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
+let minus : N -> (Z), n -> (-n, n, true);
+minus(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:minus:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
+let times_2 : Z -> (Z), z -> (z*2, 2, false);
+times_2(-2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:times_2:NameProcessor)
+     `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("return a (R)")
+      {
+        SECTION("B argument")
+        {
+          std::string_view data = R"(
+let minus : B -> (Z), b -> (-b, b, true);
+minus(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:minus:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("N argument")
+        {
+          std::string_view data = R"(
+let minus : N -> (R), n -> (-n, n, true);
+minus(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:minus:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z argument")
+        {
+          std::string_view data = R"(
+let times_2 : Z -> (R), z -> (z*2, 2, false);
+times_2(-2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:times_2:NameProcessor)
+     `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("R argument")
+        {
+          std::string_view data = R"(
+let times_2 : R -> (R), r -> (r*2, 2, false);
+times_2(-2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:times_2:NameProcessor)
+     `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("return a (string)")
+      {
+        SECTION("string argument")
+        {
+          std::string_view data = R"(
+let cat : string*string -> (string), (s1,s2) -> (s1+s2, true, -3, 2.4);
+cat("foo", "bar");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:cat:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::literal:"foo":ValueProcessor)
+         `-(language::literal:"bar":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("B argument conversion")
+        {
+          std::string_view data = R"(
+let cat : string*string -> (string), (s1,s2) -> (s1+s2, true, 2, 2.5);
+cat("foo", true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:cat:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::literal:"foo":ValueProcessor)
+         `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("N argument conversion")
+        {
+          std::string_view data = R"(
+let cat : string*string -> (string), (s1,s2) -> (s1+s2, 4, [1.2], [2,2]);
+let n : N, n = 2;
+cat("foo", n);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:cat:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::literal:"foo":ValueProcessor)
+         `-(language::name:n:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Z argument conversion")
+        {
+          std::string_view data = R"(
+let cat : string*string -> (string), (s1,s2) -> (s1+s2, s2);
+cat("foo", -1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:cat:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::literal:"foo":ValueProcessor)
+         `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+             `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("R argument conversion")
+        {
+          std::string_view data   = R"(
+let cat : string*string -> (string), (s1,s2) -> (s1+s2, 1.3);
+cat("foo", 2.5e-3);
+)";
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:cat:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::literal:"foo":ValueProcessor)
+         `-(language::real:2.5e-3:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("Return a (R^1)")
+      {
+        std::string_view data = R"(
+let f : R^1 -> (R^1), x -> (x+x, 2.3, [1.2]);
+let x : R^1, x = 1;
+f(x);
+let n:N, n=1;
+f(true);
+f(n);
+f(2);
+f(1.4);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::name:x:NameProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::true_kw:ValueProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::name:n:NameProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::integer:2:ValueProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::real:1.4:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (R^2)")
+      {
+        std::string_view data = R"(
+let f : R^2 -> (R^2), x -> (x+x, 0, [1, 3]);
+let x : R^2, x = (1,2);
+f(x);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::name:x:NameProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (R^3)")
+      {
+        std::string_view data = R"(
+let f : R^3 -> (R^3), x -> (x+x, 2*x, [1,2,3], 0);
+let x : R^3, x = (1,2,3);
+f(x);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::name:x:NameProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (R^1x1)")
+      {
+        std::string_view data = R"(
+let f : R^1x1 -> (R^1x1), x -> (x+x, [[1]], 3, 0);
+let x : R^1x1, x = 1;
+f(x);
+let n:N, n=1;
+f(true);
+f(n);
+f(2);
+f(1.4);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::name:x:NameProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::true_kw:ValueProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::name:n:NameProcessor)
+ +-(language::function_evaluation:FunctionProcessor)
+ |   +-(language::name:f:NameProcessor)
+ |   `-(language::integer:2:ValueProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::real:1.4:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (R^2x2)")
+      {
+        std::string_view data = R"(
+let f : R^2x2 -> (R^2x2), x -> (x+x, 0, [[1,2],[3,4]]);
+let x : R^2x2, x = (1,2,3,4);
+f(x);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::name:x:NameProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (R^3x3)")
+      {
+        std::string_view data = R"(
+let f : R^3x3 -> (R^3x3), x -> (x+x, 0, [[1,2,3],[4,5,6],[7,8,9]]);
+let x : R^3x3, x = (1,2,3,4,5,6,7,8,9);
+f(x);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::name:x:NameProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return a (builtin_t)")
+      {
+        std::string_view data = R"(
+let foo : builtin_t -> (builtin_t), b -> (b, b, b);
+let b0 : builtin_t;
+foo(b0);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:foo:NameProcessor)
+     `-(language::name:b0:NameProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+    }
+
+    SECTION("from single value")
+    {
+      SECTION("without conversion")
+      {
+        SECTION("Return a (B)")
+        {
+          std::string_view data = R"(
+let f : B -> (B), b -> b;
+f(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N)")
+        {
+          std::string_view data = R"(
+let f : N -> (N), n -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z)")
+        {
+          std::string_view data = R"(
+let f : Z -> (Z), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R)")
+        {
+          std::string_view data = R"(
+let f : R -> (R), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (string)")
+        {
+          std::string_view data = R"(
+let f : string -> (string), s -> s;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1), x -> [x+1];
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2)")
+        {
+          std::string_view data = R"(
+let f : R*R -> (R^2), (x,y) -> [x,y];
+f(1,2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3)")
+        {
+          std::string_view data = R"(
+let f : R*R*R -> (R^3), (x,y,z) -> [x,y,z];
+f(1,2,3);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         `-(language::integer:3:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1x1), x -> [[x+1]];
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2x2)")
+        {
+          std::string_view data = R"(
+let f : R*R*R*R -> (R^2x2), (x,y,z,t) -> [[x,y],[z,t]];
+f(1,2,3,4);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3x3)")
+        {
+          std::string_view data = R"(
+let f : R^3*R^3*R^3 -> (R^3x3), (x,y,z) -> [[x[0],x[1],x[2]],[y[0],y[1],y[2]],[z[0],z[1],z[2]]];
+f([1,2,3],[4,5,6],[7,8,9]);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+         |   +-(language::integer:1:ValueProcessor)
+         |   +-(language::integer:2:ValueProcessor)
+         |   `-(language::integer:3:ValueProcessor)
+         +-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+         |   +-(language::integer:4:ValueProcessor)
+         |   +-(language::integer:5:ValueProcessor)
+         |   `-(language::integer:6:ValueProcessor)
+         `-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+             +-(language::integer:7:ValueProcessor)
+             +-(language::integer:8:ValueProcessor)
+             `-(language::integer:9:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("return a (builtin_t)")
+        {
+          std::string_view data = R"(
+let foo : builtin_t -> (builtin_t), b -> b;
+let b0 : builtin_t;
+foo(b0);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:foo:NameProcessor)
+     `-(language::name:b0:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+
+      SECTION("with conversion")
+      {
+        SECTION("Return a B -> (R^1)")
+        {
+          std::string_view data = R"(
+let f : B -> (R^1), b -> b;
+f(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a N -> (R^1)")
+        {
+          std::string_view data = R"(
+let f : N -> (R^1), n -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a Z -> (R^1)")
+        {
+          std::string_view data = R"(
+let f : Z -> (R^1), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a R -> (R^1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a B -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : B -> (R^1x1), b -> b;
+f(true);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::true_kw:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a N -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : N -> (R^1x1), n -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a Z -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : Z -> (R^1x1), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a R -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1x1), x -> x+1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^2)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^2), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^3)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^3), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^1x1), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^2x2)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^2x2), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return '0' -> (R^3x3)")
+        {
+          std::string_view data = R"(
+let f : R -> (R^3x3), x -> 0;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+    }
+
+    SECTION("from tuple value")
+    {
+      SECTION("without conversion")
+      {
+        SECTION("Return a (B)")
+        {
+          std::string_view data = R"(
+let b:(B), b = (true, false);
+let f : Z -> (B), z -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (N)")
+        {
+          std::string_view data = R"(
+let b : (B), b = (true, false);
+let f : N -> (N), n -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N)")
+        {
+          std::string_view data = R"(
+let n : (N), n = (1, 2, 3);
+let f : N -> (N), i -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z) -> (N)")
+        {
+          std::string_view data = R"(
+let z : (Z), z = (1, 2, 3);
+let f : N -> (Z), i -> z;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (Z)")
+        {
+          std::string_view data = R"(
+let b : (B), b = (true, true);
+let f : Z -> (Z), x -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N) -> (Z)")
+        {
+          std::string_view data = R"(
+let n : (N), n = (1, 2, 3);
+let f : Z -> (Z), x -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z)")
+        {
+          std::string_view data = R"(
+let z : (Z), z = (1, 2, 3);
+let f : Z -> (Z), x -> z;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (R)")
+        {
+          std::string_view data = R"(
+let b:(B), b = (true, false);
+let f : R -> (R), x -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N) -> (R)")
+        {
+          std::string_view data = R"(
+let n:(N), n = (1, 2, 3);
+let f : R -> (R), x -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z) -> (R)")
+        {
+          std::string_view data = R"(
+let z:(Z), z = (1, -2, 3);
+let f : R -> (R), x -> z;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R)")
+        {
+          std::string_view data = R"(
+let r:(R), r = (1.1, -2, 3.2);
+let f : R -> (R), x -> r;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (string)")
+        {
+          std::string_view data = R"(
+let b : (B), b = (true, false);
+let f : string -> (string), t -> b;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N) -> (string)")
+        {
+          std::string_view data = R"(
+let n : (N), n = (1, 2, 3);
+let f : string -> (string), t -> n;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z) -> (string)")
+        {
+          std::string_view data = R"(
+let z : (Z), z = (1, -2, 3);
+let f : string -> (string), t -> z;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R), r = (1, -2, 3);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^1), r = (1, [-2], 3);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^2), r = ([1, 2], [-2, 3], [3, -4]);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^3), r = ([1, 2, 3], [0, -2, 3], [3, -4, 2]);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1x1) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^1x1), r = (1, [[-2]], 3);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2x2) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^2x2), r = ([[1, 2], [-2, 3]], [[3, -4], [2, 1]]);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3x3) -> (string)")
+        {
+          std::string_view data = R"(
+let r : (R^3x3), r = ([[1, 2, 3], [0, -2, 3], [3, -4, 2]], 0);
+let f : string -> (string), t -> r;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (string)")
+        {
+          std::string_view data = R"(
+let s : (string), s = ("foo", "bar");
+let f : string -> (string), t -> s;
+f("foo");
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::literal:"foo":ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (R^1)")
+        {
+          std::string_view data = R"(
+let b : (B), b = (true, false);
+let f : R -> (R^1), x -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N) -> (R^1)")
+        {
+          std::string_view data = R"(
+let n : (N), n = (1, 2, 3);
+let f : R -> (R^1), x -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z) -> (R^1)")
+        {
+          std::string_view data = R"(
+let z : (Z), z = (1, 2, 3);
+let f : R -> (R^1), x -> z;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R) -> (R^1)")
+        {
+          std::string_view data = R"(
+let r : (R), r = (1, 2, 3);
+let f : R -> (R^1), x -> r;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1)")
+        {
+          std::string_view data = R"(
+let r1 : (R^1), r1 = (1, 2, 3);
+let f : R -> (R^1), x -> r1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2)")
+        {
+          std::string_view data = R"(
+let r2: (R^2), r2 = ([1, 2], [3, 4], 0);
+let f : R*R -> (R^2), (x,y) -> r2;
+f(1,2);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         `-(language::integer:2:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3)")
+        {
+          std::string_view data = R"(
+let r3: (R^3), r3 = ([1, 2, 3], [1, 3, 4], 0);
+let f : R*R*R -> (R^3), (x,y,z) -> r3;
+f(1,2,3);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         `-(language::integer:3:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (B) -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let b: (B), b = (false, true);
+let f : R -> (R^1x1), x -> b;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (N) -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let n: (N), n = (1, 2, 3);
+let f : R -> (R^1x1), x -> n;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (Z) -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let z: (Z), z = (1, -2, 3);
+let f : R -> (R^1x1), x -> z;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R) -> (R^1x1)")
+        {
+          std::string_view data = R"(
+let r: (R), r = (1, -2, 3);
+let f : R -> (R^1x1), x -> r;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^1x1)")
+        {
+          std::string_view data = R"(
+let r1: (R^1x1), r1 = (1, -2, 3);
+let f : R -> (R^1x1), x -> r1;
+f(1);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::integer:1:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^2x2)")
+        {
+          std::string_view data = R"(
+let r2: (R^2x2), r2 = ([1, -2], [3, 2], 0);
+let f : R*R*R*R -> (R^2x2), (x,y,z,t) -> r2;
+f(1,2,3,4);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("Return a (R^3x3)")
+        {
+          std::string_view data = R"(
+let r3: (R^3x3), r3 = ([1, -2, 2], [3, 1, 2], 0);
+let f : R^3*R^3*R^3 -> (R^3x3), (x,y,z) -> r3;
+f([1,2,3],[4,5,6],[7,8,9]);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+         |   +-(language::integer:1:ValueProcessor)
+         |   +-(language::integer:2:ValueProcessor)
+         |   `-(language::integer:3:ValueProcessor)
+         +-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+         |   +-(language::integer:4:ValueProcessor)
+         |   +-(language::integer:5:ValueProcessor)
+         |   `-(language::integer:6:ValueProcessor)
+         `-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+             +-(language::integer:7:ValueProcessor)
+             +-(language::integer:8:ValueProcessor)
+             `-(language::integer:9:ValueProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+
+        SECTION("return a (builtin_t)")
+        {
+          std::string_view data = R"(
+let b0 : builtin_t;
+let bt : (builtin_t), bt = (b0, b0, b0);
+let foo : builtin_t -> (builtin_t), b -> bt;
+foo(b0);
+)";
+
+          std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:foo:NameProcessor)
+     `-(language::name:b0:NameProcessor)
+)";
+
+          CHECK_AST(data, result);
+        }
+      }
+    }
+
+    SECTION("Return tuple in compound")
+    {
+      SECTION("Return (R^d) in compound")
+      {
+        std::string_view data = R"(
+let b0 : builtin_t;
+let f : R*R*builtin_t*R*R -> R*R^1*(R^2)*(builtin_t)*(R^3), (x,y,b,z,t) -> (t, [x], [x,y], (b,b), ([x,y,z], 0));
+f(1,2,b0,3,4);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::name:b0:NameProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+
+      SECTION("Return (R^dxd) in compound")
+      {
+        std::string_view data = R"(
+let f : R*R*R*R -> R*R^1x1*(R^2x2)*(R^3x3), (x,y,z,t) -> (t, [[x]], (0,[[x,y],[z,t]]), ([[x,y,z], [x,x,x], [t,t,t]], 0));
+f(1,2,3,4);
+)";
+
+        std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+         +-(language::integer:1:ValueProcessor)
+         +-(language::integer:2:ValueProcessor)
+         +-(language::integer:3:ValueProcessor)
+         `-(language::integer:4:ValueProcessor)
+)";
+
+        CHECK_AST(data, result);
+      }
+    }
+
+    SECTION("errors")
+    {
+      SECTION("wrong argument number")
+      {
+        std::string_view data = R"(
+let Id : Z -> Z, z -> z;
+Id(2,3);
+)";
+
+        CHECK_AST_THROWS(data);
+      }
+
+      SECTION("wrong argument number 2")
+      {
+        std::string_view data = R"(
+let sum : R*R -> R, (x,y) -> x+y;
+sum(2);
+)";
+
+        CHECK_AST_THROWS(data);
+      }
+
+      SECTION("tuple as domain")
+      {
+        SECTION("basic type string")
+        {
+          std::string_view data = R"(
+let bad_tuple : (string) -> N, s -> 2;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"cannot use tuple (string) as a domain for user functions"});
+        }
+
+        SECTION("basic type R")
+        {
+          std::string_view data = R"(
+let bad_tuple : (R) -> R, x -> 2;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"cannot use tuple (R) as a domain for user functions"});
+        }
+      }
+
+      SECTION("invalid return implicit conversion")
+      {
+        SECTION("string -> R")
+        {
+          std::string_view data = R"(
+let bad_conv : string -> R, s -> s;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
+        }
+
+        SECTION("string -> R")
+        {
+          std::string_view data = R"(
+let bad_conv : R -> R, x -> "foo";
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
+        }
+
+        SECTION("string -> (R)")
+        {
+          std::string_view data = R"(
+let bad_conv : R -> (R), x -> (x, "bar");
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
+        }
+
+        SECTION("string -> (R) 2")
+        {
+          std::string_view data = R"(
+let bad_conv : R -> (R), x -> "bar";
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
+        }
+
+        SECTION("R -> B")
+        {
+          std::string_view data = R"(
+let bad_B : R -> B, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
+
+        SECTION("R -> (B)")
+        {
+          std::string_view data = R"(
+let bad_B : R -> (B), x -> (true, x, false);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
+
+        SECTION("R -> (B) 2")
+        {
+          std::string_view data = R"(
+let bad_B : R -> (B), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
+
+        SECTION("R -> N")
+        {
+          std::string_view data = R"(
+let next : R -> N, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
+
+        SECTION("R -> (N)")
+        {
+          std::string_view data = R"(
+let next : R -> (N), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
+
+        SECTION("R -> (N) 2")
+        {
+          std::string_view data = R"(
+let next : R -> (N), x -> (6, 2, x);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
+
+        SECTION("R -> Z")
+        {
+          std::string_view data = R"(
+let prev : R -> Z, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
+
+        SECTION("R -> (Z)")
+        {
+          std::string_view data = R"(
+let prev : R -> (Z), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
+
+        SECTION("R -> (Z) 2")
+        {
+          std::string_view data = R"(
+let prev : R -> (Z), x -> (x, 2, -3);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
+
+        SECTION("N -> B")
+        {
+          std::string_view data = R"(
+let bad_B : N -> B, n -> n;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
+
+        SECTION("N -> (B)")
+        {
+          std::string_view data = R"(
+let bad_B : N -> (B), n -> n;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
+
+        SECTION("N -> (B)")
+        {
+          std::string_view data = R"(
+let bad_B : N -> (B), n -> (true, n);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
+
+        SECTION("Z -> B")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> B, n -> n;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
+
+        SECTION("Z -> (B)")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> (B), n -> n;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
+
+        SECTION("Z -> (B)")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> (B), n -> (true, n, true);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
+
+        SECTION("Z -> R^2")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> R^2, n -> 1;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2"});
+        }
+
+        SECTION("Z -> (R^2)")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> (R^2), n -> (n, [2,1]);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2"});
+        }
+
+        SECTION("R^1 -> R^2")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> R^2, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^2"});
+        }
+
+        SECTION("R^1 -> (R^2)")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> (R^2), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^2"});
+        }
+
+        SECTION("R^1 -> (R^2)")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> (R^2), x -> ([1,2], x);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^2"});
+        }
+
+        SECTION("Z -> (R^3)")
+        {
+          std::string_view data = R"(
+let bad_B : Z -> (R^3), n -> (n, [2,1,2]);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3"});
+        }
+
+        SECTION("R^1 -> R^3")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> R^3, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^3"});
+        }
+
+        SECTION("R^1 -> (R^3)")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> (R^3), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^3"});
+        }
+
+        SECTION("R^1 -> (R^3)")
+        {
+          std::string_view data = R"(
+let bad_B : R^1 -> (R^3), x -> ([1,2,2], x);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^3"});
+        }
+
+        SECTION("R^2 -> R^3")
+        {
+          std::string_view data = R"(
+let bad_B : R^2 -> R^3, x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R^3"});
+        }
+
+        SECTION("R^2 -> (R^3)")
+        {
+          std::string_view data = R"(
+let bad_B : R^2 -> (R^3), x -> x;
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R^3"});
+        }
+
+        SECTION("R^2 -> (R^3)")
+        {
+          std::string_view data = R"(
+let bad_B : R^2 -> (R^3), x -> ([1,2,5], x);
+)";
+
+          CHECK_TYPE_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R^3"});
+        }
+      }
+
+      SECTION("invalid argument implicit conversion")
+      {
+        SECTION("N -> B")
+        {
+          std::string_view data = R"(
+let negate : B -> B, b -> not b;
+let n : N, n = 2;
+negate(n);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+        }
+
+        SECTION("Z -> B")
+        {
+          std::string_view data = R"(
+let negate : B -> B, b -> not b;
+negate(3-4);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+        }
+
+        SECTION("R -> B")
+        {
+          std::string_view data = R"(
+let negate : B -> B, b -> not b;
+negate(3.24);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+        }
+
+        SECTION("R -> N")
+        {
+          std::string_view data = R"(
+let next : N -> N, n -> n+1;
+next(3.24);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+        }
+
+        SECTION("R -> Z")
+        {
+          std::string_view data = R"(
+let prev : Z -> Z, z -> z-1;
+prev(3 + .24);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+        }
+
+        SECTION("B -> R^2")
+        {
+          std::string_view data = R"(
+let f : R^2 -> R^2, x -> x;
+f(true);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2"});
+        }
+
+        SECTION("N -> R^2")
+        {
+          std::string_view data = R"(
+let f : R^2 -> R^2, x -> x;
+let n : N, n = 2;
+f(n);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2"});
+        }
+
+        SECTION("Z -> R^2")
+        {
+          std::string_view data = R"(
+let f : R^2 -> R^2, x -> x;
+f(-2);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2"});
+        }
+
+        SECTION("R -> R^2")
+        {
+          std::string_view data = R"(
+let f : R^2 -> R^2, x -> x;
+f(1.3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2"});
+        }
+
+        SECTION("B -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R^3, x -> x;
+f(true);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3"});
+        }
+
+        SECTION("N -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R^3, x -> x;
+let n : N, n = 2;
+f(n);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3"});
+        }
+
+        SECTION("Z -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R^3, x -> x;
+f(-2);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3"});
+        }
+
+        SECTION("R -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R^3, x -> x;
+f(1.3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3"});
+        }
+
+        SECTION("B -> R^2x2")
+        {
+          std::string_view data = R"(
+let f : R^2x2 -> R^2x2, x -> x;
+f(true);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2x2"});
+        }
+
+        SECTION("N -> R^2x2")
+        {
+          std::string_view data = R"(
+let f : R^2x2 -> R^2x2, x -> x;
+let n : N, n = 2;
+f(n);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2x2"});
+        }
+
+        SECTION("Z -> R^2x2")
+        {
+          std::string_view data = R"(
+let f : R^2x2 -> R^2x2, x -> x;
+f(-2);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2x2"});
+        }
+
+        SECTION("R -> R^2x2")
+        {
+          std::string_view data = R"(
+let f : R^2x2 -> R^2x2, x -> x;
+f(1.3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2x2"});
+        }
+
+        SECTION("B -> R^3x3")
+        {
+          std::string_view data = R"(
+let f : R^3x3 -> R^3x3, x -> x;
+f(true);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3x3"});
+        }
+
+        SECTION("N -> R^3x3")
+        {
+          std::string_view data = R"(
+let f : R^3x3 -> R^3x3, x -> x;
+let n : N, n = 2;
+f(n);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3x3"});
+        }
+
+        SECTION("Z -> R^3x3")
+        {
+          std::string_view data = R"(
+let f : R^3x3 -> R^3x3, x -> x;
+f(-2);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3x3"});
+        }
+
+        SECTION("R -> R^3x3")
+        {
+          std::string_view data = R"(
+let f : R^3x3 -> R^3x3, x -> x;
+f(1.3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3x3"});
+        }
+      }
+
+      SECTION("arguments invalid tuple -> R^d conversion")
+      {
+        SECTION("tuple[2] -> R^2")
+        {
+          std::string_view data = R"(
+let f : R^2 -> R, x->x[0];
+f((1,2));
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
+        }
+
+        SECTION("tuple[3] -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R, x->x[0];
+f((1,2,3));
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
+        }
+
+        SECTION("compound -> R^2")
+        {
+          std::string_view data = R"(
+let f : R*R^2 -> R, (t,x)->x[0];
+f(1,(1,2));
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^2"});
+        }
+
+        SECTION("compound -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3*R^2 -> R, (x,y)->x[0]*y[1];
+f((1,2,3),[3,4]);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"cannot convert list to R^3"});
+        }
+
+        SECTION("list instead of tuple -> R^3")
+        {
+          std::string_view data = R"(
+let f : R^3 -> R, x -> x[0]*x[1];
+f(1,2,3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 1, provided 3"});
+        }
+
+        SECTION("list instead of tuple -> R^3*R^2")
+        {
+          std::string_view data = R"(
+let f : R^3*R^2 -> R, (x,y) -> x[0]*x[1]-y[0];
+f([1,2,3],2,3);
+)";
+
+          CHECK_EXPRESSION_BUILDER_THROWS_WITH(data, std::string{"bad number of arguments: expecting 2, provided 3"});
+        }
+      }
+
+      SECTION("non pure function")
+      {
+        SECTION("argument modification")
+        {
+          SECTION("++ argument")
+          {
+            std::string_view data = R"(
+let non_pure : N -> N, x -> 3 * ++x;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("argument ++")
+          {
+            std::string_view data = R"(
+let non_pure : N -> N, x -> 1 + x ++;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("-- argument")
+          {
+            std::string_view data = R"(
+let non_pure : Z -> Z, x -> 3 * --x;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("argument --")
+          {
+            std::string_view data = R"(
+let non_pure : Z -> Z, x -> 1 + x --;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+        }
+
+        SECTION("outer variable modification")
+        {
+          SECTION("++ outer variable")
+          {
+            std::string_view data = R"(
+let a:N, a = 4;
+let non_pure : Z -> Z, x -> x * ++a;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("outer variable ++")
+          {
+            std::string_view data = R"(
+let a:N, a = 4;
+let non_pure : N -> N, x -> x + a++;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("-- outer variable")
+          {
+            std::string_view data = R"(
+let a:Z, a = 4;
+let non_pure : Z -> Z, x -> x * --a;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+
+          SECTION("outer variable --")
+          {
+            std::string_view data = R"(
+let a:Z, a = 4;
+let non_pure : Z -> Z, x -> x + a--;
+)";
+
+            CHECK_EXPRESSION_BUILDER_THROWS_WITH(data,
+                                                 std::string{
+                                                   "invalid function definition. Function data must be constant!"});
+          }
+        }
+      }
+    }
+  }
+
+  SECTION("empty functions")
+  {
+    SECTION("void -> value")
+    {
+      std::string_view data = R"(
+let f:void -> R, void  -> 2.3;
+
+f();
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("void -> compound")
+    {
+      std::string_view data = R"(
+let f:void -> R*(Z)*R^2, void  -> (2.3, (1,2,3), [1.2, 2.3]);
+
+f();
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ `-(language::function_evaluation:FunctionProcessor)
+     +-(language::name:f:NameProcessor)
+     `-(language::function_argument_list:ASTNodeExpressionListProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("errors")
+    {
+      SECTION("void as a parameter")
+      {
+        std::string_view data = R"(
+let g:R -> R, void -> 2;
+)";
+
+        std::string error = "unexpected 'void' keyword";
+
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+
+        REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, error);
+      }
+
+      SECTION("void as a parameter")
+      {
+        std::string_view data = R"(
+let g:void -> R, x -> 2;
+)";
+
+        std::string error = "expecting 'void' keyword";
+
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+
+        REQUIRE_THROWS_WITH(ASTNodeDataTypeBuilder{*ast}, error);
+      }
+
+      SECTION("void in compound domain")
+      {
+        std::string_view data = R"(
+let h:R*void -> R, (x,void) -> 2.5;
+)";
+
+        std::string error = "parse error, expecting type specifier";
+
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+        // auto ast = ASTBuilder::build(input);
+
+        // ASTModulesImporter{*ast};
+        // ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        // ASTSymbolTableBuilder{*ast};
+        // ASTNodeDataTypeBuilder{*ast};
+
+        REQUIRE_THROWS_WITH(ASTBuilder::build(input), error);
+      }
+
+      SECTION("void in compound codomain")
+      {
+        std::string_view data = R"(
+let h:R*void -> R, (x,void) -> 2.5;
+)";
+        std::string error     = "parse error, expecting type specifier";
+
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        REQUIRE_THROWS_WITH(ASTBuilder::build(input), error);
+      }
+    }
+  }
 }
diff --git a/tests/test_ASTNodeListAffectationExpressionBuilder.cpp b/tests/test_ASTNodeListAffectationExpressionBuilder.cpp
index 7af93f6fb54b60dedad9e25f0504e69424613436..3f71228607f80f2ac04ad69fc408258ced89b413 100644
--- a/tests/test_ASTNodeListAffectationExpressionBuilder.cpp
+++ b/tests/test_ASTNodeListAffectationExpressionBuilder.cpp
@@ -61,25 +61,37 @@ const auto builtin_data_type = ast_node_data_type_from<std::shared_ptr<const dou
     REQUIRE(ast_output.str() == expected_output);                                                               \
   }
 
-#define CHECK_AST_THROWS_WITH(data, error)                                         \
-  {                                                                                \
-    static_assert(std::is_same_v<std::decay_t<decltype(data)>, std::string_view>); \
-    static_assert(std::is_same_v<std::decay_t<decltype(error)>, std::string>);     \
-                                                                                   \
-    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};                     \
-    auto ast = ASTBuilder::build(input);                                           \
-                                                                                   \
-    ASTModulesImporter{*ast};                                                      \
-    ASTNodeTypeCleaner<language::import_instruction>{*ast};                        \
-                                                                                   \
-    ASTSymbolTableBuilder{*ast};                                                   \
-    ASTNodeDataTypeBuilder{*ast};                                                  \
-                                                                                   \
-    ASTNodeDeclarationToAffectationConverter{*ast};                                \
-    ASTNodeTypeCleaner<language::var_declaration>{*ast};                           \
-    ASTNodeTypeCleaner<language::fct_declaration>{*ast};                           \
-                                                                                   \
-    REQUIRE_THROWS_WITH(ASTNodeExpressionBuilder{*ast}, error);                    \
+#define CHECK_AST_THROWS_WITH(data, error)                                                                      \
+  {                                                                                                             \
+    static_assert(std::is_same_v<std::decay_t<decltype(data)>, std::string_view>);                              \
+    static_assert(std::is_same_v<std::decay_t<decltype(error)>, std::string>);                                  \
+                                                                                                                \
+    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};                                                  \
+    auto ast = ASTBuilder::build(input);                                                                        \
+                                                                                                                \
+    ASTModulesImporter{*ast};                                                                                   \
+    BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")}; \
+                                                                                                                \
+    ASTNodeTypeCleaner<language::import_instruction>{*ast};                                                     \
+    SymbolTable& symbol_table = *ast->m_symbol_table;                                                           \
+    auto [i_symbol, success]  = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin());               \
+    if (not success) {                                                                                          \
+      throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing");          \
+    }                                                                                                           \
+                                                                                                                \
+    i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>());              \
+    i_symbol->attributes().setIsInitialized();                                                                  \
+    i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size();                                   \
+    symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId()));   \
+                                                                                                                \
+    ASTSymbolTableBuilder{*ast};                                                                                \
+    ASTNodeDataTypeBuilder{*ast};                                                                               \
+                                                                                                                \
+    ASTNodeDeclarationToAffectationConverter{*ast};                                                             \
+    ASTNodeTypeCleaner<language::var_declaration>{*ast};                                                        \
+    ASTNodeTypeCleaner<language::fct_declaration>{*ast};                                                        \
+                                                                                                                \
+    REQUIRE_THROWS_WITH(ASTNodeExpressionBuilder{*ast}, error);                                                 \
   }
 
 // clazy:excludeall=non-pod-global-static
@@ -1507,106 +1519,1607 @@ let (b0,b1): builtin_t*builtin_t, (b0,b1) = (b0,b1);
     }
   }
 
-  SECTION("Errors")
+  SECTION("list from tuple")
   {
-    SECTION("invalid affectation rhs")
+    SECTION("(B) -> list")
     {
       std::string_view data = R"(
-let x:R;
-let i:R;
-(x,i) = 3;
+let t : (B), t = (true, false, true, false, false, true, false);
+let (b,n,z,r,x1,x11,s):B*N*Z*R*R^1*R^1x1*string,
+    (b,n,z,r,x1,x11,s) = t;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<bool>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::true_kw:ValueProcessor)
+ |       +-(language::false_kw:ValueProcessor)
+ |       +-(language::true_kw:ValueProcessor)
+ |       +-(language::false_kw:ValueProcessor)
+ |       +-(language::false_kw:ValueProcessor)
+ |       +-(language::true_kw:ValueProcessor)
+ |       `-(language::false_kw:ValueProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, bool>)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:b:NameProcessor)
+     |   +-(language::name:n:NameProcessor)
+     |   +-(language::name:z:NameProcessor)
+     |   +-(language::name:r:NameProcessor)
+     |   +-(language::name:x1:NameProcessor)
+     |   +-(language::name:x11:NameProcessor)
+     |   `-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST_THROWS_WITH(data, std::string{"invalid right hand side in tuple affectation"});
+      CHECK_AST(data, result);
     }
 
-    SECTION("incompatible list sizes")
+    SECTION("(N) -> list")
     {
       std::string_view data = R"(
-let (x,y) : R*R, (x,y) = (3, 3, 2);
+let t : (N), t = (1, 3, 6, 2, 7, 1);
+let (n,z,r,x1,x11,s):N*Z*R*R^1*R^1x1*string,
+    (n,z,r,x1,x11,s) = t;
 )";
 
-      CHECK_AST_THROWS_WITH(data, std::string{"incompatible list sizes in affectation"});
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<unsigned long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::integer:1:ValueProcessor)
+ |       +-(language::integer:3:ValueProcessor)
+ |       +-(language::integer:6:ValueProcessor)
+ |       +-(language::integer:2:ValueProcessor)
+ |       +-(language::integer:7:ValueProcessor)
+ |       `-(language::integer:1:ValueProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, unsigned long>)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:n:NameProcessor)
+     |   +-(language::name:z:NameProcessor)
+     |   +-(language::name:r:NameProcessor)
+     |   +-(language::name:x1:NameProcessor)
+     |   +-(language::name:x11:NameProcessor)
+     |   `-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
     }
 
-    SECTION("incompatible list sizes 2")
+    SECTION("(Z) -> list")
     {
       std::string_view data = R"(
-let (x,y,z):R*R*R, (x,y,z) = (1, 2);
+let t : (Z), t = (1, 3, 6, 2, 7, 1);
+let (n,z,r,x1,x11,s):N*Z*R*R^1*R^1x1*string,
+    (n,z,r,x1,x11,s) = t;
 )";
 
-      CHECK_AST_THROWS_WITH(data, std::string{"incompatible list sizes in affectation"});
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<long>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::integer:1:ValueProcessor)
+ |       +-(language::integer:3:ValueProcessor)
+ |       +-(language::integer:6:ValueProcessor)
+ |       +-(language::integer:2:ValueProcessor)
+ |       +-(language::integer:7:ValueProcessor)
+ |       `-(language::integer:1:ValueProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, long>)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:n:NameProcessor)
+     |   +-(language::name:z:NameProcessor)
+     |   +-(language::name:r:NameProcessor)
+     |   +-(language::name:x1:NameProcessor)
+     |   +-(language::name:x11:NameProcessor)
+     |   `-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
     }
 
-    SECTION("incompatible list sizes from function evaluation")
+    SECTION("(R) -> list")
     {
       std::string_view data = R"(
-let f: R -> R, x -> x*x;
-let(x,y) : R*R, (x,y) = f(3);
+let t : (R), t = (6.2, -2.1, 7.2, 3);
+let (r,x1,x11,s):R*R^1*R^1x1*string,
+    (r,x1,x11,s) = t;
 )";
 
-      CHECK_AST_THROWS_WITH(data, std::string{"incompatible list sizes in affectation"});
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<double>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::real:6.2:ValueProcessor)
+ |       +-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, double, double>)
+ |       |   `-(language::real:2.1:ValueProcessor)
+ |       +-(language::real:7.2:ValueProcessor)
+ |       `-(language::integer:3:ValueProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, double>)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:r:NameProcessor)
+     |   +-(language::name:x1:NameProcessor)
+     |   +-(language::name:x11:NameProcessor)
+     |   `-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
     }
 
-    SECTION("incompatible list sizes from function evaluation")
+    SECTION("(R^1) -> list")
     {
       std::string_view data = R"(
-let(x,y):R*R,(x,y)=(2,3);
-(x,y) += (1,4);
+let t : (R^1), t = (6.2, [-2.1]);
+let (x1,s):R^1*string,
+    (x1,s) = t;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<TinyVector<1ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::real:6.2:ValueProcessor)
+ |       `-(language::vector_expression:TinyVectorExpressionProcessor<1ul>)
+ |           `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, double, double>)
+ |               `-(language::real:2.1:ValueProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, TinyVector<1ul, double> >)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:x1:NameProcessor)
+     |   `-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST_THROWS_WITH(data, std::string{"undefined affectation operator for tuples"});
+      CHECK_AST(data, result);
     }
 
-    SECTION("invalid operand type for affectation")
+    SECTION("(R^2) -> list")
     {
       std::string_view data = R"(
-let f: R -> R, x -> x+1;
-let  (x,y) : R*R, (x,y) = (f,2);
+let t : (R^2), t = ([6.2, -2.1], 0);
+let (x2,s):R^2*string,
+    (x2,s) = t;
 )";
 
-      CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: function -> R"});
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<TinyVector<2ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::vector_expression:TinyVectorExpressionProcessor<2ul>)
+ |       |   +-(language::real:6.2:ValueProcessor)
+ |       |   `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, double, double>)
+ |       |       `-(language::real:2.1:ValueProcessor)
+ |       `-(language::integer:0:ValueProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, TinyVector<2ul, double> >)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:x2:NameProcessor)
+     |   `-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
     }
 
-    SECTION("invalid operand type for string affectation")
+    SECTION("(R^3) -> list")
     {
       std::string_view data = R"(
-let f: R -> R, x -> x+1;
-let (s,n):string*N, (s,n) = (f,2);
+let t : (R^3), t = ([6.2, -2.1, 0], [1, 2, 3]);
+let (x3,s):R^3*string,
+    (x3,s) = t;
 )";
 
-      CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: function -> string"});
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<TinyVector<3ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+ |       |   +-(language::real:6.2:ValueProcessor)
+ |       |   +-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, double, double>)
+ |       |   |   `-(language::real:2.1:ValueProcessor)
+ |       |   `-(language::integer:0:ValueProcessor)
+ |       `-(language::vector_expression:TinyVectorExpressionProcessor<3ul>)
+ |           +-(language::integer:1:ValueProcessor)
+ |           +-(language::integer:2:ValueProcessor)
+ |           `-(language::integer:3:ValueProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, TinyVector<3ul, double> >)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:x3:NameProcessor)
+     |   `-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
     }
 
-    SECTION("invalid value type for affectation")
+    SECTION("(R^1x1) -> list")
     {
       std::string_view data = R"(
-let f: R -> R, x -> x+1;
-let x:R;
+let t : (R^1x1), t = (6.2, [[-2.1]]);
+let (x11,s):R^1x1*string,
+    (x11,s) = t;
+)";
 
-(f,x) = (3,2);
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<TinyMatrix<1ul, 1ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::real:6.2:ValueProcessor)
+ |       `-(language::matrix_expression:TinyMatrixExpressionProcessor<1ul, 1ul>)
+ |           `-(language::row_expression:FakeProcessor)
+ |               `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, double, double>)
+ |                   `-(language::real:2.1:ValueProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, TinyMatrix<1ul, 1ul, double> >)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:x11:NameProcessor)
+     |   `-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
 )";
 
-      CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> function"});
+      CHECK_AST(data, result);
     }
 
-    SECTION("invalid R^n -> R^m conversion")
+    SECTION("(R^2x2) -> list")
     {
       std::string_view data = R"(
-let x:R^2, x = [1,2];
-let y:R^3, y = x;
+let t : (R^2x2), t = ([[6.2, -2.1],[1, 2]], 0);
+let (x22,s):R^2x2*string,
+    (x22,s) = t;
 )";
 
-      CHECK_AST_THROWS_WITH(data, std::string{"undefined affectation type: R^3 = R^2"});
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<TinyMatrix<2ul, 2ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::matrix_expression:TinyMatrixExpressionProcessor<2ul, 2ul>)
+ |       |   +-(language::row_expression:FakeProcessor)
+ |       |   |   +-(language::real:6.2:ValueProcessor)
+ |       |   |   `-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, double, double>)
+ |       |   |       `-(language::real:2.1:ValueProcessor)
+ |       |   `-(language::row_expression:FakeProcessor)
+ |       |       +-(language::integer:1:ValueProcessor)
+ |       |       `-(language::integer:2:ValueProcessor)
+ |       `-(language::integer:0:ValueProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, TinyMatrix<2ul, 2ul, double> >)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:x22:NameProcessor)
+     |   `-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
     }
 
-    SECTION("invalid Z -> R^d conversion (non-zero)")
+    SECTION("(R^3x3) -> list")
     {
       std::string_view data = R"(
-let x:R^2, x = 1;
+let t : (R^3x3), t = ([[6.2, -2.1, 0], [1, 2, 3], [0, -2, 1]], 0);
+let (x33,s):R^3x3*string,
+    (x33,s) = t;
 )";
 
-      CHECK_AST_THROWS_WITH(data, std::string{"invalid integral value (0 is the solely valid value)"});
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<TinyMatrix<3ul, 3ul, double> >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::matrix_expression:TinyMatrixExpressionProcessor<3ul, 3ul>)
+ |       |   +-(language::row_expression:FakeProcessor)
+ |       |   |   +-(language::real:6.2:ValueProcessor)
+ |       |   |   +-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, double, double>)
+ |       |   |   |   `-(language::real:2.1:ValueProcessor)
+ |       |   |   `-(language::integer:0:ValueProcessor)
+ |       |   +-(language::row_expression:FakeProcessor)
+ |       |   |   +-(language::integer:1:ValueProcessor)
+ |       |   |   +-(language::integer:2:ValueProcessor)
+ |       |   |   `-(language::integer:3:ValueProcessor)
+ |       |   `-(language::row_expression:FakeProcessor)
+ |       |       +-(language::integer:0:ValueProcessor)
+ |       |       +-(language::unary_minus:UnaryExpressionProcessor<language::unary_minus, long, long>)
+ |       |       |   `-(language::integer:2:ValueProcessor)
+ |       |       `-(language::integer:1:ValueProcessor)
+ |       `-(language::integer:0:ValueProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, TinyMatrix<3ul, 3ul, double> >)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:x33:NameProcessor)
+     |   `-(language::name:s:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("(string) -> list")
+    {
+      std::string_view data = R"(
+let t : (string), t = ("foo", "bar", "foobar");
+let (s1,s2,s3):string*string*string,
+    (s1,s2,s3) = t;
+)";
+
+      std::string result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<)" +
+                           demangled_stdstring + R"( >)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::literal:"foo":ValueProcessor)
+ |       +-(language::literal:"bar":ValueProcessor)
+ |       `-(language::literal:"foobar":ValueProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, )" +
+                           demangled_stdstring + R"( >)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:s1:NameProcessor)
+     |   +-(language::name:s2:NameProcessor)
+     |   `-(language::name:s3:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+
+    SECTION("(builtin_t) -> list")
+    {
+      std::string_view data = R"(
+let b: builtin_t, b = b;
+let t: (builtin_t), t = (b,b,b);
+let (b1,b2,b3): builtin_t*builtin_t*builtin_t, (b1,b2,b3) = t;
+)";
+
+      std::string_view result = R"(
+(root:ASTNodeListProcessor)
+ +-(language::eq_op:AffectationProcessor<language::eq_op, EmbeddedData, EmbeddedData>)
+ |   +-(language::name:b:NameProcessor)
+ |   `-(language::name:b:NameProcessor)
+ +-(language::eq_op:AffectationToTupleFromListProcessor<EmbeddedData>)
+ |   +-(language::name:t:NameProcessor)
+ |   `-(language::expression_list:ASTNodeExpressionListProcessor)
+ |       +-(language::name:b:NameProcessor)
+ |       +-(language::name:b:NameProcessor)
+ |       `-(language::name:b:NameProcessor)
+ `-(language::eq_op:ListAffectationFromTupleProcessor<language::eq_op, EmbeddedData>)
+     +-(language::name_list:FakeProcessor)
+     |   +-(language::name:b1:NameProcessor)
+     |   +-(language::name:b2:NameProcessor)
+     |   `-(language::name:b3:NameProcessor)
+     `-(language::name:t:NameProcessor)
+)";
+
+      CHECK_AST(data, result);
+    }
+  }
+
+  SECTION("Errors")
+  {
+    SECTION("invalid affectation rhs")
+    {
+      std::string_view data = R"(
+let x:R;
+let i:R;
+(x,i) = 3;
+)";
+
+      CHECK_AST_THROWS_WITH(data, std::string{"invalid right hand side in list affectation"});
+    }
+
+    SECTION("incompatible list sizes")
+    {
+      std::string_view data = R"(
+let (x,y) : R*R, (x,y) = (3, 3, 2);
+)";
+
+      CHECK_AST_THROWS_WITH(data, std::string{"incompatible list sizes in affectation"});
+    }
+
+    SECTION("incompatible list sizes 2")
+    {
+      std::string_view data = R"(
+let (x,y,z):R*R*R, (x,y,z) = (1, 2);
+)";
+
+      CHECK_AST_THROWS_WITH(data, std::string{"incompatible list sizes in affectation"});
+    }
+
+    SECTION("incompatible list sizes from function evaluation")
+    {
+      std::string_view data = R"(
+let f: R -> R, x -> x*x;
+let(x,y) : R*R, (x,y) = f(3);
+)";
+
+      CHECK_AST_THROWS_WITH(data, std::string{"incompatible list sizes in affectation"});
+    }
+
+    SECTION("incompatible list sizes from function evaluation")
+    {
+      std::string_view data = R"(
+let(x,y):R*R,(x,y)=(2,3);
+(x,y) += (1,4);
+)";
+
+      CHECK_AST_THROWS_WITH(data, std::string{"undefined affectation operator for lists"});
+    }
+
+    SECTION("invalid operand type for affectation")
+    {
+      std::string_view data = R"(
+let f: R -> R, x -> x+1;
+let  (x,y) : R*R, (x,y) = (f,2);
+)";
+
+      CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: function -> R"});
+    }
+
+    SECTION("invalid operand type for string affectation")
+    {
+      std::string_view data = R"(
+let f: R -> R, x -> x+1;
+let (s,n):string*N, (s,n) = (f,2);
+)";
+
+      CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: function -> string"});
+    }
+
+    SECTION("invalid value type for affectation")
+    {
+      std::string_view data = R"(
+let f: R -> R, x -> x+1;
+let x:R;
+
+(f,x) = (3,2);
+)";
+
+      CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> function"});
+    }
+
+    SECTION("invalid R^n -> R^m conversion")
+    {
+      std::string_view data = R"(
+let x:R^2, x = [1,2];
+let y:R^3, y = x;
+)";
+
+      CHECK_AST_THROWS_WITH(data, std::string{"undefined affectation type: R^3 = R^2"});
+    }
+
+    SECTION("invalid Z -> R^d conversion (non-zero)")
+    {
+      std::string_view data = R"(
+let x:R^2, x = 1;
+)";
+
+      CHECK_AST_THROWS_WITH(data, std::string{"invalid integral value (0 is the solely valid value)"});
+    }
+
+    SECTION("tuples -> list")
+    {
+      SECTION("bad cast")
+      {
+        SECTION("from (string)")
+        {
+          SECTION("string -> B")
+          {
+            std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (s,b) : string*B, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> B"});
+          }
+
+          SECTION("string -> N")
+          {
+            std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (s,n) : string*N, (s,n) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> N"});
+          }
+
+          SECTION("string -> Z")
+          {
+            std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (z,n) : Z*N, (z,n) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> Z"});
+          }
+
+          SECTION("string -> R")
+          {
+            std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (s,r) : string*R, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R"});
+          }
+
+          SECTION("string -> R^1")
+          {
+            std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (s,r) : string*R^1, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R^1"});
+          }
+
+          SECTION("string -> R^2")
+          {
+            std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (s,r) : string*R^2, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R^2"});
+          }
+
+          SECTION("string -> R^3")
+          {
+            std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (s,r) : string*R^3, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R^3"});
+          }
+
+          SECTION("string -> R^1x1")
+          {
+            std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (s,r) : string*R^1x1, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R^1x1"});
+          }
+
+          SECTION("string -> R^2x2")
+          {
+            std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (s,r) : string*R^2x2, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R^2x2"});
+          }
+
+          SECTION("string -> R^3x3")
+          {
+            std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (s,r) : string*R^3x3, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> R^3x3"});
+          }
+
+          SECTION("string -> builtin_t")
+          {
+            std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (s,r) : string*builtin_t, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: string -> builtin_t"});
+          }
+        }
+
+        SECTION("from (builtin_t)")
+        {
+          SECTION("builtin_t -> B")
+          {
+            std::string_view data = R"(
+let bt:builtin_t, bt = bt;
+let t:(builtin_t), t = (bt, bt);
+let (s,b) : builtin_t*B, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: builtin_t -> B"});
+          }
+
+          SECTION("builtin_t -> N")
+          {
+            std::string_view data = R"(
+let bt:builtin_t, bt = bt;
+let t:(builtin_t), t = (bt, bt);
+let (s,n) : builtin_t*N, (s,n) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: builtin_t -> N"});
+          }
+
+          SECTION("builtin_t -> Z")
+          {
+            std::string_view data = R"(
+let bt:builtin_t, bt = bt;
+let t:(builtin_t), t = (bt, bt);
+let (z,n) : Z*N, (z,n) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: builtin_t -> Z"});
+          }
+
+          SECTION("builtin_t -> R")
+          {
+            std::string_view data = R"(
+let bt:builtin_t, bt = bt;
+let t:(builtin_t), t = (bt, bt);
+let (s,r) : builtin_t*R, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: builtin_t -> R"});
+          }
+
+          SECTION("builtin_t -> R^1")
+          {
+            std::string_view data = R"(
+let bt:builtin_t, bt = bt;
+let t:(builtin_t), t = (bt, bt);
+let (s,r) : builtin_t*R^1, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: builtin_t -> R^1"});
+          }
+
+          SECTION("builtin_t -> R^2")
+          {
+            std::string_view data = R"(
+let bt:builtin_t, bt = bt;
+let t:(builtin_t), t = (bt, bt);
+let (s,r) : builtin_t*R^2, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: builtin_t -> R^2"});
+          }
+
+          SECTION("builtin_t -> R^3")
+          {
+            std::string_view data = R"(
+let bt:builtin_t, bt = bt;
+let t:(builtin_t), t = (bt, bt);
+let (s,r) : builtin_t*R^3, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: builtin_t -> R^3"});
+          }
+
+          SECTION("builtin_t -> R^1x1")
+          {
+            std::string_view data = R"(
+let bt:builtin_t, bt = bt;
+let t:(builtin_t), t = (bt, bt);
+let (s,r) : builtin_t*R^1x1, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: builtin_t -> R^1x1"});
+          }
+
+          SECTION("builtin_t -> R^2x2")
+          {
+            std::string_view data = R"(
+let bt:builtin_t, bt = bt;
+let t:(builtin_t), t = (bt, bt);
+let (s,r) : builtin_t*R^2x2, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: builtin_t -> R^2x2"});
+          }
+
+          SECTION("builtin_t -> R^3x3")
+          {
+            std::string_view data = R"(
+let bt:builtin_t, bt = bt;
+let t:(builtin_t), t = (bt, bt);
+let (s,r) : builtin_t*R^3x3, (s,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: builtin_t -> R^3x3"});
+          }
+        }
+
+        SECTION("from (B)")
+        {
+          SECTION("B -> R^2")
+          {
+            std::string_view data = R"(
+let t:(B), t = (true, false);
+let (r,b) : R^2*B, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2"});
+          }
+
+          SECTION("B -> R^3")
+          {
+            std::string_view data = R"(
+let t:(B), t = (true, false);
+let (r,b) : R^3*B, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3"});
+          }
+
+          SECTION("B -> R^2x2")
+          {
+            std::string_view data = R"(
+let t:(B), t = (true, false);
+let (r,b) : R^2x2*B, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^2x2"});
+          }
+
+          SECTION("B -> R^3x3")
+          {
+            std::string_view data = R"(
+let t:(B), t = (true, false);
+let (r,b) : R^3x3*B, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> R^3x3"});
+          }
+
+          SECTION("B -> builtin_t")
+          {
+            std::string_view data = R"(
+let t:(B), t = (true, false);
+let (s,b) : string*builtin_t, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: B -> builtin_t"});
+          }
+        }
+
+        SECTION("from (N)")
+        {
+          SECTION("N -> B")
+          {
+            std::string_view data = R"(
+let t:(N), t = (1, 3);
+let (r,b) : B*N, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> B"});
+          }
+
+          SECTION("N -> R^2")
+          {
+            std::string_view data = R"(
+let t:(N), t = (1, 3);
+let (r,b) : R^2*N, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2"});
+          }
+
+          SECTION("N -> R^3")
+          {
+            std::string_view data = R"(
+let t:(N), t = (1, 3);
+let (r,b) : R^3*N, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3"});
+          }
+
+          SECTION("N -> R^2x2")
+          {
+            std::string_view data = R"(
+let t:(N), t = (1, 3);
+let (r,b) : R^2x2*N, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^2x2"});
+          }
+
+          SECTION("N -> R^3x3")
+          {
+            std::string_view data = R"(
+let t:(N), t = (1, 3);
+let (r,b) : R^3x3*N, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> R^3x3"});
+          }
+
+          SECTION("N -> builtin_t")
+          {
+            std::string_view data = R"(
+let t:(N), t = (1, 3);
+let (s,b) : string*builtin_t, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: N -> builtin_t"});
+          }
+        }
+
+        SECTION("from (Z)")
+        {
+          SECTION("Z -> B")
+          {
+            std::string_view data = R"(
+let t:(Z), t = (1, 3);
+let (r,b) : B*Z, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> B"});
+          }
+
+          SECTION("Z -> R^2")
+          {
+            std::string_view data = R"(
+let t:(Z), t = (1, 3);
+let (r,b) : R^2*Z, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2"});
+          }
+
+          SECTION("Z -> R^3")
+          {
+            std::string_view data = R"(
+let t:(Z), t = (1, 3);
+let (r,b) : R^3*Z, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3"});
+          }
+
+          SECTION("Z -> R^2x2")
+          {
+            std::string_view data = R"(
+let t:(Z), t = (1, 3);
+let (r,b) : R^2x2*Z, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^2x2"});
+          }
+
+          SECTION("Z -> R^3x3")
+          {
+            std::string_view data = R"(
+let t:(Z), t = (1, 3);
+let (r,b) : R^3x3*Z, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> R^3x3"});
+          }
+
+          SECTION("Z -> builtin_t")
+          {
+            std::string_view data = R"(
+let t:(Z), t = (1, 3);
+let (s,b) : string*builtin_t, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: Z -> builtin_t"});
+          }
+        }
+
+        SECTION("from (R)")
+        {
+          SECTION("R -> B")
+          {
+            std::string_view data = R"(
+let t:(R), t = (1.2, 3.2);
+let (r,b) : R*B, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> B"});
+          }
+
+          SECTION("R -> N")
+          {
+            std::string_view data = R"(
+let t:(R), t = (1.2, 3.2);
+let (r,n) : R*N, (r,n) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> N"});
+          }
+
+          SECTION("R -> Z")
+          {
+            std::string_view data = R"(
+let t:(R), t = (1.2, 3.2);
+let (r,z) : R*Z, (r,z) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> Z"});
+          }
+
+          SECTION("R -> R^2")
+          {
+            std::string_view data = R"(
+let t:(R), t = (1.2, 3.2);
+let (x,r) : R^2*R, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2"});
+          }
+
+          SECTION("R -> R^3")
+          {
+            std::string_view data = R"(
+let t:(R), t = (1.2, 3.2);
+let (x,r) : R^3*R, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3"});
+          }
+
+          SECTION("R -> R^2x2")
+          {
+            std::string_view data = R"(
+let t:(R), t = (1.2, 3.2);
+let (x,r) : R^2x2*R, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^2x2"});
+          }
+
+          SECTION("R -> R^3x3")
+          {
+            std::string_view data = R"(
+let t:(R), t = (1.2, 3.2);
+let (x,r) : R^3x3*R, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> R^3x3"});
+          }
+
+          SECTION("R -> builtin_t")
+          {
+            std::string_view data = R"(
+let t:(R), t = (1.2, 3.2);
+let (s,b) : string*builtin_t, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R -> builtin_t"});
+          }
+        }
+
+        SECTION("from (R^1)")
+        {
+          SECTION("R^1 -> B")
+          {
+            std::string_view data = R"(
+let t:(R^1), t = (1.2, 3.2);
+let (r,b) : R^1*B, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> B"});
+          }
+
+          SECTION("R^1 -> N")
+          {
+            std::string_view data = R"(
+let t:(R^1), t = (1.2, 3.2);
+let (r,n) : R^1*N, (r,n) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> N"});
+          }
+
+          SECTION("R^1 -> Z")
+          {
+            std::string_view data = R"(
+let t:(R^1), t = (1.2, 3.2);
+let (r,z) : R^1*Z, (r,z) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> Z"});
+          }
+
+          SECTION("R^1 -> R")
+          {
+            std::string_view data = R"(
+let t:(R^1), t = (1.2, 3.2);
+let (r,x) : R^1*R, (r,x) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R"});
+          }
+
+          SECTION("R^1 -> R^2")
+          {
+            std::string_view data = R"(
+let t:(R^1), t = (1.2, 3.2);
+let (x,r) : R^2*R^1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^2"});
+          }
+
+          SECTION("R^1 -> R^3")
+          {
+            std::string_view data = R"(
+let t:(R^1), t = (1.2, 3.2);
+let (x,r) : R^3*R^1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^3"});
+          }
+
+          SECTION("R^1 -> R^1x1")
+          {
+            std::string_view data = R"(
+let t:(R^1), t = (1.2, 3.2);
+let (x,r) : R^1x1*R^1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^1x1"});
+          }
+
+          SECTION("R^1 -> R^2x2")
+          {
+            std::string_view data = R"(
+let t:(R^1), t = (1.2, 3.2);
+let (x,r) : R^2x2*R^1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^2x2"});
+          }
+
+          SECTION("R^1 -> R^3x3")
+          {
+            std::string_view data = R"(
+let t:(R^1), t = (1.2, 3.2);
+let (x,r) : R^3x3*R^1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> R^3x3"});
+          }
+
+          SECTION("R^1 -> builtin_t")
+          {
+            std::string_view data = R"(
+let t:(R^1), t = (1.2, 3.2);
+let (s,b) : string*builtin_t, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1 -> builtin_t"});
+          }
+        }
+
+        SECTION("from (R^2)")
+        {
+          SECTION("R^2 -> B")
+          {
+            std::string_view data = R"(
+let t:(R^2), t = ([1.2, 3.2], 0);
+let (r,b) : R^2*B, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> B"});
+          }
+
+          SECTION("R^2 -> N")
+          {
+            std::string_view data = R"(
+let t:(R^2), t = ([1.2, 3.2], 0);
+let (r,n) : R^2*N, (r,n) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> N"});
+          }
+
+          SECTION("R^2 -> Z")
+          {
+            std::string_view data = R"(
+let t:(R^2), t = ([1.2, 3.2], 0);
+let (r,z) : R^2*Z, (r,z) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> Z"});
+          }
+
+          SECTION("R^2 -> R")
+          {
+            std::string_view data = R"(
+let t:(R^2), t = ([1.2, 3.2], 0);
+let (r,x) : R^2*R, (r,x) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R"});
+          }
+
+          SECTION("R^2 -> R^1")
+          {
+            std::string_view data = R"(
+let t:(R^2), t = ([1.2, 3.2], 0);
+let (x,r) : R^2*R^1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R^1"});
+          }
+
+          SECTION("R^2 -> R^3")
+          {
+            std::string_view data = R"(
+let t:(R^2), t = ([1.2, 3.2], 0);
+let (x,r) : R^3*R^2, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R^3"});
+          }
+
+          SECTION("R^2 -> R^1x1")
+          {
+            std::string_view data = R"(
+let t:(R^2), t = ([1.2, 3.2], 0);
+let (x,r) : R^1x1*R^1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R^1x1"});
+          }
+
+          SECTION("R^2 -> R^2x2")
+          {
+            std::string_view data = R"(
+let t:(R^2), t = ([1.2, 3.2], 0);
+let (x,r) : R^2x2*R^2, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R^2x2"});
+          }
+
+          SECTION("R^2 -> R^3x3")
+          {
+            std::string_view data = R"(
+let t:(R^2), t = ([1.2, 3.2], 0);
+let (x,r) : R^3x3*R^2, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> R^3x3"});
+          }
+
+          SECTION("R^2 -> builtin_t")
+          {
+            std::string_view data = R"(
+let t:(R^2), t = ([1.2, 3.2], 0);
+let (s,b) : string*builtin_t, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2 -> builtin_t"});
+          }
+        }
+
+        SECTION("from (R^3)")
+        {
+          SECTION("R^3 -> B")
+          {
+            std::string_view data = R"(
+let t:(R^3), t = ([1.2, 3.2, 1], 0);
+let (r,b) : R^3*B, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3 -> B"});
+          }
+
+          SECTION("R^3 -> N")
+          {
+            std::string_view data = R"(
+let t:(R^3), t = ([1.2, 3.2, 1], 0);
+let (r,n) : R^3*N, (r,n) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3 -> N"});
+          }
+
+          SECTION("R^3 -> Z")
+          {
+            std::string_view data = R"(
+let t:(R^3), t = ([1.2, 3.2, 1], 0);
+let (r,z) : R^3*Z, (r,z) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3 -> Z"});
+          }
+
+          SECTION("R^3 -> R")
+          {
+            std::string_view data = R"(
+let t:(R^3), t = ([1.2, 3.2, 1], 0);
+let (r,x) : R^3*R, (r,x) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3 -> R"});
+          }
+
+          SECTION("R^3 -> R^1")
+          {
+            std::string_view data = R"(
+let t:(R^3), t = ([1.2, 3.2, 1], 0);
+let (x,r) : R^3*R^1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3 -> R^1"});
+          }
+
+          SECTION("R^3 -> R^2")
+          {
+            std::string_view data = R"(
+let t:(R^3), t = ([1.2, 3.2, 1], 0);
+let (x,r) : R^3*R^2, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3 -> R^2"});
+          }
+
+          SECTION("R^3 -> R^1x1")
+          {
+            std::string_view data = R"(
+let t:(R^3), t = ([1.2, 3.2, 1], 0);
+let (x,r) : R^1x1*R^3, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3 -> R^1x1"});
+          }
+
+          SECTION("R^3 -> R^2x2")
+          {
+            std::string_view data = R"(
+let t:(R^3), t = ([1.2, 3.2, 1], 0);
+let (x,r) : R^2x2*R^3, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3 -> R^2x2"});
+          }
+
+          SECTION("R^3 -> R^3x3")
+          {
+            std::string_view data = R"(
+let t:(R^3), t = ([1.2, 3.2, 1], 0);
+let (x,r) : R^3x3*R^2, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3 -> R^3x3"});
+          }
+
+          SECTION("R^3 -> builtin_t")
+          {
+            std::string_view data = R"(
+let t:(R^3), t = ([1.2, 3.2, 1], 0);
+let (s,b) : string*builtin_t, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3 -> builtin_t"});
+          }
+        }
+
+        SECTION("from (R^1x1)")
+        {
+          SECTION("R^1x1 -> B")
+          {
+            std::string_view data = R"(
+let t:(R^1x1), t = (1.2, 3.2);
+let (r,b) : R^1x1*B, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1x1 -> B"});
+          }
+
+          SECTION("R^1x1 -> N")
+          {
+            std::string_view data = R"(
+let t:(R^1x1), t = (1.2, 3.2);
+let (r,n) : R^1x1*N, (r,n) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1x1 -> N"});
+          }
+
+          SECTION("R^1x1 -> Z")
+          {
+            std::string_view data = R"(
+let t:(R^1x1), t = (1.2, 3.2);
+let (r,z) : R^1x1*Z, (r,z) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1x1 -> Z"});
+          }
+
+          SECTION("R^1x1 -> R")
+          {
+            std::string_view data = R"(
+let t:(R^1x1), t = (1.2, 3.2);
+let (r,x) : R^1x1*R, (r,x) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1x1 -> R"});
+          }
+
+          SECTION("R^1x1 -> R^2")
+          {
+            std::string_view data = R"(
+let t:(R^1x1), t = (1.2, 3.2);
+let (x,r) : R^2*R^1x1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1x1 -> R^2"});
+          }
+
+          SECTION("R^1x1 -> R^3")
+          {
+            std::string_view data = R"(
+let t:(R^1x1), t = (1.2, 3.2);
+let (x,r) : R^3*R^1x1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1x1 -> R^3"});
+          }
+
+          SECTION("R^1x1 -> R^1")
+          {
+            std::string_view data = R"(
+let t:(R^1x1), t = (1.2, 3.2);
+let (x,r) : R^1x1*R^1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1x1 -> R^1"});
+          }
+
+          SECTION("R^1x1 -> R^2x2")
+          {
+            std::string_view data = R"(
+let t:(R^1x1), t = (1.2, 3.2);
+let (x,r) : R^2x2*R^1x1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1x1 -> R^2x2"});
+          }
+
+          SECTION("R^1x1 -> R^3x3")
+          {
+            std::string_view data = R"(
+let t:(R^1x1), t = (1.2, 3.2);
+let (x,r) : R^3x3*R^1x1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1x1 -> R^3x3"});
+          }
+
+          SECTION("R^1x1 -> builtin_t")
+          {
+            std::string_view data = R"(
+let t:(R^1x1), t = (1.2, 3.2);
+let (s,b) : string*builtin_t, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^1x1 -> builtin_t"});
+          }
+        }
+
+        SECTION("from (R^2x2)")
+        {
+          SECTION("R^2x2 -> B")
+          {
+            std::string_view data = R"(
+let t:(R^2x2), t = ([[1.2, 3.2],[2.1, -3.1]], 0);
+let (r,b) : R^2x2*B, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2x2 -> B"});
+          }
+
+          SECTION("R^2x2 -> N")
+          {
+            std::string_view data = R"(
+let t:(R^2x2), t = ([[1.2, 3.2],[2.1, -3.1]], 0);
+let (r,n) : R^2x2*N, (r,n) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2x2 -> N"});
+          }
+
+          SECTION("R^2x2 -> Z")
+          {
+            std::string_view data = R"(
+let t:(R^2x2), t = ([[1.2, 3.2],[2.1, -3.1]], 0);
+let (r,z) : R^2x2*Z, (r,z) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2x2 -> Z"});
+          }
+
+          SECTION("R^2x2 -> R")
+          {
+            std::string_view data = R"(
+let t:(R^2x2), t = ([[1.2, 3.2],[2.1, -3.1]], 0);
+let (r,x) : R^2x2*R, (r,x) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2x2 -> R"});
+          }
+
+          SECTION("R^2x2 -> R^2")
+          {
+            std::string_view data = R"(
+let t:(R^2x2), t = ([[1.2, 3.2],[2.1, -3.1]], 0);
+let (x,r) : R^2*R^2x2, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2x2 -> R^2"});
+          }
+
+          SECTION("R^2x2 -> R^3")
+          {
+            std::string_view data = R"(
+let t:(R^2x2), t = ([[1.2, 3.2],[2.1, -3.1]], 0);
+let (x,r) : R^3*R^2x2, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2x2 -> R^3"});
+          }
+
+          SECTION("R^2x2 -> R^1")
+          {
+            std::string_view data = R"(
+let t:(R^2x2), t = ([[1.2, 3.2],[2.1, -3.1]], 0);
+let (x,r) : R^2x2*R^1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2x2 -> R^1"});
+          }
+
+          SECTION("R^2x2 -> R^1x1")
+          {
+            std::string_view data = R"(
+let t:(R^2x2), t = ([[1.2, 3.2],[2.1, -3.1]], 0);
+let (x,r) : R^2x2*R^1x1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2x2 -> R^1x1"});
+          }
+
+          SECTION("R^2x2 -> R^3x3")
+          {
+            std::string_view data = R"(
+let t:(R^2x2), t = ([[1.2, 3.2],[2.1, -3.1]], 0);
+let (x,r) : R^3x3*R^2x2, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2x2 -> R^3x3"});
+          }
+
+          SECTION("R^2x2 -> builtin_t")
+          {
+            std::string_view data = R"(
+let t:(R^2x2), t = ([[1.2, 3.2],[2.1, -3.1]], 0);
+let (s,b) : string*builtin_t, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^2x2 -> builtin_t"});
+          }
+        }
+
+        SECTION("from (R^3x3)")
+        {
+          SECTION("R^3x3 -> B")
+          {
+            std::string_view data = R"(
+let t:(R^3x3), t = ([[1.2, 3.2, 0],[1, 2.1, -3.1],[3, -1, 7]], 0);
+let (r,b) : R^3x3*B, (r,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3x3 -> B"});
+          }
+
+          SECTION("R^3x3 -> N")
+          {
+            std::string_view data = R"(
+let t:(R^3x3), t = ([[1.2, 3.2, 0],[1, 2.1, -3.1],[3, -1, 7]], 0);
+let (r,n) : R^3x3*N, (r,n) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3x3 -> N"});
+          }
+
+          SECTION("R^3x3 -> Z")
+          {
+            std::string_view data = R"(
+let t:(R^3x3), t = ([[1.2, 3.2, 0],[1, 2.1, -3.1],[3, -1, 7]], 0);
+let (r,z) : R^3x3*Z, (r,z) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3x3 -> Z"});
+          }
+
+          SECTION("R^3x3 -> R")
+          {
+            std::string_view data = R"(
+let t:(R^3x3), t = ([[1.2, 3.2, 0],[1, 2.1, -3.1],[3, -1, 7]], 0);
+let (r,x) : R^3x3*R, (r,x) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3x3 -> R"});
+          }
+
+          SECTION("R^3x3 -> R^2")
+          {
+            std::string_view data = R"(
+let t:(R^3x3), t = ([[1.2, 3.2, 0],[1, 2.1, -3.1],[3, -1, 7]], 0);
+let (x,r) : R^2*R^3x3, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3x3 -> R^2"});
+          }
+
+          SECTION("R^3x3 -> R^3")
+          {
+            std::string_view data = R"(
+let t:(R^3x3), t = ([[1.2, 3.2, 0],[1, 2.1, -3.1],[3, -1, 7]], 0);
+let (x,r) : R^3*R^3x3, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3x3 -> R^3"});
+          }
+
+          SECTION("R^3x3 -> R^1")
+          {
+            std::string_view data = R"(
+let t:(R^3x3), t = ([[1.2, 3.2, 0],[1, 2.1, -3.1],[3, -1, 7]], 0);
+let (x,r) : R^3x3*R^1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3x3 -> R^1"});
+          }
+
+          SECTION("R^3x3 -> R^1x1")
+          {
+            std::string_view data = R"(
+let t:(R^3x3), t = ([[1.2, 3.2, 0],[1, 2.1, -3.1],[3, -1, 7]], 0);
+let (x,r) : R^3x3*R^1x1, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3x3 -> R^1x1"});
+          }
+
+          SECTION("R^3x3 -> R^2x2")
+          {
+            std::string_view data = R"(
+let t:(R^3x3), t = ([[1.2, 3.2, 0],[1, 2.1, -3.1],[3, -1, 7]], 0);
+let (x,r) : R^3x3*R^2x2, (x,r) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3x3 -> R^2x2"});
+          }
+
+          SECTION("R^3x3 -> builtin_t")
+          {
+            std::string_view data = R"(
+let t:(R^3x3), t = ([[1.2, 3.2, 0],[1, 2.1, -3.1],[3, -1, 7]], 0);
+let (s,b) : string*builtin_t, (s,b) = t;
+)";
+
+            CHECK_AST_THROWS_WITH(data, std::string{"invalid implicit conversion: R^3x3 -> builtin_t"});
+          }
+        }
+      }
+
+      SECTION("tuple -> list with tuple")
+      {
+        std::string_view data = R"(
+let t:(N), t = (1, 2, 3, 4);
+let (a,b,c,d) : R*N*(R)*Z, (a,b,c,d) = t;
+)";
+
+        CHECK_AST_THROWS_WITH(data, std::string{"cannot affect a tuple to a compound type made of tuples"});
+      }
     }
   }
 }
diff --git a/tests/test_AffectationProcessor.cpp b/tests/test_AffectationProcessor.cpp
index 4314b3b96165e200a528378a2e1053be27346e87..71670fd5d75b4ae7d20d8ece4b3ef980165cd382 100644
--- a/tests/test_AffectationProcessor.cpp
+++ b/tests/test_AffectationProcessor.cpp
@@ -10,7 +10,13 @@
 #include <language/ast/ASTNodeTypeCleaner.hpp>
 #include <language/ast/ASTSymbolTableBuilder.hpp>
 #include <language/utils/ASTPrinter.hpp>
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/TypeDescriptor.hpp>
 #include <utils/Demangle.hpp>
+#include <utils/Stringify.hpp>
+
+#include <FixturesForBuiltinT.hpp>
+#include <language/utils/BasicAffectationRegistrerFor.hpp>
 
 #include <pegtl/string_input.hpp>
 
@@ -46,6 +52,66 @@
     REQUIRE(value == expected_value);                                         \
   }
 
+#define CHECK_BUILTIN_AFFECTATION_RESULT(data, variable_name, expected_value)                                   \
+  {                                                                                                             \
+    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};                                                  \
+    auto ast = ASTBuilder::build(input);                                                                        \
+                                                                                                                \
+    ASTModulesImporter{*ast};                                                                                   \
+                                                                                                                \
+    BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")}; \
+                                                                                                                \
+    SymbolTable& symbol_table = *ast->m_symbol_table;                                                           \
+    auto [i_symbol, success]  = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin());               \
+    if (not success) {                                                                                          \
+      throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing");          \
+    }                                                                                                           \
+                                                                                                                \
+    i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>());              \
+    i_symbol->attributes().setIsInitialized();                                                                  \
+    i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size();                                   \
+    symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId()));   \
+                                                                                                                \
+    auto [i_symbol_bt_a, success_bt_a] = symbol_table.add("bt_a", ast->begin());                                \
+    if (not success_bt_a) {                                                                                     \
+      throw UnexpectedError("cannot add 'bt_a' of type builtin_t for testing");                                 \
+    }                                                                                                           \
+    i_symbol_bt_a->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>);            \
+    i_symbol_bt_a->attributes().setIsInitialized();                                                             \
+    i_symbol_bt_a->attributes().value() =                                                                       \
+      EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(3.2)));                 \
+                                                                                                                \
+    auto [i_symbol_bt_b, success_bt_b] = symbol_table.add("bt_b", ast->begin());                                \
+    if (not success_bt_b) {                                                                                     \
+      throw UnexpectedError("cannot add 'bt_b' of type builtin_t for testing");                                 \
+    }                                                                                                           \
+    i_symbol_bt_b->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>);            \
+    i_symbol_bt_b->attributes().setIsInitialized();                                                             \
+    i_symbol_bt_b->attributes().value() =                                                                       \
+      EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(5.3)));                 \
+                                                                                                                \
+    ASTSymbolTableBuilder{*ast};                                                                                \
+    ASTNodeDataTypeBuilder{*ast};                                                                               \
+                                                                                                                \
+    ASTNodeDeclarationToAffectationConverter{*ast};                                                             \
+    ASTNodeTypeCleaner<language::var_declaration>{*ast};                                                        \
+                                                                                                                \
+    ASTNodeExpressionBuilder{*ast};                                                                             \
+    ExecutionPolicy exec_policy;                                                                                \
+    ast->execute(exec_policy);                                                                                  \
+                                                                                                                \
+    using namespace TAO_PEGTL_NAMESPACE;                                                                        \
+    position use_position{internal::iterator{"fixture"}, "fixture"};                                            \
+    use_position.byte    = 10000;                                                                               \
+    auto [symbol, found] = symbol_table.find(variable_name, use_position);                                      \
+                                                                                                                \
+    auto attributes     = symbol->attributes();                                                                 \
+    auto embedded_value = std::get<EmbeddedData>(attributes.value());                                           \
+                                                                                                                \
+    double value = *dynamic_cast<const DataHandler<const double>&>(embedded_value.get()).data_ptr();            \
+    REQUIRE(value == expected);                                                                                 \
+  }
+
 #define CHECK_AFFECTATION_THROWS_WITH(data, error_message)              \
   {                                                                     \
     TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};          \
@@ -93,91 +159,396 @@ TEST_CASE("AffectationProcessor", "[language]")
 {
   SECTION("Affectations")
   {
-    SECTION("B")
+    SECTION("from value")
     {
-      CHECK_AFFECTATION_RESULT("let b : B; b = true;", "b", true);
-    }
+      SECTION("-> B")
+      {
+        CHECK_AFFECTATION_RESULT("let b : B; b = true;", "b", true);
+      }
 
-    SECTION("N")
-    {
-      CHECK_AFFECTATION_RESULT("let n : N, n = 1;", "n", 1ul);
-      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let n : N, n = m;", "n", 2ul);
-      CHECK_AFFECTATION_RESULT("let n : N, n = true;", "n", 1ul);
-      CHECK_AFFECTATION_RESULT("let n : N, n = false;", "n", 0ul);
-    }
+      SECTION("-> N")
+      {
+        CHECK_AFFECTATION_RESULT("let n : N, n = 1;", "n", 1ul);
+        CHECK_AFFECTATION_RESULT("let m : N, m = 2; let n : N, n = m;", "n", 2ul);
+        CHECK_AFFECTATION_RESULT("let n : N, n = true;", "n", 1ul);
+        CHECK_AFFECTATION_RESULT("let n : N, n = false;", "n", 0ul);
+      }
 
-    SECTION("Z")
-    {
-      CHECK_AFFECTATION_RESULT("let z : Z, z = -1;", "z", -1l);
-      CHECK_AFFECTATION_RESULT("let z : Z, z = true;", "z", 1l);
-      CHECK_AFFECTATION_RESULT("let z : Z, z = false;", "z", 0l);
-    }
+      SECTION("-> Z")
+      {
+        CHECK_AFFECTATION_RESULT("let z : Z, z = -1;", "z", -1l);
+        CHECK_AFFECTATION_RESULT("let z : Z, z = true;", "z", 1l);
+        CHECK_AFFECTATION_RESULT("let z : Z, z = false;", "z", 0l);
+      }
 
-    SECTION("R")
-    {
-      CHECK_AFFECTATION_RESULT("let r : R, r = -1;", "r", double{-1});
-      CHECK_AFFECTATION_RESULT("let r : R, r = true;", "r", double{1});
-      CHECK_AFFECTATION_RESULT("let r : R, r = false;", "r", double{0});
-      CHECK_AFFECTATION_RESULT("let r : R, r = -2.3;", "r", double{-2.3});
-    }
+      SECTION("-> R")
+      {
+        CHECK_AFFECTATION_RESULT("let r : R, r = -1;", "r", double{-1});
+        CHECK_AFFECTATION_RESULT("let r : R, r = true;", "r", double{1});
+        CHECK_AFFECTATION_RESULT("let r : R, r = false;", "r", double{0});
+        CHECK_AFFECTATION_RESULT("let r : R, r = -2.3;", "r", double{-2.3});
+      }
 
-    SECTION("R^1")
-    {
-      CHECK_AFFECTATION_RESULT("let x : R^1, x = -1;", "x", (TinyVector<1>{-1}));
-      CHECK_AFFECTATION_RESULT("let x : R^1, x = true;", "x", (TinyVector<1>{true}));
-      CHECK_AFFECTATION_RESULT("let x : R^1, x = false;", "x", (TinyVector<1>{false}));
-      CHECK_AFFECTATION_RESULT("let x : R^1, x = -2.3;", "x", (TinyVector<1>{-2.3}));
-      CHECK_AFFECTATION_RESULT("let x : R^1, x = [-1];", "x", (TinyVector<1>{-1}));
-      CHECK_AFFECTATION_RESULT("let x : R^1, x = [true];", "x", (TinyVector<1>{true}));
-      CHECK_AFFECTATION_RESULT("let x : R^1, x = [false];", "x", (TinyVector<1>{false}));
-      CHECK_AFFECTATION_RESULT("let x : R^1, x = [-2.3];", "x", (TinyVector<1>{-2.3}));
-      CHECK_AFFECTATION_RESULT("let x : R^1, x = 0;", "x", (TinyVector<1>{zero}));
-    }
+      SECTION("-> R^1")
+      {
+        CHECK_AFFECTATION_RESULT("let x : R^1, x = -1;", "x", (TinyVector<1>{-1}));
+        CHECK_AFFECTATION_RESULT("let x : R^1, x = true;", "x", (TinyVector<1>{true}));
+        CHECK_AFFECTATION_RESULT("let x : R^1, x = false;", "x", (TinyVector<1>{false}));
+        CHECK_AFFECTATION_RESULT("let x : R^1, x = -2.3;", "x", (TinyVector<1>{-2.3}));
+        CHECK_AFFECTATION_RESULT("let x : R^1, x = [-1];", "x", (TinyVector<1>{-1}));
+        CHECK_AFFECTATION_RESULT("let x : R^1, x = [true];", "x", (TinyVector<1>{true}));
+        CHECK_AFFECTATION_RESULT("let x : R^1, x = [false];", "x", (TinyVector<1>{false}));
+        CHECK_AFFECTATION_RESULT("let x : R^1, x = [-2.3];", "x", (TinyVector<1>{-2.3}));
+        CHECK_AFFECTATION_RESULT("let x : R^1, x = 0;", "x", (TinyVector<1>{zero}));
+      }
 
-    SECTION("R^2")
-    {
-      CHECK_AFFECTATION_RESULT("let x : R^2, x = [-1, true];", "x", (TinyVector<2>{-1, true}));
-      CHECK_AFFECTATION_RESULT("let x : R^2, x = [true, false];", "x", (TinyVector<2>{true, false}));
-      CHECK_AFFECTATION_RESULT("let x : R^2, x = [-0.3, 12];", "x", (TinyVector<2>{-0.3, 12}));
-      CHECK_AFFECTATION_RESULT("let x : R^2, x = 0;", "x", (TinyVector<2>{zero}));
-    }
+      SECTION("-> R^2")
+      {
+        CHECK_AFFECTATION_RESULT("let x : R^2, x = [-1, true];", "x", (TinyVector<2>{-1, true}));
+        CHECK_AFFECTATION_RESULT("let x : R^2, x = [true, false];", "x", (TinyVector<2>{true, false}));
+        CHECK_AFFECTATION_RESULT("let x : R^2, x = [-0.3, 12];", "x", (TinyVector<2>{-0.3, 12}));
+        CHECK_AFFECTATION_RESULT("let x : R^2, x = 0;", "x", (TinyVector<2>{zero}));
+      }
 
-    SECTION("R^3")
-    {
-      CHECK_AFFECTATION_RESULT("let x : R^3, x = [-1, true, false];", "x", (TinyVector<3>{-1, true, false}));
-      CHECK_AFFECTATION_RESULT("let x : R^3, x = [-0.3, 12, 6.2];", "x", (TinyVector<3>{-0.3, 12, 6.2}));
-      CHECK_AFFECTATION_RESULT("let x : R^3; x = 0;", "x", (TinyVector<3>{zero}));
-    }
+      SECTION("-> R^3")
+      {
+        CHECK_AFFECTATION_RESULT("let x : R^3, x = [-1, true, false];", "x", (TinyVector<3>{-1, true, false}));
+        CHECK_AFFECTATION_RESULT("let x : R^3, x = [-0.3, 12, 6.2];", "x", (TinyVector<3>{-0.3, 12, 6.2}));
+        CHECK_AFFECTATION_RESULT("let x : R^3; x = 0;", "x", (TinyVector<3>{zero}));
+      }
 
-    SECTION("R^1x1")
-    {
-      CHECK_AFFECTATION_RESULT("let x : R^1x1, x = -1;", "x", (TinyMatrix<1>{-1}));
-      CHECK_AFFECTATION_RESULT("let x : R^1x1, x = true;", "x", (TinyMatrix<1>{true}));
-      CHECK_AFFECTATION_RESULT("let x : R^1x1, x = false;", "x", (TinyMatrix<1>{false}));
-      CHECK_AFFECTATION_RESULT("let x : R^1x1, x = -2.3;", "x", (TinyMatrix<1>{-2.3}));
-      CHECK_AFFECTATION_RESULT("let x : R^1x1, x = [[-1]];", "x", (TinyMatrix<1>{-1}));
-      CHECK_AFFECTATION_RESULT("let x : R^1x1, x = [[true]];", "x", (TinyMatrix<1>{true}));
-      CHECK_AFFECTATION_RESULT("let x : R^1x1, x = [[false]];", "x", (TinyMatrix<1>{false}));
-      CHECK_AFFECTATION_RESULT("let x : R^1x1, x = [[-2.3]];", "x", (TinyMatrix<1>{-2.3}));
-      CHECK_AFFECTATION_RESULT("let x : R^1x1; x = 0;", "x", (TinyMatrix<1>{zero}));
-    }
+      SECTION("-> R^1x1")
+      {
+        CHECK_AFFECTATION_RESULT("let x : R^1x1, x = -1;", "x", (TinyMatrix<1>{-1}));
+        CHECK_AFFECTATION_RESULT("let x : R^1x1, x = true;", "x", (TinyMatrix<1>{true}));
+        CHECK_AFFECTATION_RESULT("let x : R^1x1, x = false;", "x", (TinyMatrix<1>{false}));
+        CHECK_AFFECTATION_RESULT("let x : R^1x1, x = -2.3;", "x", (TinyMatrix<1>{-2.3}));
+        CHECK_AFFECTATION_RESULT("let x : R^1x1, x = [[-1]];", "x", (TinyMatrix<1>{-1}));
+        CHECK_AFFECTATION_RESULT("let x : R^1x1, x = [[true]];", "x", (TinyMatrix<1>{true}));
+        CHECK_AFFECTATION_RESULT("let x : R^1x1, x = [[false]];", "x", (TinyMatrix<1>{false}));
+        CHECK_AFFECTATION_RESULT("let x : R^1x1, x = [[-2.3]];", "x", (TinyMatrix<1>{-2.3}));
+        CHECK_AFFECTATION_RESULT("let x : R^1x1; x = 0;", "x", (TinyMatrix<1>{zero}));
+      }
 
-    SECTION("R^2x2")
-    {
-      CHECK_AFFECTATION_RESULT("let x : R^2x2, x = [[-1, true], [3, 5]];", "x", (TinyMatrix<2>{-1, true, 3, 5}));
-      CHECK_AFFECTATION_RESULT("let x : R^2x2, x = [[true, false], [1==2, 2==2]];", "x",
-                               (TinyMatrix<2>{true, false, false, true}));
-      CHECK_AFFECTATION_RESULT("let x : R^2x2, x = [[-0.3, 12],[2, -3]];", "x", (TinyMatrix<2>{-0.3, 12, 2, -3}));
-      CHECK_AFFECTATION_RESULT("let x : R^2x2, x = 0;", "x", (TinyMatrix<2>{zero}));
+      SECTION("-> R^2x2")
+      {
+        CHECK_AFFECTATION_RESULT("let x : R^2x2, x = [[-1, true], [3, 5]];", "x", (TinyMatrix<2>{-1, true, 3, 5}));
+        CHECK_AFFECTATION_RESULT("let x : R^2x2, x = [[true, false], [1==2, 2==2]];", "x",
+                                 (TinyMatrix<2>{true, false, false, true}));
+        CHECK_AFFECTATION_RESULT("let x : R^2x2, x = [[-0.3, 12],[2, -3]];", "x", (TinyMatrix<2>{-0.3, 12, 2, -3}));
+        CHECK_AFFECTATION_RESULT("let x : R^2x2, x = 0;", "x", (TinyMatrix<2>{zero}));
+      }
+
+      SECTION("-> R^3x3")
+      {
+        CHECK_AFFECTATION_RESULT("let x : R^3x3, x = [[-1, true, false], [2, 3.1, 4], [-1, true, 2]];", "x",
+                                 (TinyMatrix<3>{-1, true, false, 2, 3.1, 4, -1, true, 2}));
+        CHECK_AFFECTATION_RESULT("let x : R^3x3, x = [[-0.3, 12, 6.2], [7.1, 3.2, 2-3], [2, -1, 0]];", "x",
+                                 (TinyMatrix<3>{-0.3, 12, 6.2, 7.1, 3.2, 2 - 3, 2, -1, 0}));
+        CHECK_AFFECTATION_RESULT("let x : R^3x3, x = 0;", "x", (TinyMatrix<3>{zero}));
+      }
+
+      SECTION("-> string")
+      {
+        CHECK_AFFECTATION_RESULT("let x : string, x = true;", "x", (stringify(true)));
+        CHECK_AFFECTATION_RESULT("let n:N, n = 3; let x : string, x = n;", "x", (stringify(3ul)));
+        CHECK_AFFECTATION_RESULT("let x : string, x = -2;", "x", (stringify(-2l)));
+        CHECK_AFFECTATION_RESULT("let x : string, x = 1.7;", "x", (stringify(double{1.7})));
+
+        CHECK_AFFECTATION_RESULT("let x : string, x = [1.7];", "x", (stringify(TinyVector<1>{1.7})));
+        CHECK_AFFECTATION_RESULT("let x : string, x = [1.7, 2];", "x", (stringify(TinyVector<2>{1.7, 2})));
+        CHECK_AFFECTATION_RESULT("let x : string, x = [1.7, 5, 2];", "x", (stringify(TinyVector<3>{1.7, 5, 2})));
+
+        CHECK_AFFECTATION_RESULT("let x : string, x = [[1.7]];", "x", (stringify(TinyMatrix<1>{1.7})));
+        CHECK_AFFECTATION_RESULT("let x : string, x = [[1.7, 2],[1,3]];", "x",
+                                 (stringify(TinyMatrix<2>{1.7, 2, 1, 3})));
+        CHECK_AFFECTATION_RESULT("let x : string, x = [[1.7, 5, 2],[1,3,5],[-1,2,6]];", "x",
+                                 (stringify(TinyMatrix<3>{1.7, 5, 2, 1, 3, 5, -1, 2, 6})));
+
+        CHECK_AFFECTATION_RESULT("let x : string, x = \"foobar\";", "x", (std::string{"foobar"}));
+      }
+
+      SECTION("-> builtin_t")
+      {
+        const double expected = double{3.2};
+        CHECK_BUILTIN_AFFECTATION_RESULT("let b:builtin_t, b = bt_a;", "b", expected);
+      }
     }
 
-    SECTION("R^3x3")
+    SECTION("from tuple")
     {
-      CHECK_AFFECTATION_RESULT("let x : R^3x3, x = [[-1, true, false], [2, 3.1, 4], [-1, true, 2]];", "x",
-                               (TinyMatrix<3>{-1, true, false, 2, 3.1, 4, -1, true, 2}));
-      CHECK_AFFECTATION_RESULT("let x : R^3x3, x = [[-0.3, 12, 6.2], [7.1, 3.2, 2-3], [2, -1, 0]];", "x",
-                               (TinyMatrix<3>{-0.3, 12, 6.2, 7.1, 3.2, 2 - 3, 2, -1, 0}));
-      CHECK_AFFECTATION_RESULT("let x : R^3x3, x = 0;", "x", (TinyMatrix<3>{zero}));
+      SECTION("-> B")
+      {
+        CHECK_AFFECTATION_RESULT("let b : B; b = true;", "b", true);
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(B), t = true;
+let b:B, b = t;
+)"},
+                                 "b", true);
+      }
+
+      SECTION("-> N")
+      {
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(B), t = true;
+let n:N, n = t;
+)"},
+                                 "n", 1ul);
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(N), t = 1;
+let n:N, n = t;
+)"},
+                                 "n", 1ul);
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(Z), t = 2;
+let n:N, n = t;
+)"},
+                                 "n", 2ul);
+      }
+
+      SECTION("-> Z")
+      {
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(B), t = true;
+let z:Z, z = t;
+)"},
+                                 "z", 1l);
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(N), t = 2;
+let z:Z, z = t;
+)"},
+                                 "z", 2l);
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(Z), t = 3;
+let z:Z, z = t;
+)"},
+                                 "z", 3l);
+      }
+
+      SECTION("-> R")
+      {
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(B), t = true;
+let r:R, r = t;
+)"},
+                                 "r", double{1});
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(N), t = 2;
+let r:R, r = t;
+)"},
+                                 "r", double{2});
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(Z), t =-3;
+let r:R, r = t;
+)"},
+                                 "r", double{-3});
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R), t =-3.2;
+let r:R, r = t;
+)"},
+                                 "r", double{-3.2});
+      }
+
+      SECTION("-> R^1")
+      {
+        using R1x1 = TinyVector<1>;
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(B), t = true;
+let r:R^1, r = t;
+)"},
+                                 "r", R1x1{1});
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(N), t = 2;
+let r:R^1, r = t;
+)"},
+                                 "r", R1x1{2});
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(Z), t =-3;
+let r:R^1, r = t;
+)"},
+                                 "r", R1x1{-3});
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R), t =-3.2;
+let r:R^1, r = t;
+)"},
+                                 "r", R1x1{-3.2});
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^1), t =-1.2;
+let r:R^1, r = t;
+)"},
+                                 "r", R1x1{-1.2});
+      }
+
+      SECTION("-> R^2")
+      {
+        using R2 = TinyVector<2>;
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^2), t =[-1.2, 2];
+let r:R^2, r = t;
+)"},
+                                 "r", (R2{-1.2, 2}));
+      }
+
+      SECTION("-> R^3")
+      {
+        using R3 = TinyVector<3>;
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^3), t =[-1.2, 2, 1];
+let r:R^3, r = t;
+)"},
+                                 "r", (R3{-1.2, 2, 1}));
+      }
+
+      SECTION("-> R^1x1")
+      {
+        using R1x1 = TinyMatrix<1>;
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(B), t = true;
+let r:R^1x1, r = t;
+)"},
+                                 "r", R1x1{1});
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(N), t = 2;
+let r:R^1x1, r = t;
+)"},
+                                 "r", R1x1{2});
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(Z), t =-3;
+let r:R^1x1, r = t;
+)"},
+                                 "r", R1x1{-3});
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R), t =-3.2;
+let r:R^1x1, r = t;
+)"},
+                                 "r", R1x1{-3.2});
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^1x1), t =-1.2;
+let r:R^1x1, r = t;
+)"},
+                                 "r", R1x1{-1.2});
+      }
+
+      SECTION("-> R^2x2")
+      {
+        using R2x2 = TinyMatrix<2>;
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^2x2), t =[[-1.2, 2],[1,3]];
+let r:R^2x2, r = t;
+)"},
+                                 "r", (R2x2{-1.2, 2, 1, 3}));
+      }
+
+      SECTION("-> R^3x3")
+      {
+        using R3x3 = TinyMatrix<3>;
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^3x3), t = [[-1.2, 2, 1],[-2.2, 1, 7],[-3, 1, 2]];
+let r:R^3x3, r = t;
+)"},
+                                 "r", (R3x3{-1.2, 2, 1, -2.2, 1, 7, -3, 1, 2}));
+      }
+
+      SECTION("-> string")
+      {
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(B), t = true;
+let x:string, x = t;
+)"},
+                                 "x", (stringify(true)));
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(N), t = 3;
+let x:string, x = t;
+)"},
+                                 "x", (stringify(3ul)));
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(Z), t = -2;
+let x:string, x = t;
+)"},
+                                 "x", (stringify(-2l)));
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R), t = 1.7;
+let x:string, x = t;
+)"},
+                                 "x", (stringify(double{1.7})));
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^1), t = [1.7];
+let x:string, x = t;
+)"},
+                                 "x", (stringify(TinyVector<1>{1.7})));
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^2), t = [1.7, 2];
+let x:string, x = t;
+)"},
+                                 "x", (stringify(TinyVector<2>{1.7, 2})));
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^3), t = [1.7, 5, 2];
+let x:string, x = t;
+)"},
+                                 "x", (stringify(TinyVector<3>{1.7, 5, 2})));
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^1x1), t = [[1.7]];
+let x:string, x = t;
+)"},
+                                 "x", (stringify(TinyMatrix<1>{1.7})));
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^2x2), t = [[1.7, 2],[1,3]];
+let x:string, x = t;
+)"},
+                                 "x", (stringify(TinyMatrix<2>{1.7, 2, 1, 3})));
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(R^3x3), t = [[1.7, 5, 2],[1,3,5],[-1,2,6]];
+let x:string, x = t;
+)"},
+                                 "x", (stringify(TinyMatrix<3>{1.7, 5, 2, 1, 3, 5, -1, 2, 6})));
+
+        CHECK_AFFECTATION_RESULT(std::string{R"(
+let t:(string), t = "foobar";
+let x:string, x = t;
+)"},
+                                 "x", (std::string{"foobar"}));
+      }
+
+      SECTION("-> builtin_t")
+      {
+        const double expected = double{3.2};
+        CHECK_BUILTIN_AFFECTATION_RESULT(std::string{R"(
+let t:(builtin_t), t = bt_a;
+let x:builtin_t, x = t;
+)"},
+                                         "x", expected);
+      }
     }
   }
 
@@ -360,6 +731,33 @@ TEST_CASE("AffectationProcessor", "[language]")
   {
     SECTION("invalid affectations")
     {
+      SECTION("tuple -> value")
+      {
+        CHECK_AFFECTATION_EXEC_THROWS_WITH(
+          std::string_view{
+            R"(
+let t : (B), t = (true, 2>1, false);
+let b : B; b = t;
+)"},
+          "cannot affect a (B) of size 3 to a B");
+
+        CHECK_AFFECTATION_EXEC_THROWS_WITH(
+          std::string_view{
+            R"(
+let t :(N), t = (2, 3, 6);
+let z : Z; z = t;
+)"},
+          "cannot affect a (N) of size 3 to a Z");
+
+        CHECK_AFFECTATION_EXEC_THROWS_WITH(
+          std::string_view{
+            R"(
+let t :(R^1), t = (2, 3, 6);
+let r : R^1; r = t;
+)"},
+          "cannot affect a (R^1) of size 3 to a R^1");
+      }
+
       SECTION("-> B")
       {
         CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 1; let b : B; b = n;", "undefined affectation type: B = N");
diff --git a/tests/test_AffectationToTupleProcessor.cpp b/tests/test_AffectationToTupleProcessor.cpp
index 1ecaf971ffa09028c6baf59093c1517326c0dc14..84d1a1639d52107a802ff66f8cff9dfd3bff0b5f 100644
--- a/tests/test_AffectationToTupleProcessor.cpp
+++ b/tests/test_AffectationToTupleProcessor.cpp
@@ -206,35 +206,279 @@ let t :(R^1x1); t = (x,2);
 
   SECTION("Affectations from tuple")
   {
-    CHECK_AFFECTATION_RESULT(R"(
-let x :(R^3), x = [1,2,3];
-let s :(string); s = x;
+    SECTION(" -> (B)")
+    {
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(B), a = (true, false, true);
+let b:(B), b = a;
 )",
-                             "s", (std::vector<std::string>{stringify(TinyVector<3, double>{1, 2, 3})}));
+                               "b", (std::vector<bool>{true, false, true}));
+    }
 
-    CHECK_AFFECTATION_RESULT(R"(
-let A :(R^3x3), A = ([[1,2,3],[4,5,6],[7,8,9]]);
-let s :(string); s = A;
+    SECTION(" -> (N)")
+    {
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(B), a = (true, false, true);
+let b:(N), b = a;
 )",
-                             "s", (std::vector<std::string>{stringify(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9})}));
+                               "b", (std::vector<uint64_t>{true, false, true}));
 
-    CHECK_AFFECTATION_RESULT(R"(
-let x :(R), x = (1,2,3);
-let s :(string); s = x;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(N), a = (1, 2, 3);
+let b:(N), b = a;
 )",
-                             "s", (std::vector<std::string>{stringify(1.), stringify(2.), stringify(3.)}));
+                               "b", (std::vector<uint64_t>{1, 2, 3}));
 
-    CHECK_AFFECTATION_RESULT(R"(
-let n :(N), n = (1,2,3);
-let t :(R); t = n;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(Z), a = (5, 6, 2);
+let b:(N), b = a;
 )",
-                             "t", (std::vector<double>{1, 2, 3}));
+                               "b", (std::vector<uint64_t>{5, 6, 2}));
+    }
 
-    CHECK_AFFECTATION_RESULT(R"(
-let s :(N), s = (1,2,3);
-let t :(N); t = s;
+    SECTION(" -> (Z)")
+    {
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(B), a = (true, false, true);
+let b:(Z), b = a;
+)",
+                               "b", (std::vector<int64_t>{true, false, true}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(N), a = (1, 2, 3);
+let b:(Z), b = a;
+)",
+                               "b", (std::vector<int64_t>{1, 2, 3}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(Z), a = (5, -6, 2);
+let b:(Z), b = a;
+)",
+                               "b", (std::vector<int64_t>{5, -6, 2}));
+    }
+
+    SECTION(" -> (R)")
+    {
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(B), a = (true, false, true);
+let b:(R), b = a;
 )",
-                             "t", (std::vector<uint64_t>{1, 2, 3}));
+                               "b", (std::vector<double>{true, false, true}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(N), a = (1, 2, 3);
+let b:(R), b = a;
+)",
+                               "b", (std::vector<double>{1, 2, 3}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(Z), a = (5, -6, 2);
+let b:(R), b = a;
+)",
+                               "b", (std::vector<double>{5, -6, 2}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R), a = (5.2, -6.3, 2.1);
+let b:(R), b = a;
+)",
+                               "b", (std::vector<double>{5.2, -6.3, 2.1}));
+    }
+
+    SECTION(" -> (R^1)")
+    {
+      using R1 = TinyVector<1>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(B), a = (true, false, true);
+let b:(R^1), b = a;
+)",
+                               "b", (std::vector<R1>{R1{true}, R1{false}, R1{true}}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(N), a = (1, 2, 3);
+let b:(R^1), b = a;
+)",
+                               "b", (std::vector<R1>{R1{1}, R1{2}, R1{3}}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(Z), a = (5, -6, 2);
+let b:(R^1), b = a;
+)",
+                               "b", (std::vector<R1>{R1{5}, R1{-6}, R1{2}}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R), a = (5.2, -6.3, 2.1);
+let b:(R^1), b = a;
+)",
+                               "b", (std::vector<R1>{R1{5.2}, R1{-6.3}, R1{2.1}}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^1), a = (5.2, -6.3, 2.1);
+let b:(R^1), b = a;
+)",
+                               "b", (std::vector<R1>{R1{5.2}, R1{-6.3}, R1{2.1}}));
+    }
+
+    SECTION(" -> (R^2)")
+    {
+      using R2 = TinyVector<2>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^2), a = ([5.2, -6.3], [2.1, 0]);
+let b:(R^2), b = a;
+)",
+                               "b", (std::vector<R2>{R2{5.2, -6.3}, R2{2.1, 0}}));
+    }
+
+    SECTION(" -> (R^3)")
+    {
+      using R3 = TinyVector<3>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^3), a = ([5.2, -6.3, 2], [2.1, 0, -1.1]);
+let b:(R^3), b = a;
+)",
+                               "b", (std::vector<R3>{R3{5.2, -6.3, 2}, R3{2.1, 0, -1.1}}));
+    }
+
+    SECTION(" -> (R^1x1)")
+    {
+      using R1x1 = TinyMatrix<1>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(B), a = (true, false, true);
+let b:(R^1x1), b = a;
+)",
+                               "b", (std::vector<R1x1>{R1x1{true}, R1x1{false}, R1x1{true}}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(N), a = (1, 2, 3);
+let b:(R^1x1), b = a;
+)",
+                               "b", (std::vector<R1x1>{R1x1{1}, R1x1{2}, R1x1{3}}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(Z), a = (5, -6, 2);
+let b:(R^1x1), b = a;
+)",
+                               "b", (std::vector<R1x1>{R1x1{5}, R1x1{-6}, R1x1{2}}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R), a = (5.2, -6.3, 2.1);
+let b:(R^1x1), b = a;
+)",
+                               "b", (std::vector<R1x1>{R1x1{5.2}, R1x1{-6.3}, R1x1{2.1}}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^1x1), a = (5.2, -6.3, 2.1);
+let b:(R^1x1), b = a;
+)",
+                               "b", (std::vector<R1x1>{R1x1{5.2}, R1x1{-6.3}, R1x1{2.1}}));
+    }
+
+    SECTION(" -> (R^2x2)")
+    {
+      using R2x2 = TinyMatrix<2>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^2x2), a = ([[5.2, -6.3], [2.1, 0]], 0);
+let b:(R^2x2), b = a;
+)",
+                               "b", (std::vector<R2x2>{R2x2{5.2, -6.3, 2.1, 0}, R2x2{zero}}));
+    }
+
+    SECTION(" -> (R^3x3)")
+    {
+      using R3x3 = TinyMatrix<3>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^3x3), a = ([[5.2, -6.3, 2], [2.1, 0, -1.1], [1,-1.2,-0.7]] , 0);
+let b:(R^3x3), b = a;
+)",
+                               "b", (std::vector<R3x3>{R3x3{5.2, -6.3, 2, 2.1, 0, -1.1, 1, -1.2, -0.7}, R3x3{zero}}));
+    }
+
+    SECTION(" -> (string)")
+    {
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(B), a = (true, false, true);
+let b:(string), b = a;
+)",
+                               "b", (std::vector<std::string>{stringify(true), stringify(false), stringify(true)}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(N), a = (1, 2, 3);
+let b:(string), b = a;
+)",
+                               "b", (std::vector<std::string>{stringify(1ul), stringify(2ul), stringify(3ul)}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(Z), a = (5, -6, 2);
+let b:(string), b = a;
+)",
+                               "b", (std::vector<std::string>{stringify(5l), stringify(-6l), stringify(2l)}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R), a = (5.2, -6.3, 2.1);
+let b:(string), b = a;
+)",
+                               "b",
+                               (std::vector<std::string>{stringify(double{5.2}), stringify(double{-6.3}),
+                                                         stringify(double{2.1})}));
+
+      using R1 = TinyVector<1>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^1), a = ([5.2], [-6.3], [2.1]);
+let b:(string), b = a;
+)",
+                               "b",
+                               (std::vector<std::string>{stringify(R1{5.2}), stringify(R1{-6.3}), stringify(R1{2.1})}));
+
+      using R2 = TinyVector<2>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^2), a = ([5.2, 1.2], [-6.3, 4], [2.1, -1]);
+let b:(string), b = a;
+)",
+                               "b",
+                               (std::vector<std::string>{stringify(R2{5.2, 1.2}), stringify(R2{-6.3, 4}),
+                                                         stringify(R2{2.1, -1})}));
+
+      using R3 = TinyVector<3>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^3), a = ([5.2, 1.2, 2.1], [-6.3, 4, 0], [2.1, -1, 3]);
+let b:(string), b = a;
+)",
+                               "b",
+                               (std::vector<std::string>{stringify(R3{5.2, 1.2, 2.1}), stringify(R3{-6.3, 4, 0}),
+                                                         stringify(R3{2.1, -1, 3})}));
+
+      using R1x1 = TinyMatrix<1>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^1x1), a = ([[5.2]], [[-6.3]], [[2.1]]);
+let b:(string), b = a;
+)",
+                               "b",
+                               (std::vector<std::string>{stringify(R1x1{5.2}), stringify(R1x1{-6.3}),
+                                                         stringify(R1x1{2.1})}));
+
+      using R2x2 = TinyMatrix<2>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^2x2), a = ([[5.2, 1.2], [-6.3, 4]], [[2.1, -1],[1,3]], 0);
+let b:(string), b = a;
+)",
+                               "b",
+                               (std::vector<std::string>{stringify(R2x2{5.2, 1.2, -6.3, 4}),
+                                                         stringify(R2x2{2.1, -1, 1, 3}), stringify(R2x2{zero})}));
+
+      using R3x3 = TinyMatrix<3>;
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(R^3x3), a = ([[5.2, 1.2, 2.1], [-6.3, 4, 0], [2.1, -1, 3]], 0);
+let b:(string), b = a;
+)",
+                               "b",
+                               (std::vector<std::string>{stringify(R3x3{5.2, 1.2, 2.1, -6.3, 4, 0, 2.1, -1, 3}),
+                                                         stringify(R3x3{zero})}));
+
+      CHECK_AFFECTATION_RESULT(R"(
+let a:(string), a = ("foo", "bar", "foobar");
+let b:(string), b = a;
+)",
+                               "b", (std::vector<std::string>{"foo", "bar", "foobar"}));
+    }
   }
 
   SECTION("errors")
@@ -256,6 +500,12 @@ let z : (Z), z = (2,-5,6);
 let n : (N), n = z;
 )",
                                          "trying to affect negative value (-5)");
+
+      CHECK_AFFECTATION_EXEC_THROWS_WITH(R"(
+let z : (Z), z = -3;
+let n : N, n = z;
+)",
+                                         "trying to affect negative value (-3)");
     }
 
     SECTION("affect negative values to (N) in lists")
diff --git a/tests/test_ConsoleManager.cpp b/tests/test_ConsoleManager.cpp
index 68559e449a746cf901dca8820d1bf5b4ebd82172..9fd1fb330842d3bcfc14d25387464026311a2c7a 100644
--- a/tests/test_ConsoleManager.cpp
+++ b/tests/test_ConsoleManager.cpp
@@ -29,5 +29,13 @@ TEST_CASE("ConsoleManager", "[utils]")
     REQUIRE(rang::rang_implementation::controlMode() == rang::control::Off);
 
     rang::setControlMode(saved_control);
+
+    const bool show_preamble = ConsoleManager::showPreamble();
+
+    ConsoleManager::setShowPreamble(not show_preamble);
+    REQUIRE(show_preamble != ConsoleManager::showPreamble());
+
+    ConsoleManager::setShowPreamble(show_preamble);
+    REQUIRE(show_preamble == ConsoleManager::showPreamble());
   }
 }
diff --git a/tests/test_DiscreteFunctionP0.cpp b/tests/test_DiscreteFunctionP0.cpp
index 48c107e4ad2f07391a49270a04c9a3dc20d2db4a..39b91c4de355c1a96feaa1f1b5e2a59bf9c494d8 100644
--- a/tests/test_DiscreteFunctionP0.cpp
+++ b/tests/test_DiscreteFunctionP0.cpp
@@ -4,6 +4,10 @@
 #include <MeshDataBaseForTests.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
 
+#ifdef __clang__
+#pragma clang optimize off
+#endif   // __clang__
+
 // clazy:excludeall=non-pod-global-static
 
 TEST_CASE("DiscreteFunctionP0", "[scheme]")
@@ -3435,7 +3439,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
                 Ah[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3,   //
-                                               -0.2 * x - 1, 2 + x};
+                                            -0.2 * x - 1, 2 + x};
               });
 
             {
@@ -3457,7 +3461,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
                 Ah[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3,   //
-                                               -0.2 * x - 1, 2 + x};
+                                            -0.2 * x - 1, 2 + x};
               });
 
             {
@@ -3479,7 +3483,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
                 Ah[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3,   //
-                                               -0.2 * x - 1, 2 + x};
+                                            -0.2 * x - 1, 2 + x};
               });
 
             {
@@ -3501,7 +3505,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
                 Ah[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3,   //
-                                               -0.2 * x - 1, 2 + x};
+                                            -0.2 * x - 1, 2 + x};
               });
 
             {
@@ -3697,3 +3701,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
   }
 #endif   // NDEBUG
 }
+
+#ifdef __clang__
+#pragma clang optimize on
+#endif   // __clang__
diff --git a/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp b/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp
index ed0331dde90ba7270c0050a633a84982545a5e7b..887d0785da4ce3b5b02ce9b9e7e8eec7dd841159 100644
--- a/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp
@@ -496,6 +496,10 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
         REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
         REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
         REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
+        REQUIRE_THROWS_WITH(dot(p_u, p_u), "error: invalid operand type Vh(P0:R)");
+        REQUIRE_THROWS_WITH(dot(p_R1x1_u, p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+        REQUIRE_THROWS_WITH(dot(p_R2x2_u, p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+        REQUIRE_THROWS_WITH(dot(p_R3x3_u, p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
         REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
       }
 
@@ -514,6 +518,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
         REQUIRE_THROWS_WITH(det(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
         REQUIRE_THROWS_WITH(det(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
         REQUIRE_THROWS_WITH(det(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+        REQUIRE_THROWS_WITH(det(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("trace Vh -> Vh")
@@ -531,6 +536,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
         REQUIRE_THROWS_WITH(trace(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
         REQUIRE_THROWS_WITH(trace(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
         REQUIRE_THROWS_WITH(trace(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+        REQUIRE_THROWS_WITH(trace(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("inverse Vh -> Vh")
@@ -548,6 +554,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
         REQUIRE_THROWS_WITH(inverse(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
         REQUIRE_THROWS_WITH(inverse(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
         REQUIRE_THROWS_WITH(inverse(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+        REQUIRE_THROWS_WITH(inverse(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("transpose Vh -> Vh")
@@ -565,6 +572,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
         REQUIRE_THROWS_WITH(transpose(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
         REQUIRE_THROWS_WITH(transpose(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
         REQUIRE_THROWS_WITH(transpose(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+        REQUIRE_THROWS_WITH(transpose(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("sum_of_Vh Vh -> Vh")
@@ -625,6 +633,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
         REQUIRE_THROWS_WITH(vectorize(std::vector{p_u, p_other_mesh_u}),
                             "error: discrete functions are not defined on the same mesh");
         REQUIRE_THROWS_WITH(vectorize(std::vector{p_R1_u}), "error: invalid operand type Vh(P0:R^1)");
+        REQUIRE_THROWS_WITH(vectorize(std::vector{p_Vector3_u}), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("dot Vh*Rd -> Vh")
@@ -635,6 +644,11 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
                                                       DiscreteFunctionR2, DiscreteFunctionR);
         CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot,   //
                                                       DiscreteFunctionR3, DiscreteFunctionR);
+
+        REQUIRE_THROWS_WITH(dot(p_u, TinyVector<2>{1, 2}), "error: invalid operand type Vh(P0:R)");
+        REQUIRE_THROWS_WITH(dot(p_Vector3_u, TinyVector<2>{1, 2}),
+                            "error: incompatible operand types Vh(P0Vector:R) and R^2");
+
         REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})),
                             "error: incompatible operand types Vh(P0:R^1) and R^2");
         REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
@@ -650,6 +664,11 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
                                                       DiscreteFunctionR2, DiscreteFunctionR);
         CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot,   //
                                                       DiscreteFunctionR3, DiscreteFunctionR);
+
+        REQUIRE_THROWS_WITH(dot(TinyVector<2>{1, 2}, p_u), "error: invalid operand type Vh(P0:R)");
+        REQUIRE_THROWS_WITH(dot(TinyVector<2>{1, 2}, p_Vector3_u),
+                            "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+
         REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u),
                             "error: incompatible operand types R^2 and Vh(P0:R^1)");
         REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
@@ -674,6 +693,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
         REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
         REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
         REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        REQUIRE_THROWS_WITH(sum_of<double>(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("integral_of_R* Vh -> R*")
@@ -709,6 +729,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
         REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
         REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
         REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        REQUIRE_THROWS_WITH(integral_of<double>(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
     }
   }
diff --git a/tests/test_EmbeddedDiscreteFunctionMathFunctions2D.cpp b/tests/test_EmbeddedDiscreteFunctionMathFunctions2D.cpp
index dfbdb01500b08f73dd9804d4d4581aafe4c4ba63..ddbf9266412e1395b922d70d3f83c6ca3ad977a5 100644
--- a/tests/test_EmbeddedDiscreteFunctionMathFunctions2D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionMathFunctions2D.cpp
@@ -498,6 +498,10 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
           REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
           REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
           REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(dot(p_u, p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(dot(p_R1x1_u, p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(dot(p_R2x2_u, p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(dot(p_R3x3_u, p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
           REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
         }
 
@@ -516,6 +520,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
           REQUIRE_THROWS_WITH(det(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
           REQUIRE_THROWS_WITH(det(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
           REQUIRE_THROWS_WITH(det(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(det(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
         }
 
         SECTION("trace Vh -> Vh")
@@ -533,6 +538,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
           REQUIRE_THROWS_WITH(trace(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
           REQUIRE_THROWS_WITH(trace(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
           REQUIRE_THROWS_WITH(trace(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(trace(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
         }
 
         SECTION("inverse Vh -> Vh")
@@ -550,6 +556,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
           REQUIRE_THROWS_WITH(inverse(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
           REQUIRE_THROWS_WITH(inverse(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
           REQUIRE_THROWS_WITH(inverse(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(inverse(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
         }
 
         SECTION("transpose Vh -> Vh")
@@ -567,6 +574,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
           REQUIRE_THROWS_WITH(transpose(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
           REQUIRE_THROWS_WITH(transpose(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
           REQUIRE_THROWS_WITH(transpose(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(transpose(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
         }
 
         SECTION("sum_of_Vh Vh -> Vh")
@@ -627,6 +635,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
           REQUIRE_THROWS_WITH(vectorize(std::vector{p_u, p_other_mesh_u}),
                               "error: discrete functions are not defined on the same mesh");
           REQUIRE_THROWS_WITH(vectorize(std::vector{p_R1_u}), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(vectorize(std::vector{p_Vector3_u}), "error: invalid operand type Vh(P0Vector:R)");
         }
 
         SECTION("dot Vh*Rd -> Vh")
@@ -637,6 +646,11 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
                                                         DiscreteFunctionR2, DiscreteFunctionR);
           CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot,   //
                                                         DiscreteFunctionR3, DiscreteFunctionR);
+
+          REQUIRE_THROWS_WITH(dot(p_u, TinyVector<2>{1, 2}), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(dot(p_Vector3_u, TinyVector<2>{1, 2}),
+                              "error: incompatible operand types Vh(P0Vector:R) and R^2");
+
           REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})),
                               "error: incompatible operand types Vh(P0:R^1) and R^2");
           REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
@@ -652,6 +666,11 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
                                                         DiscreteFunctionR2, DiscreteFunctionR);
           CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot,   //
                                                         DiscreteFunctionR3, DiscreteFunctionR);
+
+          REQUIRE_THROWS_WITH(dot(TinyVector<2>{1, 2}, p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(dot(TinyVector<2>{1, 2}, p_Vector3_u),
+                              "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+
           REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u),
                               "error: incompatible operand types R^2 and Vh(P0:R^1)");
           REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
@@ -676,6 +695,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
           REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
           REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
           REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
         }
 
         SECTION("integral_of_R* Vh -> R*")
@@ -711,6 +731,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
           REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
           REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
           REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
         }
       }
     }
diff --git a/tests/test_EmbeddedDiscreteFunctionMathFunctions3D.cpp b/tests/test_EmbeddedDiscreteFunctionMathFunctions3D.cpp
index 5f9896ad64df5601f54f9d701fc5bfe94f4d51b9..efb77fdde37448c15de9b95499cd28d01805e63f 100644
--- a/tests/test_EmbeddedDiscreteFunctionMathFunctions3D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionMathFunctions3D.cpp
@@ -496,6 +496,10 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
         REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
         REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
         REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
+        REQUIRE_THROWS_WITH(dot(p_u, p_u), "error: invalid operand type Vh(P0:R)");
+        REQUIRE_THROWS_WITH(dot(p_R1x1_u, p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+        REQUIRE_THROWS_WITH(dot(p_R2x2_u, p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+        REQUIRE_THROWS_WITH(dot(p_R3x3_u, p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
         REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
       }
 
@@ -514,6 +518,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
         REQUIRE_THROWS_WITH(det(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
         REQUIRE_THROWS_WITH(det(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
         REQUIRE_THROWS_WITH(det(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+        REQUIRE_THROWS_WITH(det(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("trace Vh -> Vh")
@@ -531,6 +536,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
         REQUIRE_THROWS_WITH(trace(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
         REQUIRE_THROWS_WITH(trace(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
         REQUIRE_THROWS_WITH(trace(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+        REQUIRE_THROWS_WITH(trace(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("inverse Vh -> Vh")
@@ -548,6 +554,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
         REQUIRE_THROWS_WITH(inverse(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
         REQUIRE_THROWS_WITH(inverse(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
         REQUIRE_THROWS_WITH(inverse(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+        REQUIRE_THROWS_WITH(inverse(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("transpose Vh -> Vh")
@@ -565,6 +572,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
         REQUIRE_THROWS_WITH(transpose(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
         REQUIRE_THROWS_WITH(transpose(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
         REQUIRE_THROWS_WITH(transpose(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+        REQUIRE_THROWS_WITH(transpose(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("sum_of_Vh Vh -> Vh")
@@ -625,6 +633,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
         REQUIRE_THROWS_WITH(vectorize(std::vector{p_u, p_other_mesh_u}),
                             "error: discrete functions are not defined on the same mesh");
         REQUIRE_THROWS_WITH(vectorize(std::vector{p_R1_u}), "error: invalid operand type Vh(P0:R^1)");
+        REQUIRE_THROWS_WITH(vectorize(std::vector{p_Vector3_u}), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("dot Vh*Rd -> Vh")
@@ -635,6 +644,11 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
                                                       DiscreteFunctionR2, DiscreteFunctionR);
         CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot,   //
                                                       DiscreteFunctionR3, DiscreteFunctionR);
+
+        REQUIRE_THROWS_WITH(dot(p_u, TinyVector<2>{1, 2}), "error: invalid operand type Vh(P0:R)");
+        REQUIRE_THROWS_WITH(dot(p_Vector3_u, TinyVector<2>{1, 2}),
+                            "error: incompatible operand types Vh(P0Vector:R) and R^2");
+
         REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})),
                             "error: incompatible operand types Vh(P0:R^1) and R^2");
         REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
@@ -650,6 +664,11 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
                                                       DiscreteFunctionR2, DiscreteFunctionR);
         CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot,   //
                                                       DiscreteFunctionR3, DiscreteFunctionR);
+
+        REQUIRE_THROWS_WITH(dot(TinyVector<2>{1, 2}, p_u), "error: invalid operand type Vh(P0:R)");
+        REQUIRE_THROWS_WITH(dot(TinyVector<2>{1, 2}, p_Vector3_u),
+                            "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+
         REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u),
                             "error: incompatible operand types R^2 and Vh(P0:R^1)");
         REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
@@ -674,6 +693,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
         REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
         REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
         REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        REQUIRE_THROWS_WITH(sum_of<double>(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
 
       SECTION("integral_of_R* Vh -> R*")
@@ -709,6 +729,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
         REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
         REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
         REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        REQUIRE_THROWS_WITH(integral_of<double>(p_Vector3_u), "error: invalid operand type Vh(P0Vector:R)");
       }
     }
   }
diff --git a/tests/test_EmbeddedDiscreteFunctionOperators1D.cpp b/tests/test_EmbeddedDiscreteFunctionOperators1D.cpp
index 5538543de393bd2fb8009e3d2cbd04d57107ead3..47ff54931e0f569dedda032a1b99608ad0b22bd0 100644
--- a/tests/test_EmbeddedDiscreteFunctionOperators1D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionOperators1D.cpp
@@ -681,6 +681,12 @@ TEST_CASE("EmbeddedDiscreteFunctionOperators1D", "[scheme]")
             REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
                                 "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
 
+            REQUIRE_THROWS_WITH(p_R2x2_u * (TinyVector<1>{2}),
+                                "error: incompatible operand types Vh(P0:R^2x2) and R^1");
+
+            REQUIRE_THROWS_WITH(p_R3x3_u * (TinyVector<2>{2, 1}),
+                                "error: incompatible operand types Vh(P0:R^3x3) and R^2");
+
             REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
                                 "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
             REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
@@ -829,6 +835,8 @@ TEST_CASE("EmbeddedDiscreteFunctionOperators1D", "[scheme]")
                                       DiscreteFunctionR, DiscreteFunctionR);
             CHECK_EMBEDDED_XxVH_TO_VH(double{1.3}, /, p_R_u,   //
                                       DiscreteFunctionR, DiscreteFunctionR);
+
+            REQUIRE_THROWS_WITH(3.2 / p_R1_v, "error: incompatible operand types R and Vh(P0:R^1)");
           }
         }
       }
diff --git a/tests/test_EmbeddedDiscreteFunctionOperators2D.cpp b/tests/test_EmbeddedDiscreteFunctionOperators2D.cpp
index fbf114d085d04a76da6715d3493516c9ead57044..f6885617190de4a928626105697eaeabab641b40 100644
--- a/tests/test_EmbeddedDiscreteFunctionOperators2D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionOperators2D.cpp
@@ -681,6 +681,12 @@ TEST_CASE("EmbeddedDiscreteFunctionOperators2D", "[scheme]")
             REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
                                 "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
 
+            REQUIRE_THROWS_WITH(p_R2x2_u * (TinyVector<1>{2}),
+                                "error: incompatible operand types Vh(P0:R^2x2) and R^1");
+
+            REQUIRE_THROWS_WITH(p_R3x3_u * (TinyVector<2>{2, 1}),
+                                "error: incompatible operand types Vh(P0:R^3x3) and R^2");
+
             REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
                                 "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
             REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
@@ -831,6 +837,8 @@ TEST_CASE("EmbeddedDiscreteFunctionOperators2D", "[scheme]")
                                       DiscreteFunctionR, DiscreteFunctionR);
             CHECK_EMBEDDED_XxVH_TO_VH(double{1.3}, /, p_R_u,   //
                                       DiscreteFunctionR, DiscreteFunctionR);
+
+            REQUIRE_THROWS_WITH(3.2 / p_R1_v, "error: incompatible operand types R and Vh(P0:R^1)");
           }
         }
       }
diff --git a/tests/test_EmbeddedDiscreteFunctionOperators3D.cpp b/tests/test_EmbeddedDiscreteFunctionOperators3D.cpp
index 1b24d0a9128db6bcc8d4c3eff70a38a3c5fc9ee4..60b68b473def74a34e839a0d558a3e08f21eb58f 100644
--- a/tests/test_EmbeddedDiscreteFunctionOperators3D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionOperators3D.cpp
@@ -681,6 +681,12 @@ TEST_CASE("EmbeddedDiscreteFunctionOperators3D", "[scheme]")
             REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
                                 "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
 
+            REQUIRE_THROWS_WITH(p_R2x2_u * (TinyVector<1>{2}),
+                                "error: incompatible operand types Vh(P0:R^2x2) and R^1");
+
+            REQUIRE_THROWS_WITH(p_R3x3_u * (TinyVector<2>{2, 1}),
+                                "error: incompatible operand types Vh(P0:R^3x3) and R^2");
+
             REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
                                 "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
             REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
@@ -831,6 +837,8 @@ TEST_CASE("EmbeddedDiscreteFunctionOperators3D", "[scheme]")
                                       DiscreteFunctionR, DiscreteFunctionR);
             CHECK_EMBEDDED_XxVH_TO_VH(double{1.3}, /, p_R_u,   //
                                       DiscreteFunctionR, DiscreteFunctionR);
+
+            REQUIRE_THROWS_WITH(3.2 / p_R1_v, "error: incompatible operand types R and Vh(P0:R^1)");
           }
         }
       }
diff --git a/tests/test_FunctionProcessor.cpp b/tests/test_FunctionProcessor.cpp
index 97cc8d6be42199f684739dba21b1b3f9a2cab644..500998b50f1729e9416b26cef7462bcc101b3151 100644
--- a/tests/test_FunctionProcessor.cpp
+++ b/tests/test_FunctionProcessor.cpp
@@ -9,6 +9,7 @@
 #include <language/ast/ASTNodeTypeCleaner.hpp>
 #include <language/ast/ASTSymbolTableBuilder.hpp>
 #include <utils/Demangle.hpp>
+#include <utils/Stringify.hpp>
 
 #include <pegtl/string_input.hpp>
 
@@ -570,6 +571,16 @@ let fx:R^3x3, fx = f(3);
                                        (TinyMatrix<3>{x, 2 * x, x * x, 3 * x, 2 + x, x - 1, x + 0.5, 2 * x - 1,
                                                       1 / x}));
     }
+
+    SECTION(" -> N*(R)")
+    {
+      std::string_view data = R"(
+let f : R -> (R)*R, x -> ((2*x, x+2, 3), x);
+let (t,x):(R)*R, (t,x) = f(3.2);
+)";
+      CHECK_FUNCTION_EVALUATION_RESULT(data, "x", double{3.2});
+      CHECK_FUNCTION_EVALUATION_RESULT(data, "t", (std::vector<double>{double{2 * 3.2}, double{3.2 + 2}, double{3}}));
+    }
   }
 
   SECTION("multi-expression functions (using R^d)")
@@ -823,52 +834,1113 @@ let x:R, x = g(f(3));
     }
   }
 
-  SECTION("errors")
+  SECTION("tuple functions")
   {
-    SECTION("negative value to N")
+    SECTION("-> (B)")
     {
-      SECTION("single N argument")
+      SECTION("from B")
       {
         std::string_view data = R"(
-let f : N -> N, n -> 2;
-f(2);
-f(-1);
+let f : B -> (B), b -> not b;
+let b:(B), b = f(true);
 )";
+        std::vector<bool> result{false};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "b", result);
+      }
 
-        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-1)");
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let f : B -> (B), n -> (not n, n, false);
+let t:(B), t = f(true);
+)";
+        std::vector<bool> result{false, true, false};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
       }
 
-      SECTION("multiple N arguments")
+      SECTION("from (B)")
       {
         std::string_view data = R"(
-let f : R*N -> R, (n,x) -> n*x;
-f(-2, 3);
-f(-1,-4);
+let a:(B), a = (true, false);
+let f : N -> (B), n -> a;
+let t:(B), t = f(true);
 )";
+        std::vector<bool> result{true, false};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
 
-        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-4)");
+    SECTION("-> (N)")
+    {
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (N), n -> not n;
+let t:(N), t = f(false);
+)";
+        std::vector<uint64_t> result{true};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
       }
 
-      SECTION("single N return value")
+      SECTION("from N")
       {
         std::string_view data = R"(
-let f : N*N -> N, (m,n) -> m-n;
-f(2, 1);
-f(1, 3);
+let f : N -> (N), n -> n;
+let t:(N), t = f(7);
+)";
+        std::vector<uint64_t> result{7};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (N), p -> -p;
+let t:(N), t = f(-2);
 )";
+        std::vector<uint64_t> result{2};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
 
-        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-2)");
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (N), n -> l;
+let t:(N), t = f(false);
+)";
+        std::vector<uint64_t> result{1, 0, 1};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
       }
 
-      SECTION("compound N return value")
+      SECTION("from (N)")
       {
         std::string_view data = R"(
-let f : N*N -> N*N, (m,n) -> (m+n, m-n);
-f(3, 2);
-f(1, 4);
+let l :(N), l = (2,3,6);
+let f : N -> (N), n -> l;
+let t :(N), t = f(2);
 )";
+        std::vector<uint64_t> result{2, 3, 6};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
 
-        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-3)");
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, 6, 7);
+let f : Z -> (N), p -> l;
+let t :(N), t = f(-2);
+)";
+        std::vector<uint64_t> result{2, 6, 7};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (N), b -> (b, n, 12);
+let t :(N), t = f(false);
+)";
+        std::vector<uint64_t> result{false, 3, 12};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (Z)")
+    {
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (Z), n -> not n;
+let t:(Z), t = f(false);
+)";
+        std::vector<int64_t> result{true};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from N")
+      {
+        std::string_view data = R"(
+let f : N -> (Z), n -> n;
+let t:(Z), t = f(7);
+)";
+        std::vector<int64_t> result{7};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (Z), p -> p;
+let t:(Z), t = f(-2);
+)";
+        std::vector<int64_t> result{-2};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (Z), n -> l;
+let t:(Z), t = f(false);
+)";
+        std::vector<int64_t> result{1, 0, 1};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (N)")
+      {
+        std::string_view data = R"(
+let l :(N), l = (2,3,6);
+let f : N -> (Z), n -> l;
+let t :(Z), t = f(2);
+)";
+        std::vector<int64_t> result{2, 3, 6};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, -6, 7);
+let f : Z -> (Z), p -> l;
+let t :(Z), t = f(-2);
+)";
+        std::vector<int64_t> result{2, -6, 7};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (Z), b -> (b, n, -12);
+let t :(Z), t = f(false);
+)";
+        std::vector<int64_t> result{false, 3, -12};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R)")
+    {
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (R), n -> not n;
+let t:(R), t = f(false);
+)";
+        std::vector<double> result{true};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from N")
+      {
+        std::string_view data = R"(
+let f : N -> (R), n -> n;
+let t:(R), t = f(7);
+)";
+        std::vector<double> result{7};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (R), p -> p;
+let t:(R), t = f(-2);
+)";
+        std::vector<double> result{-2};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R")
+      {
+        std::string_view data = R"(
+let f : Z -> (R), p -> 2.3*p;
+let t:(R), t = f(-2);
+)";
+        std::vector<double> result{-2l * 2.3};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (R), n -> l;
+let t:(R), t = f(false);
+)";
+        std::vector<double> result{true, false, true};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (N)")
+      {
+        std::string_view data = R"(
+let l :(N), l = (2,3,6);
+let f : N -> (R), n -> l;
+let t :(R), t = f(2);
+)";
+        std::vector<double> result{2ul, 3ul, 6ul};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, -6, 7);
+let f : Z -> (R), p -> l;
+let t :(R), t = f(-2);
+)";
+        std::vector<double> result{2l, -6l, 7l};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R)")
+      {
+        std::string_view data = R"(
+let l :(R), l = (2.2, -6, 7.3);
+let f : Z -> (R), p -> l;
+let t :(R), t = f(-2);
+)";
+        std::vector<double> result{2.2, -6, 7.3};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (R), b -> (b, n, -12, 2.3);
+let t :(R), t = f(false);
+)";
+        std::vector<double> result{false, 3ul, -12l, 2.3};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^1)")
+    {
+      using Rd = TinyVector<1>;
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (R^1), n -> not n;
+let t:(R^1), t = f(false);
+)";
+        std::vector<Rd> result{Rd{true}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from N")
+      {
+        std::string_view data = R"(
+let f : N -> (R^1), n -> n;
+let t:(R^1), t = f(7);
+)";
+        std::vector<Rd> result{Rd{7}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1), p -> p;
+let t:(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{-2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1), p -> 2.3*p;
+let t:(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{-2l * 2.3}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^1")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1), p -> 2.2*[p];
+let t:(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{-2l * 2.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (R^1), n -> l;
+let t:(R^1), t = f(false);
+)";
+        std::vector<Rd> result{Rd{true}, Rd{false}, Rd{true}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (N)")
+      {
+        std::string_view data = R"(
+let l :(N), l = (2,3,6);
+let f : N -> (R^1), n -> l;
+let t :(R^1), t = f(2);
+)";
+        std::vector<Rd> result{Rd{2ul}, Rd{3ul}, Rd{6ul}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, -6, 7);
+let f : Z -> (R^1), p -> l;
+let t :(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{2l}, Rd{-6l}, Rd{7l}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R)")
+      {
+        std::string_view data = R"(
+let l :(R), l = (2.2, -6.1, 7.4);
+let f : Z -> (R^1), p -> l;
+let t :(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{2.2}, Rd{-6.1}, Rd{7.4}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^1)")
+      {
+        std::string_view data = R"(
+let l :(R^1), l = ([2.2], -[6.1], [7.4]);
+let f : Z -> (R^1), p -> l;
+let t :(R^1), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{2.2}, Rd{-6.1}, Rd{7.4}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (R^1), b -> (b, n, -12, 2.3, [4.2]);
+let t :(R^1), t = f(false);
+)";
+        std::vector<Rd> result{Rd{false}, Rd{3ul}, Rd{-12l}, Rd{2.3}, Rd{4.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^2)")
+    {
+      using Rd = TinyVector<2>;
+      SECTION("from R^2")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^2), p -> 2.2*[p, 2*p];
+let t:(R^2), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{-2l * 2.2, -4l * 2.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from '0'")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^2), p -> 0;
+let t :(R^2), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{zero}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^2)")
+      {
+        std::string_view data = R"(
+let l :(R^2), l = ([2.2, 1.2], -[6.1, 7], [7.4, 1]);
+let f : Z -> (R^2), p -> l;
+let t :(R^2), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{2.2, 1.2}, -Rd{6.1, 7}, Rd{7.4, 1}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let f : B -> (R^2), b -> ([1.2, 3], 0, [4.2, 1]);
+let t :(R^2), t = f(false);
+)";
+        std::vector<Rd> result{Rd{1.2, 3}, Rd{zero}, Rd{4.2, 1}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^3)")
+    {
+      using Rd = TinyVector<3>;
+      SECTION("from R^3")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^3), p -> 2.2*[p, 2*p, 2];
+let t:(R^3), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{-2l * 2.2, -4l * 2.2, 4.4}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from '0'")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^3), p -> 0;
+let t :(R^3), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{zero}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^3)")
+      {
+        std::string_view data = R"(
+let l :(R^3), l = ([2.2, 1.2, 2], -[6.1, 7, 5], [7.4, 1, -1]);
+let f : Z -> (R^3), p -> l;
+let t :(R^3), t = f(-2);
+)";
+        std::vector<Rd> result{Rd{2.2, 1.2, 2}, -Rd{6.1, 7, 5}, Rd{7.4, 1, -1}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let f : B -> (R^3), b -> ([1.2, 3, 1], 0, [4.2, 1, 7]);
+let t :(R^3), t = f(false);
+)";
+        std::vector<Rd> result{Rd{1.2, 3, 1}, Rd{zero}, Rd{4.2, 1, 7}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^1x1)")
+    {
+      using Rdxd = TinyMatrix<1>;
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (R^1x1), n -> not n;
+let t:(R^1x1), t = f(false);
+)";
+        std::vector<Rdxd> result{Rdxd{true}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from N")
+      {
+        std::string_view data = R"(
+let f : N -> (R^1x1), n -> n;
+let t:(R^1x1), t = f(7);
+)";
+        std::vector<Rdxd> result{Rdxd{7}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1x1), p -> p;
+let t:(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{-2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1x1), p -> 2.3*p;
+let t:(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{-2l * 2.3}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^1x1")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^1x1), p -> 2.2*[[p]];
+let t:(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{-2l * 2.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (R^1x1), n -> l;
+let t:(R^1x1), t = f(false);
+)";
+        std::vector<Rdxd> result{Rdxd{true}, Rdxd{false}, Rdxd{true}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (N)")
+      {
+        std::string_view data = R"(
+let l :(N), l = (2,3,6);
+let f : N -> (R^1x1), n -> l;
+let t :(R^1x1), t = f(2);
+)";
+        std::vector<Rdxd> result{Rdxd{2ul}, Rdxd{3ul}, Rdxd{6ul}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, -6, 7);
+let f : Z -> (R^1x1), p -> l;
+let t :(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{2l}, Rdxd{-6l}, Rdxd{7l}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R)")
+      {
+        std::string_view data = R"(
+let l :(R), l = (2.2, -6.1, 7.4);
+let f : Z -> (R^1x1), p -> l;
+let t :(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{2.2}, Rdxd{-6.1}, Rdxd{7.4}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^1x1)")
+      {
+        std::string_view data = R"(
+let l :(R^1x1), l = ([[2.2]], -[[6.1]], [[7.4]]);
+let f : Z -> (R^1x1), p -> l;
+let t :(R^1x1), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{2.2}, Rdxd{-6.1}, Rdxd{7.4}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (R^1x1), b -> (b, n, -12, 2.3, [[4.2]]);
+let t :(R^1x1), t = f(false);
+)";
+        std::vector<Rdxd> result{Rdxd{false}, Rdxd{3ul}, Rdxd{-12l}, Rdxd{2.3}, Rdxd{4.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^2x2)")
+    {
+      using Rdxd = TinyMatrix<2>;
+      SECTION("from R^2x2")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^2x2), p -> 2.2*[[p, 2*p], [2,3]];
+let t:(R^2x2), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{-2l * 2.2, -4l * 2.2, 2l * 2.2, 3l * 2.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from '0'")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^2x2), p -> 0;
+let t :(R^2x2), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{zero}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^2x2)")
+      {
+        std::string_view data = R"(
+let l :(R^2x2), l = ([[2.2, 1.2],[6.1, 7]], [[7.4, 1],[3.4, -2]]);
+let f : Z -> (R^2x2), p -> l;
+let t :(R^2x2), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{2.2, 1.2, 6.1, 7}, Rdxd{7.4, 1, 3.4, -2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let f : B -> (R^2x2), b -> ([[1.2, 3],[4.5, 6]], 0, [[4.2, 1],[1.3,-6]]);
+let t :(R^2x2), t = f(false);
+)";
+        std::vector<Rdxd> result{Rdxd{1.2, 3, 4.5, 6}, Rdxd{zero}, Rdxd{4.2, 1, 1.3, -6}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (R^3x3)")
+    {
+      using Rdxd = TinyMatrix<3>;
+      SECTION("from R^3x3")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^3x3), p -> 2.2*[[p, 2*p, 2],[2,3,5],[3,6,2]];
+let t:(R^3x3), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{-2l * 2.2, -4l * 2.2, 2l * 2.2,   //
+                                      2l * 2.2, 3l * 2.2, 5l * 2.2,     //
+                                      3l * 2.2, 6l * 2.2, 2l * 2.2}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from '0'")
+      {
+        std::string_view data = R"(
+let f : Z -> (R^3x3), p -> 0;
+let t :(R^3x3), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{zero}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^3x3)")
+      {
+        std::string_view data = R"(
+let l :(R^3x3), l = ([[2.2, 1.2, 2], [-6.1, 7, 5], [7.4, 1, -1]], 0);
+let f : Z -> (R^3x3), p -> l;
+let t :(R^3x3), t = f(-2);
+)";
+        std::vector<Rdxd> result{Rdxd{2.2, 1.2, 2,   //
+                                      -6.1, 7, 5,    //
+                                      7.4, 1, -1},
+                                 Rdxd{zero}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let f : B -> (R^3x3), b -> ([[1.2, 3, 1],[1,2,3],[4.2,2,6]], 0, [[4.2, 1, 7],[2,4,6],[2,3,1]]);
+let t :(R^3x3), t = f(false);
+)";
+        std::vector<Rdxd> result{Rdxd{1.2, 3, 1,   //
+                                      1, 2, 3,     //
+                                      4.2, 2, 6},
+                                 Rdxd{zero},
+                                 Rdxd{4.2, 1, 7,   //
+                                      2, 4, 6,     //
+                                      2, 3, 1}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("-> (string)")
+    {
+      SECTION("from B")
+      {
+        std::string_view data = R"(
+let f : B -> (string), n -> not n;
+let t:(string), t = f(false);
+)";
+        std::vector<std::string> result{stringify(true)};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from N")
+      {
+        std::string_view data = R"(
+let f : N -> (string), n -> n;
+let t:(string), t = f(7);
+)";
+        std::vector<std::string> result{stringify(7)};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from Z")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> p;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(-2)};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> 2.3*p;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(-2l * 2.3)};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^1")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [2.3*p];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyVector<1>{-2l * 2.3})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^2")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [2.3*p, 3];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyVector<2>{-2l * 2.3, 3})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^3")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [1, 2.3*p, 3];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyVector<3>{1, -2l * 2.3, 3})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^1x1")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [[2.3*p]];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyMatrix<1>{-2l * 2.3})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^2x2")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [[2.3*p, 3],[2, p]];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyMatrix<2>{-2l * 2.3, 3, 2, -2})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from R^3x3")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> [[1, 2.3*p, 3],[1.2, 2.3, 0.5],[0.1, 1.4, 6]];
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(TinyMatrix<3>{1, -2l * 2.3, 3,   //
+                                                                1.2, 2.3, 0.5,     //
+                                                                0.1, 1.4, 6})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from string")
+      {
+        std::string_view data = R"(
+let f : Z -> (string), p -> "foo";
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{std::string{"foo"}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (B)")
+      {
+        std::string_view data = R"(
+let l : (B), l = (true, false, true);
+let f : B -> (string), n -> l;
+let t:(string), t = f(false);
+)";
+        std::vector<std::string> result{stringify(std::vector<bool>{true, false, true})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (N)")
+      {
+        std::string_view data = R"(
+let l :(N), l = (2,3,6);
+let f : N -> (string), n -> l;
+let t :(string), t = f(2);
+)";
+        std::vector<std::string> result{stringify(std::vector<double>{2ul, 3ul, 6ul})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (Z)")
+      {
+        std::string_view data = R"(
+let l :(Z), l = (2, -6, 7);
+let f : Z -> (string), p -> l;
+let t :(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<double>{2l, -6l, 7l})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R)")
+      {
+        std::string_view data = R"(
+let l :(R), l = (2.2, -6, 7.3);
+let f : Z -> (string), p -> l;
+let t :(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<double>{2.2, -6, 7.3})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^1)")
+      {
+        using Rd = TinyVector<1>;
+
+        std::string_view data = R"(
+let l : (R^1), l = (0, [2], [3.5]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<Rd>{Rd{zero}, Rd{2}, Rd{3.5}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^2)")
+      {
+        using Rd = TinyVector<2>;
+
+        std::string_view data = R"(
+let l : (R^2), l = ([1, 2], 0, [2, 3.5]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<Rd>{Rd{1, 2}, Rd{zero}, Rd{2, 3.5}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^3)")
+      {
+        using Rd = TinyVector<3>;
+
+        std::string_view data = R"(
+let l : (R^3), l = ([1, 2, 3], 0, [2, 3.5, 7]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<Rd>{Rd{1, 2, 3}, Rd{zero}, Rd{2, 3.5, 7}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^1x1)")
+      {
+        using Rdxd = TinyMatrix<1>;
+
+        std::string_view data = R"(
+let l : (R^1x1), l = ([[1]], 0, [[3.5]]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{stringify(std::vector<Rdxd>{Rdxd{1}, Rdxd{zero}, Rdxd{3.5}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^2x2)")
+      {
+        using Rdxd = TinyMatrix<2>;
+
+        std::string_view data = R"(
+let l : (R^2x2), l = ([[1,2],[3,4]], 0, [[3.5,-5],[3.7,2.9]]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{
+          stringify(std::vector<Rdxd>{Rdxd{1, 2, 3, 4}, Rdxd{zero}, Rdxd{3.5, -5, 3.7, 2.9}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (R^3x3)")
+      {
+        using Rdxd = TinyMatrix<3>;
+
+        std::string_view data = R"(
+let l : (R^3x3), l = ([[1,2,3],[3,5,6],[9,3,4]], 0, [[1,3,7],[3.5,-5,2.3],[2.1,3.7,2.9]]);
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+
+        std::vector<std::string> result{stringify(std::vector<Rdxd>{Rdxd{1, 2, 3, 3, 5, 6, 9, 3, 4},   //
+                                                                    Rdxd{zero},                        //
+                                                                    Rdxd{1, 3, 7, 3.5, -5, 2.3, 2.1, 3.7, 2.9}})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from (string)")
+      {
+        std::string_view data = R"(
+let l:(string), l = ("foo", "bar", "foobar");
+let f : Z -> (string), p -> l;
+let t:(string), t = f(-2);
+)";
+        std::vector<std::string> result{std::vector<std::string>{"foo", "bar", "foobar"}};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("from list")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : B -> (string), b -> (b, n, -12, 2.3, "foo");
+let t :(string), t = f(false);
+)";
+
+        std::vector<std::string> result{stringify(
+          std::vector<std::string>{stringify(false), stringify(3ul), stringify(-12l), stringify(2.3), "foo"})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+    }
+
+    SECTION("empty functions")
+    {
+      SECTION("to value")
+      {
+        std::string_view data = R"(
+let f : void -> R^3, void -> [1,2,3];
+let x :R^3, x = f();
+)";
+
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "x", (TinyVector<3>{1, 2, 3}));
+      }
+
+      SECTION("to tuple")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : void -> (string), void -> (false, n, -12, 2.3, "foo");
+n+=5;
+let t :(string), t = f();
+)";
+
+        std::vector<std::string> result{stringify(
+          std::vector<std::string>{stringify(false), stringify(8ul), stringify(-12l), stringify(2.3), "foo"})};
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", result);
+      }
+
+      SECTION("to compound")
+      {
+        std::string_view data = R"(
+let n : N, n = 3;
+let f : void -> N*(R)*R^2, void -> (n, (1,2,3), [-12, 2.3]);
+n+=5;
+let (m,t,x):N*(R)*R^2, (m,t,x) = f();
+)";
+
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "m", 8ul);
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "t", (std::vector<double>{1, 2, 3}));
+        CHECK_FUNCTION_EVALUATION_RESULT(data, "x", (TinyVector<2>{-12, 2.3}));
+      }
+    }
+  }
+
+  SECTION("errors")
+  {
+    SECTION("negative value to N")
+    {
+      SECTION("single N argument")
+      {
+        std::string_view data = R"(
+let f : N -> N, n -> 2;
+f(2);
+f(-1);
+)";
+
+        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-1)");
+      }
+
+      SECTION("multiple N arguments")
+      {
+        std::string_view data = R"(
+let f : R*N -> R, (n,x) -> n*x;
+f(-2, 3);
+f(-1,-4);
+)";
+
+        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-4)");
+      }
+
+      SECTION("single N return value")
+      {
+        std::string_view data = R"(
+let f : N*N -> N, (m,n) -> m-n;
+f(2, 1);
+f(1, 3);
+)";
+
+        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-2)");
+      }
+
+      SECTION("compound N return value")
+      {
+        std::string_view data = R"(
+let f : N*N -> N*N, (m,n) -> (m+n, m-n);
+f(3, 2);
+f(1, 4);
+)";
+
+        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-3)");
+      }
+    }
+
+    SECTION("negative value to (N)")
+    {
+      SECTION("return N -> (N)")
+      {
+        std::string_view data = R"(
+let f : N -> (N), n -> -2;
+f(2);
+)";
+
+        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-2)");
+      }
+
+      SECTION("return (Z) -> (N)")
+      {
+        std::string_view data = R"(
+let z :(Z), z = (1, 2, -3);
+let f : N -> (N), n -> z;
+f(2);
+)";
+
+        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-3)");
+      }
+
+      SECTION("return list")
+      {
+        std::string_view data = R"(
+let f : N*N -> (N), (m,n) -> (m, n, n-m);
+f(2, 1);
+f(1, 2);
+)";
+
+        CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-1)");
       }
     }
   }
diff --git a/tests/test_ItemArrayVariant.cpp b/tests/test_ItemArrayVariant.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f3b529126f6552cff212790f024728b1d3a2cda9
--- /dev/null
+++ b/tests/test_ItemArrayVariant.cpp
@@ -0,0 +1,101 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemArrayVariant.hpp>
+#include <mesh/Mesh.hpp>
+#include <utils/Messenger.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("ItemArrayVariant", "[mesh]")
+{
+  std::shared_ptr mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+
+  const Connectivity<2>& connectivity = *mesh->shared_connectivity();
+
+  using R1 = TinyVector<1>;
+  using R2 = TinyVector<2>;
+  using R3 = TinyVector<3>;
+
+  using R1x1 = TinyMatrix<1>;
+  using R2x2 = TinyMatrix<2>;
+  using R3x3 = TinyMatrix<3>;
+
+  SECTION("NodeArray<double>")
+  {
+    NodeArray<double> node_array{connectivity, 2};
+    ItemArrayVariant v(node_array);
+    REQUIRE_NOTHROW(v.get<NodeArray<const double>>());
+    REQUIRE_THROWS_WITH(v.get<NodeArray<int64_t>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<NodeArray<uint64_t>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<NodeArray<const R1>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<NodeArray<const R2>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<NodeArray<const R3>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<NodeArray<const R1x1>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<NodeArray<const R2x2>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<NodeArray<const R3x3>>(), "error: invalid ItemArray type");
+
+    REQUIRE_THROWS_WITH(v.get<EdgeArray<const double>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<FaceArray<const double>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<CellArray<const double>>(), "error: invalid ItemArray type");
+  }
+
+  SECTION("EdgeArray<R3>")
+  {
+    EdgeArray<R3> node_array{connectivity, 3};
+    ItemArrayVariant v(node_array);
+    REQUIRE_THROWS_WITH(v.get<EdgeArray<int64_t>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArray<uint64_t>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArray<double>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArray<const R1>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArray<const R2>>(), "error: invalid ItemArray type");
+    REQUIRE_NOTHROW(v.get<EdgeArray<const R3>>());
+    REQUIRE_THROWS_WITH(v.get<EdgeArray<const R1x1>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArray<const R2x2>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArray<const R3x3>>(), "error: invalid ItemArray type");
+
+    REQUIRE_THROWS_WITH(v.get<NodeArray<const R3>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<FaceArray<const R3>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<CellArray<const R3>>(), "error: invalid ItemArray type");
+  }
+
+  SECTION("FaceArray<R3x3>")
+  {
+    FaceArray<R3x3> node_array{connectivity, 2};
+    ItemArrayVariant v(node_array);
+    REQUIRE_THROWS_WITH(v.get<FaceArray<int64_t>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<FaceArray<uint64_t>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<FaceArray<double>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<FaceArray<const R1>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<FaceArray<const R2>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<FaceArray<const R3>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<FaceArray<const R1x1>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<FaceArray<const R2x2>>(), "error: invalid ItemArray type");
+    REQUIRE_NOTHROW(v.get<FaceArray<const R3x3>>());
+
+    REQUIRE_THROWS_WITH(v.get<NodeArray<const R3x3>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArray<const R3x3>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<CellArray<const R3x3>>(), "error: invalid ItemArray type");
+  }
+
+  SECTION("CellArray<int64_t>")
+  {
+    CellArray<int64_t> node_array{connectivity, 2};
+    ItemArrayVariant v(node_array);
+    REQUIRE_NOTHROW(v.get<CellArray<const int64_t>>());
+    REQUIRE_THROWS_WITH(v.get<CellArray<const uint64_t>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<CellArray<const double>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<CellArray<const R1>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<CellArray<const R2>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<CellArray<const R3>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<CellArray<const R1x1>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<CellArray<const R2x2>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<CellArray<const R3x3>>(), "error: invalid ItemArray type");
+
+    REQUIRE_THROWS_WITH(v.get<NodeArray<const int64_t>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArray<const int64_t>>(), "error: invalid ItemArray type");
+    REQUIRE_THROWS_WITH(v.get<FaceArray<const int64_t>>(), "error: invalid ItemArray type");
+  }
+}
diff --git a/tests/test_ItemArrayVariantFunctionInterpoler.cpp b/tests/test_ItemArrayVariantFunctionInterpoler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..640e3e77aa5e6ede05fe0f217d7587f7839e4d56
--- /dev/null
+++ b/tests/test_ItemArrayVariantFunctionInterpoler.cpp
@@ -0,0 +1,648 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <language/ast/ASTBuilder.hpp>
+#include <language/ast/ASTModulesImporter.hpp>
+#include <language/ast/ASTNodeDataTypeBuilder.hpp>
+#include <language/ast/ASTNodeExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionEvaluationExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionExpressionBuilder.hpp>
+#include <language/ast/ASTNodeTypeCleaner.hpp>
+#include <language/ast/ASTSymbolTableBuilder.hpp>
+#include <language/utils/PugsFunctionAdapter.hpp>
+#include <language/utils/SymbolTable.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+
+#include <language/utils/ItemArrayVariantFunctionInterpoler.hpp>
+
+#include <pegtl/string_input.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("ItemArrayVariantFunctionInterpoler", "[scheme]")
+{
+  auto same_item_array = [](auto f, auto g) -> bool {
+    using ItemIdType = typename decltype(f)::index_type;
+    if (f.sizeOfArrays() != g.sizeOfArrays()) {
+      return false;
+    }
+
+    for (ItemIdType item_id = 0; item_id < f.numberOfItems(); ++item_id) {
+      for (size_t i = 0; i < f.sizeOfArrays(); ++i) {
+        if (f[item_id][i] != g[item_id][i]) {
+          return false;
+        }
+      }
+    }
+
+    return true;
+  };
+
+  SECTION("1D")
+  {
+    constexpr size_t Dimension = 1;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+    for (const auto& named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_1d = named_mesh.mesh();
+
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+        auto xr = mesh_1d->xr();
+
+        std::string_view data = R"(
+import math;
+let B_scalar_non_linear1_1d: R^1 -> B, x -> (exp(2 * x[0]) + 3 > 4);
+let B_scalar_non_linear2_1d: R^1 -> B, x -> (exp(2 * x[0]) + 3 < 4);
+
+let N_scalar_non_linear1_1d: R^1 -> N, x -> floor(3 * x[0] * x[0] + 2);
+let N_scalar_non_linear2_1d: R^1 -> N, x -> floor(2 * x[0] * x[0]);
+
+let Z_scalar_non_linear1_1d: R^1 -> Z, x -> floor(exp(2 * x[0]) - 1);
+let Z_scalar_non_linear2_1d: R^1 -> Z, x -> floor(cos(2 * x[0]) + 0.5);
+
+let R_scalar_non_linear1_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+let R_scalar_non_linear2_1d: R^1 -> R, x -> 2 * sin(x[0]) + 1;
+let R_scalar_non_linear3_1d: R^1 -> R, x -> x[0] * sin(x[0]);
+
+let R1_non_linear1_1d: R^1 -> R^1, x -> 2 * exp(x[0]);
+let R1_non_linear2_1d: R^1 -> R^1, x -> 2 * exp(x[0])*x[0];
+
+let R2_non_linear_1d: R^1 -> R^2, x -> [2 * exp(x[0]), -3*x[0]];
+
+let R3_non_linear_1d: R^1 -> R^3, x -> [2 * exp(x[0]) + 3, x[0] - 2, 3];
+
+let R1x1_non_linear_1d: R^1 -> R^1x1, x -> (2 * exp(x[0]) * sin(x[0]) + 3);
+
+let R2x2_non_linear_1d: R^1 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0])], [3, x[0] * x[0]]];
+
+let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3], [x[0] * x[0], -4*x[0], 2*x[0]+1], [3, -6*x[0], exp(x[0])]];
+)";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        SECTION("B_scalar_non_linear_1d")
+        {
+          auto [i_symbol_f1, found_f1] = symbol_table->find("B_scalar_non_linear1_1d", position);
+          REQUIRE(found_f1);
+          REQUIRE(i_symbol_f1->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function1_symbol_id(std::get<uint64_t>(i_symbol_f1->attributes().value()), symbol_table);
+
+          auto [i_symbol_f2, found_f2] = symbol_table->find("B_scalar_non_linear2_1d", position);
+          REQUIRE(found_f2);
+          REQUIRE(i_symbol_f2->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function2_symbol_id(std::get<uint64_t>(i_symbol_f2->attributes().value()), symbol_table);
+
+          CellArray<bool> cell_array{mesh_1d->connectivity(), 2};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_array[cell_id][0]         = std::exp(2 * x[0]) + 3 > 4;
+              cell_array[cell_id][1]         = std::exp(2 * x[0]) + 3 < 4;
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell,
+                                                        {function1_symbol_id, function2_symbol_id});
+          std::shared_ptr item_data_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(cell_array, item_data_variant->get<CellArray<bool>>()));
+        }
+
+        SECTION("N_scalar_non_linear_1d")
+        {
+          auto [i_symbol_f1, found_f1] = symbol_table->find("N_scalar_non_linear1_1d", position);
+          REQUIRE(found_f1);
+          REQUIRE(i_symbol_f1->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function1_symbol_id(std::get<uint64_t>(i_symbol_f1->attributes().value()), symbol_table);
+
+          auto [i_symbol_f2, found_f2] = symbol_table->find("N_scalar_non_linear2_1d", position);
+          REQUIRE(found_f2);
+          REQUIRE(i_symbol_f2->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function2_symbol_id(std::get<uint64_t>(i_symbol_f2->attributes().value()), symbol_table);
+
+          NodeArray<uint64_t> node_array{mesh_1d->connectivity(), 2};
+          parallel_for(
+            node_array.numberOfItems(), PUGS_LAMBDA(const NodeId node_id) {
+              const TinyVector<Dimension>& x = xr[node_id];
+              node_array[node_id][0]         = std::floor(3 * x[0] * x[0] + 2);
+              node_array[node_id][1]         = std::floor(2 * x[0] * x[0]);
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::node,
+                                                        {function1_symbol_id, function2_symbol_id});
+          std::shared_ptr item_data_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(node_array, item_data_variant->get<NodeArray<uint64_t>>()));
+        }
+
+        SECTION("Z_scalar_non_linear_1d")
+        {
+          auto [i_symbol_f1, found_f1] = symbol_table->find("Z_scalar_non_linear1_1d", position);
+          REQUIRE(found_f1);
+          REQUIRE(i_symbol_f1->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function1_symbol_id(std::get<uint64_t>(i_symbol_f1->attributes().value()), symbol_table);
+
+          auto [i_symbol_f2, found_f2] = symbol_table->find("Z_scalar_non_linear2_1d", position);
+          REQUIRE(found_f2);
+          REQUIRE(i_symbol_f2->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function2_symbol_id(std::get<uint64_t>(i_symbol_f2->attributes().value()), symbol_table);
+
+          CellArray<int64_t> cell_array{mesh_1d->connectivity(), 2};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_array[cell_id][0]         = std::floor(std::exp(2 * x[0]) - 1);
+              cell_array[cell_id][1]         = std::floor(cos(2 * x[0]) + 0.5);
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell,
+                                                        {function1_symbol_id, function2_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<int64_t>>()));
+        }
+
+        SECTION("R_scalar_non_linear_1d")
+        {
+          auto [i_symbol_f1, found_f1] = symbol_table->find("R_scalar_non_linear1_1d", position);
+          REQUIRE(found_f1);
+          REQUIRE(i_symbol_f1->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function1_symbol_id(std::get<uint64_t>(i_symbol_f1->attributes().value()), symbol_table);
+
+          auto [i_symbol_f2, found_f2] = symbol_table->find("R_scalar_non_linear2_1d", position);
+          REQUIRE(found_f2);
+          REQUIRE(i_symbol_f2->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function2_symbol_id(std::get<uint64_t>(i_symbol_f2->attributes().value()), symbol_table);
+
+          auto [i_symbol_f3, found_f3] = symbol_table->find("R_scalar_non_linear3_1d", position);
+          REQUIRE(found_f3);
+          REQUIRE(i_symbol_f3->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function3_symbol_id(std::get<uint64_t>(i_symbol_f3->attributes().value()), symbol_table);
+
+          CellArray<double> cell_array{mesh_1d->connectivity(), 3};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+
+              cell_array[cell_id][0] = 2 * std::exp(x[0]) + 3;
+              cell_array[cell_id][1] = 2 * std::sin(x[0]) + 1;
+              cell_array[cell_id][2] = x[0] * std::sin(x[0]);
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell,
+                                                        {function1_symbol_id, function2_symbol_id,
+                                                         function3_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<double>>()));
+        }
+
+        SECTION("R1_non_linear_1d")
+        {
+          using DataType = TinyVector<1>;
+
+          auto [i_symbol_f1, found_f1] = symbol_table->find("R1_non_linear1_1d", position);
+          REQUIRE(found_f1);
+          REQUIRE(i_symbol_f1->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function1_symbol_id(std::get<uint64_t>(i_symbol_f1->attributes().value()), symbol_table);
+
+          auto [i_symbol_f2, found_f2] = symbol_table->find("R1_non_linear2_1d", position);
+          REQUIRE(found_f2);
+          REQUIRE(i_symbol_f2->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function2_symbol_id(std::get<uint64_t>(i_symbol_f2->attributes().value()), symbol_table);
+
+          CellArray<DataType> cell_array{mesh_1d->connectivity(), 2};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+
+              cell_array[cell_id][0] = DataType{2 * std::exp(x[0])};
+              cell_array[cell_id][1] = DataType{2 * std::exp(x[0]) * x[0]};
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell,
+                                                        {function1_symbol_id, function2_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<DataType>>()));
+        }
+
+        SECTION("R2_non_linear_1d")
+        {
+          using DataType = TinyVector<2>;
+
+          auto [i_symbol, found] = symbol_table->find("R2_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellArray<DataType> cell_array{mesh_1d->connectivity(), 1};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+
+              cell_array[cell_id][0] = DataType{2 * std::exp(x[0]), -3 * x[0]};
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, {function_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<DataType>>()));
+        }
+
+        SECTION("R3_non_linear_1d")
+        {
+          using DataType = TinyVector<3>;
+
+          auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellArray<DataType> cell_array{mesh_1d->connectivity(), 1};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_array[cell_id][0]         = DataType{2 * std::exp(x[0]) + 3, x[0] - 2, 3};
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, {function_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<DataType>>()));
+        }
+
+        SECTION("R1x1_non_linear_1d")
+        {
+          using DataType = TinyMatrix<1>;
+
+          auto [i_symbol, found] = symbol_table->find("R1x1_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellArray<DataType> cell_array{mesh_1d->connectivity(), 1};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_array[cell_id][0]         = DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3};
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, {function_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<DataType>>()));
+        }
+
+        SECTION("R2x2_non_linear_1d")
+        {
+          using DataType = TinyMatrix<2>;
+
+          auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellArray<DataType> cell_array{mesh_1d->connectivity(), 1};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_array[cell_id][0] =
+                DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3, std::sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, {function_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<DataType>>()));
+        }
+
+        SECTION("R3x3_non_linear_1d")
+        {
+          using DataType = TinyMatrix<3>;
+
+          auto [i_symbol, found] = symbol_table->find("R3x3_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellArray<DataType> cell_array{mesh_1d->connectivity(), 1};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+
+              cell_array[cell_id][0] = DataType{2 * exp(x[0]) * std::sin(x[0]) + 3,
+                                                std::sin(x[0] - 2 * x[0]),
+                                                3,
+                                                x[0] * x[0],
+                                                -4 * x[0],
+                                                2 * x[0] + 1,
+                                                3,
+                                                -6 * x[0],
+                                                std::exp(x[0])};
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, {function_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<DataType>>()));
+        }
+      }
+    }
+  }
+
+  SECTION("2D")
+  {
+    constexpr size_t Dimension = 2;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (const auto& named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_2d = named_mesh.mesh();
+
+        auto xl = MeshDataManager::instance().getMeshData(*mesh_2d).xl();
+
+        std::string_view data = R"(
+import math;
+let B_scalar_non_linear1_2d: R^2 -> B, x -> (exp(2 * x[0])< 2*x[1]);
+let B_scalar_non_linear2_2d: R^2 -> B, x -> (sin(2 * x[0])< x[1]);
+
+let R2_non_linear_2d: R^2 -> R^2, x -> [2 * exp(x[0]), -3*x[1]];
+
+let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(x[1] - 2 * x[0]), 3],
+                                           [x[1] * x[0], -4*x[1], 2*x[0]+1],
+                                           [3, -6*x[0], exp(x[1])]];
+  )";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        SECTION("B_scalar_non_linear_2d")
+        {
+          using DataType = bool;
+
+          auto [i_symbol_f1, found_f1] = symbol_table->find("B_scalar_non_linear1_2d", position);
+          REQUIRE(found_f1);
+          REQUIRE(i_symbol_f1->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function1_symbol_id(std::get<uint64_t>(i_symbol_f1->attributes().value()), symbol_table);
+
+          auto [i_symbol_f2, found_f2] = symbol_table->find("B_scalar_non_linear2_2d", position);
+          REQUIRE(found_f2);
+          REQUIRE(i_symbol_f2->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function2_symbol_id(std::get<uint64_t>(i_symbol_f2->attributes().value()), symbol_table);
+
+          FaceArray<DataType> face_array{mesh_2d->connectivity(), 2};
+          parallel_for(
+            face_array.numberOfItems(), PUGS_LAMBDA(const FaceId face_id) {
+              const TinyVector<Dimension>& x = xl[face_id];
+              face_array[face_id][0]         = std::exp(2 * x[0]) < 2 * x[1];
+              face_array[face_id][1]         = std::sin(2 * x[0]) < x[1];
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_2d, ItemType::face,
+                                                        {function1_symbol_id, function2_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(face_array, item_array_variant->get<FaceArray<DataType>>()));
+        }
+
+        SECTION("R2_non_linear_2d")
+        {
+          using DataType = TinyVector<2>;
+
+          auto [i_symbol, found] = symbol_table->find("R2_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          FaceArray<DataType> face_array{mesh_2d->connectivity(), 1};
+          parallel_for(
+            face_array.numberOfItems(), PUGS_LAMBDA(const FaceId face_id) {
+              const TinyVector<Dimension>& x = xl[face_id];
+
+              face_array[face_id][0] = DataType{2 * std::exp(x[0]), -3 * x[1]};
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_2d, ItemType::face, {function_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(face_array, item_array_variant->get<FaceArray<DataType>>()));
+        }
+
+        SECTION("R3x3_non_linear_2d")
+        {
+          using DataType = TinyMatrix<3>;
+
+          auto [i_symbol, found] = symbol_table->find("R3x3_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          FaceArray<DataType> face_array{mesh_2d->connectivity(), 1};
+          parallel_for(
+            face_array.numberOfItems(), PUGS_LAMBDA(const FaceId face_id) {
+              const TinyVector<Dimension>& x = xl[face_id];
+
+              face_array[face_id][0] = DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3,
+                                                std::sin(x[1] - 2 * x[0]),
+                                                3,
+                                                x[1] * x[0],
+                                                -4 * x[1],
+                                                2 * x[0] + 1,
+                                                3,
+                                                -6 * x[0],
+                                                std::exp(x[1])};
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_2d, ItemType::face, {function_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(face_array, item_array_variant->get<FaceArray<DataType>>()));
+        }
+      }
+    }
+  }
+
+  SECTION("3D")
+  {
+    constexpr size_t Dimension = 3;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (const auto& named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_3d = named_mesh.mesh();
+
+        auto xe = MeshDataManager::instance().getMeshData(*mesh_3d).xe();
+
+        std::string_view data = R"(
+import math;
+let R_scalar_non_linear1_3d: R^3 -> R, x -> 2 * exp(x[0]+x[2]) + 3 * x[1];
+let R_scalar_non_linear2_3d: R^3 -> R, x -> 3 * sin(x[0]+x[2]) + 2 * x[1];
+
+let R3_non_linear_3d: R^3 -> R^3, x -> [2 * exp(x[0]) + 3, x[1] - 2, 3 * x[2]];
+let R2x2_non_linear_3d: R^3 -> R^2x2,
+                          x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(x[2] - 2 * x[0])],
+                                [3, x[1] * x[0] - x[2]]];
+  )";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        SECTION("R_scalar_non_linear_3d")
+        {
+          auto [i_symbol_f1, found_f1] = symbol_table->find("R_scalar_non_linear1_3d", position);
+          REQUIRE(found_f1);
+          REQUIRE(i_symbol_f1->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function1_symbol_id(std::get<uint64_t>(i_symbol_f1->attributes().value()), symbol_table);
+
+          auto [i_symbol_f2, found_f2] = symbol_table->find("R_scalar_non_linear2_3d", position);
+          REQUIRE(found_f2);
+          REQUIRE(i_symbol_f2->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function2_symbol_id(std::get<uint64_t>(i_symbol_f2->attributes().value()), symbol_table);
+
+          EdgeArray<double> edge_array{mesh_3d->connectivity(), 2};
+          parallel_for(
+            edge_array.numberOfItems(), PUGS_LAMBDA(const EdgeId edge_id) {
+              const TinyVector<Dimension>& x = xe[edge_id];
+
+              edge_array[edge_id][0] = 2 * std::exp(x[0] + x[2]) + 3 * x[1];
+              edge_array[edge_id][1] = 3 * std::sin(x[0] + x[2]) + 2 * x[1];
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_3d, ItemType::edge,
+                                                        {function1_symbol_id, function2_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(edge_array, item_array_variant->get<EdgeArray<double>>()));
+        }
+
+        SECTION("R3_non_linear_3d")
+        {
+          using DataType = TinyVector<3>;
+
+          auto [i_symbol, found] = symbol_table->find("R3_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          EdgeArray<DataType> edge_array{mesh_3d->connectivity(), 1};
+          parallel_for(
+            edge_array.numberOfItems(), PUGS_LAMBDA(const EdgeId edge_id) {
+              const TinyVector<Dimension>& x = xe[edge_id];
+
+              edge_array[edge_id][0] = DataType{2 * std::exp(x[0]) + 3, x[1] - 2, 3 * x[2]};
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_3d, ItemType::edge, {function_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(edge_array, item_array_variant->get<EdgeArray<DataType>>()));
+        }
+
+        SECTION("R2x2_non_linear_3d")
+        {
+          using DataType = TinyMatrix<2>;
+
+          auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          EdgeArray<DataType> edge_array{mesh_3d->connectivity(), 1};
+          parallel_for(
+            edge_array.numberOfItems(), PUGS_LAMBDA(const EdgeId edge_id) {
+              const TinyVector<Dimension>& x = xe[edge_id];
+              edge_array[edge_id][0] =
+                DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3, std::sin(x[2] - 2 * x[0]), 3, x[1] * x[0] - x[2]};
+            });
+
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_3d, ItemType::edge, {function_symbol_id});
+          std::shared_ptr item_array_variant = interpoler.interpolate();
+
+          REQUIRE(same_item_array(edge_array, item_array_variant->get<EdgeArray<DataType>>()));
+        }
+      }
+    }
+  }
+}
diff --git a/tests/test_ListAffectationProcessor.cpp b/tests/test_ListAffectationProcessor.cpp
index 219b9a2f16907f3d6010d3144b7d2b903455d6fa..2c28e6e0b0e6a8c8ae8c61d4b5778c8e3617af58 100644
--- a/tests/test_ListAffectationProcessor.cpp
+++ b/tests/test_ListAffectationProcessor.cpp
@@ -45,6 +45,25 @@
     REQUIRE(value == expected_value);                                         \
   }
 
+#define CHECK_AFFECTATION_THROW_WITH(data, error_message)          \
+  {                                                                \
+    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};     \
+    auto ast = ASTBuilder::build(input);                           \
+                                                                   \
+    ASTModulesImporter{*ast};                                      \
+    ASTNodeTypeCleaner<language::import_instruction>{*ast};        \
+                                                                   \
+    ASTSymbolTableBuilder{*ast};                                   \
+    ASTNodeDataTypeBuilder{*ast};                                  \
+                                                                   \
+    ASTNodeDeclarationToAffectationConverter{*ast};                \
+    ASTNodeTypeCleaner<language::var_declaration>{*ast};           \
+                                                                   \
+    ASTNodeExpressionBuilder{*ast};                                \
+    ExecutionPolicy exec_policy;                                   \
+    REQUIRE_THROWS_WITH(ast->execute(exec_policy), error_message); \
+  }
+
 // clazy:excludeall=non-pod-global-static
 
 TEST_CASE("ListAffectationProcessor", "[language]")
@@ -738,4 +757,176 @@ let (s1,b2) : (string)*B, (s1,b2) = (s, false);
       }
     }
   }
+
+  SECTION("list from tuple affectation")
+  {
+    SECTION("from (B)")
+    {
+      std::string_view data = R"(
+let t:(B), t = (true, false, true, false, true, false, true);
+let (b,n,z,r,r1,r11,s):B*N*Z*R*R^1*R^1x1*string, (b,n,z,r,r1,r11,s)=t;
+)";
+
+      CHECK_AFFECTATION_RESULT(data, "b", (true));
+      CHECK_AFFECTATION_RESULT(data, "n", (0ul));
+      CHECK_AFFECTATION_RESULT(data, "z", (1l));
+      CHECK_AFFECTATION_RESULT(data, "r", (double{0}));
+      CHECK_AFFECTATION_RESULT(data, "r1", (TinyVector<1>{1}));
+      CHECK_AFFECTATION_RESULT(data, "r11", (TinyMatrix<1>{0}));
+      CHECK_AFFECTATION_RESULT(data, "s", (std::string{stringify(true)}));
+    }
+
+    SECTION("from (N)")
+    {
+      std::string_view data = R"(
+let t:(N), t = (1, 2, 3, 4, 5, 6);
+let (n,z,r,r1,r11,s):N*Z*R*R^1*R^1x1*string, (n,z,r,r1,r11,s)=t;
+)";
+
+      CHECK_AFFECTATION_RESULT(data, "n", (1ul));
+      CHECK_AFFECTATION_RESULT(data, "z", (2l));
+      CHECK_AFFECTATION_RESULT(data, "r", (double{3}));
+      CHECK_AFFECTATION_RESULT(data, "r1", (TinyVector<1>{4}));
+      CHECK_AFFECTATION_RESULT(data, "r11", (TinyMatrix<1>{5}));
+      CHECK_AFFECTATION_RESULT(data, "s", (std::string{stringify(6ul)}));
+    }
+
+    SECTION("from (Z)")
+    {
+      std::string_view data = R"(
+let t:(Z), t = (1, -2, 3, -4, 5, 6);
+let (n,z,r,r1,r11,s):N*Z*R*R^1*R^1x1*string, (n,z,r,r1,r11,s)=t;
+)";
+
+      CHECK_AFFECTATION_RESULT(data, "n", (1ul));
+      CHECK_AFFECTATION_RESULT(data, "z", (-2l));
+      CHECK_AFFECTATION_RESULT(data, "r", (double{3}));
+      CHECK_AFFECTATION_RESULT(data, "r1", (TinyVector<1>{-4}));
+      CHECK_AFFECTATION_RESULT(data, "r11", (TinyMatrix<1>{5}));
+      CHECK_AFFECTATION_RESULT(data, "s", (std::string{stringify(6l)}));
+    }
+
+    SECTION("from (R)")
+    {
+      std::string_view data = R"(
+let t:(R), t = (1.2, -2.3, 7.3, -4.2);
+let (r,r1,r11,s):R*R^1*R^1x1*string, (r,r1,r11,s)=t;
+)";
+
+      CHECK_AFFECTATION_RESULT(data, "r", (double{1.2}));
+      CHECK_AFFECTATION_RESULT(data, "r1", (TinyVector<1>{-2.3}));
+      CHECK_AFFECTATION_RESULT(data, "r11", (TinyMatrix<1>{7.3}));
+      CHECK_AFFECTATION_RESULT(data, "s", (std::string{stringify(-4.2)}));
+    }
+
+    SECTION("from (R^1)")
+    {
+      std::string_view data = R"(
+let t:(R^1), t = (1.2, -[2.3]);
+let (r1,s):R^1*string, (r1,s)=t;
+)";
+
+      CHECK_AFFECTATION_RESULT(data, "r1", (TinyVector<1>{1.2}));
+      CHECK_AFFECTATION_RESULT(data, "s", (std::string{stringify(TinyVector<1>{-2.3})}));
+    }
+
+    SECTION("from (R^2)")
+    {
+      std::string_view data = R"(
+let t:(R^2), t = ([1.2, 2], [2.3, 0]);
+let (r2,s):R^2*string, (r2,s)=t;
+)";
+
+      CHECK_AFFECTATION_RESULT(data, "r2", (TinyVector<2>{1.2, 2}));
+      CHECK_AFFECTATION_RESULT(data, "s", (std::string{stringify(TinyVector<2>{2.3, 0})}));
+    }
+
+    SECTION("from (R^3)")
+    {
+      std::string_view data = R"(
+let t:(R^3), t = ([1.2, 2, -1], [2.3, 0, 2]);
+let (r3,s):R^3*string, (r3,s)=t;
+)";
+
+      CHECK_AFFECTATION_RESULT(data, "r3", (TinyVector<3>{1.2, 2, -1}));
+      CHECK_AFFECTATION_RESULT(data, "s", (std::string{stringify(TinyVector<3>{2.3, 0, 2})}));
+    }
+
+    SECTION("from (R^1x1)")
+    {
+      std::string_view data = R"(
+let t:(R^1x1), t = (1.2, -[[2.3]]);
+let (r11,s):R^1x1*string, (r11,s)=t;
+)";
+
+      CHECK_AFFECTATION_RESULT(data, "r11", (TinyMatrix<1>{1.2}));
+      CHECK_AFFECTATION_RESULT(data, "s", (std::string{stringify(TinyMatrix<1>{-2.3})}));
+    }
+
+    SECTION("from (R^2x2)")
+    {
+      std::string_view data = R"(
+let t:(R^2x2), t = ([[1.2, 2], [2.3, 0]], [[1.1, 2.2], [-1.3, 2]]);
+let (r22,s):R^2x2*string, (r22,s)=t;
+)";
+
+      CHECK_AFFECTATION_RESULT(data, "r22", (TinyMatrix<2>{1.2, 2, 2.3, 0}));
+      CHECK_AFFECTATION_RESULT(data, "s", (std::string{stringify(TinyMatrix<2>{1.1, 2.2, -1.3, 2})}));
+    }
+
+    SECTION("from (R^3x3)")
+    {
+      std::string_view data = R"(
+let t:(R^3x3), t = ([[1.2, 2, -1], [2.3, 0, 2], [1, 4, 5]], 0);
+let (r33,s):R^3x3*string, (r33,s)=t;
+)";
+
+      CHECK_AFFECTATION_RESULT(data, "r33", (TinyMatrix<3>{1.2, 2, -1, 2.3, 0, 2, 1, 4, 5}));
+      CHECK_AFFECTATION_RESULT(data, "s", (std::string{stringify(TinyMatrix<3>{zero})}));
+    }
+
+    SECTION("from (string)")
+    {
+      std::string_view data = R"(
+let t:(string), t = ("foo", "bar");
+let (s1,s2):string*string, (s1,s2)=t;
+)";
+
+      CHECK_AFFECTATION_RESULT(data, "s1", (std::string{"foo"}));
+      CHECK_AFFECTATION_RESULT(data, "s2", (std::string{"bar"}));
+    }
+
+    SECTION("errors")
+    {
+      SECTION("negative (Z) -> N")
+      {
+        std::string_view data = R"(
+let t:(Z), t = (2, 3, -4, 5);
+let (a,b,c,d):Z*N*N*R , (a,b,c,d)=t;
+)";
+
+        CHECK_AFFECTATION_THROW_WITH(data, "trying to affect negative value (-4)");
+      }
+
+      SECTION("tuple too small")
+      {
+        std::string_view data = R"(
+let t:(Z), t = (2, 3, 4);
+let (a,b,c,d):Z*N*N*R , (a,b,c,d)=t;
+)";
+
+        CHECK_AFFECTATION_THROW_WITH(data, "cannot affect a (Z) of size 3 to a Z*N*N*R");
+      }
+
+      SECTION("tuple too large")
+      {
+        std::string_view data = R"(
+let t:(Z), t = (1, 2, 3, 4, 5);
+let (a,b,c,d):Z*N*N*R , (a,b,c,d)=t;
+)";
+
+        CHECK_AFFECTATION_THROW_WITH(data, "cannot affect a (Z) of size 5 to a Z*N*N*R");
+      }
+    }
+  }
 }
diff --git a/tests/test_SmallMatrix.cpp b/tests/test_SmallMatrix.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9712e13e330ee84adc55afeee1bcf81150312bc9
--- /dev/null
+++ b/tests/test_SmallMatrix.cpp
@@ -0,0 +1,416 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <Kokkos_Core.hpp>
+
+#include <utils/PugsAssert.hpp>
+#include <utils/Types.hpp>
+
+#include <algebra/SmallMatrix.hpp>
+
+#include <sstream>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("SmallMatrix", "[algebra]")
+{
+  SmallMatrix<int> A{3, 4};
+
+  REQUIRE(A.numberOfRows() == 3);
+  REQUIRE(A.numberOfColumns() == 4);
+
+  for (size_t i = 0; i < 3; ++i) {
+    for (size_t j = 0; j < 4; ++j) {
+      A(i, j) = 1 + 4 * i + j;
+    }
+  }
+
+  REQUIRE(not A.isSquare());
+
+  REQUIRE(((A(0, 0) == 1) and (A(0, 1) == 2) and (A(0, 2) == 3) and (A(0, 3) == 4) and   //
+           (A(1, 0) == 5) and (A(1, 1) == 6) and (A(1, 2) == 7) and (A(1, 3) == 8) and   //
+           (A(2, 0) == 9) and (A(2, 1) == 10) and (A(2, 2) == 11) and (A(2, 3) == 12)));
+
+  SmallMatrix<int> B{3, 4};
+  B(0, 0) = -6;
+  B(0, 1) = 5;
+  B(0, 2) = 3;
+  B(0, 3) = 8;
+
+  B(1, 0) = -4;
+  B(1, 1) = 6;
+  B(1, 2) = 5;
+  B(1, 3) = 3;
+
+  B(2, 0) = 7;
+  B(2, 1) = 1;
+  B(2, 2) = 3;
+  B(2, 3) = 6;
+
+  SECTION("copy and shallow copy")
+  {
+    SmallMatrix<const int> affected_A;
+
+    REQUIRE(affected_A.numberOfRows() == 0);
+    REQUIRE(affected_A.numberOfColumns() == 0);
+
+    affected_A = A;
+    REQUIRE(&A(0, 0) == &affected_A(0, 0));
+
+    REQUIRE(affected_A.numberOfRows() == A.numberOfRows());
+    REQUIRE(affected_A.numberOfColumns() == A.numberOfColumns());
+
+    const SmallMatrix<int> copy_A = copy(A);
+
+    REQUIRE(copy_A.numberOfRows() == A.numberOfRows());
+    REQUIRE(copy_A.numberOfColumns() == A.numberOfColumns());
+
+    bool is_same = true;
+    for (size_t i = 0; i < A.numberOfRows(); ++i) {
+      for (size_t j = 0; j < A.numberOfColumns(); ++j) {
+        is_same &= (copy_A(i, j) == A(i, j));
+      }
+    }
+    REQUIRE(is_same);
+
+    REQUIRE(&A(0, 0) != &copy_A(0, 0));
+  }
+
+  SECTION("fill and special affectations")
+  {
+    SmallMatrix<int> M(5);
+    REQUIRE(M.isSquare());
+    M.fill(1);
+    for (size_t i = 0; i < M.numberOfRows(); ++i) {
+      for (size_t j = 0; j < M.numberOfColumns(); ++j) {
+        REQUIRE(M(i, j) == 1);
+      }
+    }
+
+    M.fill(-4);
+    for (size_t i = 0; i < M.numberOfRows(); ++i) {
+      for (size_t j = 0; j < M.numberOfColumns(); ++j) {
+        REQUIRE(M(i, j) == -4);
+      }
+    }
+
+    M = zero;
+    for (size_t i = 0; i < M.numberOfRows(); ++i) {
+      for (size_t j = 0; j < M.numberOfColumns(); ++j) {
+        REQUIRE(M(i, j) == 0);
+      }
+    }
+
+    M = identity;
+    for (size_t i = 0; i < M.numberOfRows(); ++i) {
+      for (size_t j = 0; j < M.numberOfColumns(); ++j) {
+        REQUIRE(M(i, j) == (i == j));
+      }
+    }
+  }
+
+  SECTION("build from TinyMatrix")
+  {
+    TinyMatrix<3, 4> T{1, 2, 3, 4, 0.5, 1, 1.5, 2, 0.3, 0.6, 0.9, 1.2};
+    SmallMatrix S{T};
+
+    REQUIRE(S.numberOfRows() == T.numberOfRows());
+    REQUIRE(S.numberOfColumns() == T.numberOfColumns());
+
+    for (size_t i = 0; i < S.numberOfRows(); ++i) {
+      for (size_t j = 0; j < S.numberOfColumns(); ++j) {
+        REQUIRE(S(i, j) == T(i, j));
+      }
+    }
+  }
+
+  SmallMatrix AT = transpose(A);
+
+  SECTION("transposed")
+  {
+    REQUIRE(AT.numberOfColumns() == A.numberOfRows());
+    REQUIRE(AT.numberOfRows() == A.numberOfColumns());
+
+    bool is_same = true;
+    for (size_t i = 0; i < A.numberOfRows(); ++i) {
+      for (size_t j = 0; j < A.numberOfColumns(); ++j) {
+        is_same &= (AT(j, i) == A(i, j));
+      }
+    }
+    REQUIRE(is_same);
+  }
+
+  SECTION("sum")
+  {
+    SmallMatrix AaddB = copy(A);
+    AaddB += B;
+
+    REQUIRE(AaddB(0, 0) == -5);
+    REQUIRE(AaddB(0, 1) == 7);
+    REQUIRE(AaddB(0, 2) == 6);
+    REQUIRE(AaddB(0, 3) == 12);
+
+    REQUIRE(AaddB(1, 0) == 1);
+    REQUIRE(AaddB(1, 1) == 12);
+    REQUIRE(AaddB(1, 2) == 12);
+    REQUIRE(AaddB(1, 3) == 11);
+
+    REQUIRE(AaddB(2, 0) == 16);
+    REQUIRE(AaddB(2, 1) == 11);
+    REQUIRE(AaddB(2, 2) == 14);
+    REQUIRE(AaddB(2, 3) == 18);
+
+    SmallMatrix ApB = A + B;
+    for (size_t i = 0; i < ApB.numberOfRows(); ++i) {
+      for (size_t j = 0; j < ApB.numberOfColumns(); ++j) {
+        REQUIRE(AaddB(i, j) == ApB(i, j));
+      }
+    }
+  }
+
+  SECTION("difference")
+  {
+    SmallMatrix AsubsB = copy(A);
+    AsubsB -= B;
+
+    REQUIRE(AsubsB(0, 0) == 7);
+    REQUIRE(AsubsB(0, 1) == -3);
+    REQUIRE(AsubsB(0, 2) == 0);
+    REQUIRE(AsubsB(0, 3) == -4);
+
+    REQUIRE(AsubsB(1, 0) == 9);
+    REQUIRE(AsubsB(1, 1) == 0);
+    REQUIRE(AsubsB(1, 2) == 2);
+    REQUIRE(AsubsB(1, 3) == 5);
+
+    REQUIRE(AsubsB(2, 0) == 2);
+    REQUIRE(AsubsB(2, 1) == 9);
+    REQUIRE(AsubsB(2, 2) == 8);
+    REQUIRE(AsubsB(2, 3) == 6);
+
+    SmallMatrix AmB = A - B;
+    for (size_t i = 0; i < AmB.numberOfRows(); ++i) {
+      for (size_t j = 0; j < AmB.numberOfColumns(); ++j) {
+        REQUIRE(AsubsB(i, j) == AmB(i, j));
+      }
+    }
+  }
+
+  SECTION("left product by a scalar")
+  {
+    int a = 2;
+
+    SmallMatrix aA = a * A;
+
+    {
+      bool is_same = true;
+      for (size_t i = 0; i < A.numberOfRows(); ++i) {
+        for (size_t j = 0; j < A.numberOfColumns(); ++j) {
+          is_same &= (aA(i, j) == a * A(i, j));
+        }
+      }
+      REQUIRE(is_same);
+    }
+
+    SmallMatrix copy_A = copy(A);
+    copy_A *= a;
+
+    {
+      bool is_same = true;
+      for (size_t i = 0; i < A.numberOfRows(); ++i) {
+        for (size_t j = 0; j < A.numberOfColumns(); ++j) {
+          is_same &= (aA(i, j) == copy_A(i, j));
+        }
+      }
+      REQUIRE(is_same);
+    }
+  }
+
+  SECTION("divide by a scalar")
+  {
+    SmallMatrix<double> M(3, 5);
+    M(0, 0) = 2;
+    M(0, 1) = 4;
+    M(0, 2) = 6;
+    M(0, 3) = 8;
+    M(0, 4) = 10;
+
+    M(1, 0) = 12;
+    M(1, 1) = 14;
+    M(1, 2) = 16;
+    M(1, 3) = 18;
+    M(1, 4) = 20;
+
+    M(2, 0) = 22;
+    M(2, 1) = 24;
+    M(2, 2) = 26;
+    M(2, 3) = 28;
+    M(2, 4) = 30;
+
+    M /= 2;
+
+    for (size_t i = 0; i < M.numberOfRows(); ++i) {
+      for (size_t j = 0; j < M.numberOfColumns(); ++j) {
+        REQUIRE(M(i, j) == 1 + i * M.numberOfColumns() + j);
+      }
+    }
+  }
+
+  SECTION("matrix-vector product")
+  {
+    SmallVector<int> u(4);
+
+    u[0] = 1;
+    u[1] = 3;
+    u[2] = -2;
+    u[3] = -1;
+
+    SmallVector v = B * u;
+    REQUIRE(((v[0] == -5) and (v[1] == 1) and (v[2] == -2)));
+  }
+
+  SECTION("matrix-matrix product")
+  {
+    SmallMatrix<int> BAT = B * AT;
+    REQUIRE(BAT.isSquare());
+    REQUIRE(BAT.numberOfRows() == 3);
+    REQUIRE(BAT.numberOfColumns() == 3);
+
+    REQUIRE(BAT(0, 0) == 45);
+    REQUIRE(BAT(0, 1) == 85);
+    REQUIRE(BAT(0, 2) == 125);
+
+    REQUIRE(BAT(1, 0) == 35);
+    REQUIRE(BAT(1, 1) == 75);
+    REQUIRE(BAT(1, 2) == 115);
+
+    REQUIRE(BAT(2, 0) == 42);
+    REQUIRE(BAT(2, 1) == 110);
+    REQUIRE(BAT(2, 2) == 178);
+
+    SmallMatrix<int> ATB = AT * B;
+    REQUIRE(ATB.isSquare());
+    REQUIRE(ATB.numberOfRows() == 4);
+    REQUIRE(ATB.numberOfColumns() == 4);
+
+    REQUIRE(ATB(0, 0) == 37);
+    REQUIRE(ATB(0, 1) == 44);
+    REQUIRE(ATB(0, 2) == 55);
+    REQUIRE(ATB(0, 3) == 77);
+
+    REQUIRE(ATB(1, 0) == 34);
+    REQUIRE(ATB(1, 1) == 56);
+    REQUIRE(ATB(1, 2) == 66);
+    REQUIRE(ATB(1, 3) == 94);
+
+    REQUIRE(ATB(2, 0) == 31);
+    REQUIRE(ATB(2, 1) == 68);
+    REQUIRE(ATB(2, 2) == 77);
+    REQUIRE(ATB(2, 3) == 111);
+
+    REQUIRE(ATB(3, 0) == 28);
+    REQUIRE(ATB(3, 1) == 80);
+    REQUIRE(ATB(3, 2) == 88);
+    REQUIRE(ATB(3, 3) == 128);
+  }
+
+  SECTION("output")
+  {
+    std::ostringstream out;
+    out << A;
+
+    std::ostringstream expected;
+    expected << "0| 0:1 1:2 2:3 3:4\n";
+    expected << "1| 0:5 1:6 2:7 3:8\n";
+    expected << "2| 0:9 1:10 2:11 3:12\n";
+
+    REQUIRE(out.str() == expected.str());
+  }
+
+#ifndef NDEBUG
+  SECTION("matrix-vector compatibility")
+  {
+    SmallVector<int> v(5);
+    REQUIRE_THROWS_WITH(A * v, "cannot compute matrix-vector product: incompatible sizes");
+  }
+
+  SECTION("matrix-matrix compatibility")
+  {
+    SmallMatrix C = copy(A);
+    REQUIRE_THROWS_WITH(C * A, "cannot compute matrix product: incompatible sizes");
+    REQUIRE_THROWS_WITH(C -= AT, "cannot substract matrix: incompatible sizes");
+    REQUIRE_THROWS_WITH(C += AT, "cannot add matrix: incompatible sizes");
+    REQUIRE_THROWS_WITH(C + AT, "cannot compute matrix sum: incompatible sizes");
+    REQUIRE_THROWS_WITH(C - AT, "cannot compute matrix difference: incompatible sizes");
+  }
+
+  SECTION("invalid element")
+  {
+    REQUIRE_THROWS_WITH(A(A.numberOfRows(), 0), "cannot access element: invalid indices");
+    REQUIRE_THROWS_WITH(A(0, A.numberOfColumns()), "cannot access element: invalid indices");
+  }
+
+  SECTION("invalid identity affectation")
+  {
+    SmallMatrix<double> C(3, 2);
+    REQUIRE_THROWS_WITH((C = identity), "identity must be a square matrix");
+  }
+
+  SECTION("invalid constructor")
+  {
+    REQUIRE_THROWS_WITH((SmallMatrix{2, 3, SmallArray<double>{7}}), "incompatible array size and matrix dimensions");
+  }
+
+  SECTION("output with signaling NaN")
+  {
+    SmallMatrix<double> A(2, 3);
+    A(0, 0) = 1;
+    A(0, 2) = 3;
+    A(1, 0) = 2;
+    std::ostringstream A_ost;
+    A_ost << A;
+
+    std::ostringstream ref_ost;
+    ref_ost << "0| 0:1 1:nan 2:3\n";
+    ref_ost << "1| 0:2 1:nan 2:nan\n";
+
+    REQUIRE(A_ost.str() == ref_ost.str());
+  }
+
+  // SECTION("checking for bounds violation")
+  // {
+  //   REQUIRE_THROWS_AS(A(3, 0), AssertError);
+  //   REQUIRE_THROWS_AS(A(0, 4), AssertError);
+
+  //   REQUIRE_THROWS_AS(getMinor(A, 3, 0), AssertError);
+  //   REQUIRE_THROWS_AS(getMinor(A, 0, 4), AssertError);
+
+  //   const TinyMatrix<3, 4, int>& constA = A;
+  //   REQUIRE_THROWS_AS(constA(3, 0), AssertError);
+  //   REQUIRE_THROWS_AS(constA(0, 4), AssertError);
+  // }
+
+  // SECTION("checking for nan initialization")
+  // {
+  //   TinyMatrix<3, 4, double> B;
+
+  //   for (size_t i = 0; i < B.numberOfRows(); ++i) {
+  //     for (size_t j = 0; j < B.numberOfColumns(); ++j) {
+  //       REQUIRE(std::isnan(B(i, j)));
+  //     }
+  //   }
+  // }
+
+  // SECTION("checking for bad initialization")
+  // {
+  //   TinyMatrix<3, 4, int> B;
+
+  //   for (size_t i = 0; i < B.numberOfRows(); ++i) {
+  //     for (size_t j = 0; j < B.numberOfColumns(); ++j) {
+  //       REQUIRE(B(i, j) == std::numeric_limits<int>::max() / 2);
+  //     }
+  //   }
+  // }
+#endif   // NDEBUG
+}
diff --git a/tests/test_SmallVector.cpp b/tests/test_SmallVector.cpp
index 3b499c3ab3e984247b7f20f96b626f8ee413763a..b5ffc6819736b2e55d33706aeb18f1e1d28dabca 100644
--- a/tests/test_SmallVector.cpp
+++ b/tests/test_SmallVector.cpp
@@ -360,6 +360,60 @@ TEST_CASE("SmallVector", "[algebra]")
     REQUIRE(z[4] == 4);
   }
 
+  SECTION("unary minus")
+  {
+    SmallVector<int> x{5};
+    x[0] = 2;
+    x[1] = 3;
+    x[2] = 5;
+    x[3] = 7;
+    x[4] = 8;
+
+    SmallVector<const int> y = x;
+
+    SmallVector<int> z = -x;
+
+    REQUIRE(z[0] == -2);
+    REQUIRE(z[1] == -3);
+    REQUIRE(z[2] == -5);
+    REQUIRE(z[3] == -7);
+    REQUIRE(z[4] == -8);
+
+    z = -y;
+    REQUIRE(z[0] == -2);
+    REQUIRE(z[1] == -3);
+    REQUIRE(z[2] == -5);
+    REQUIRE(z[3] == -7);
+    REQUIRE(z[4] == -8);
+  }
+
+  SECTION("min/max")
+  {
+    SmallVector<int> u(5);
+    u[0] = 1;
+    u[1] = 7;
+    u[2] = 6;
+    u[3] = 2;
+    u[4] = 9;
+
+    REQUIRE(min(u) == 1);
+    REQUIRE(max(u) == 9);
+    REQUIRE(min(-u) == -9);
+    REQUIRE(max(-u) == -1);
+
+    SmallVector<int> v(5);
+    v[0] = 1;
+    v[1] = 11;
+    v[2] = 6;
+    v[3] = -2;
+    v[4] = 9;
+
+    REQUIRE(min(v) == -2);
+    REQUIRE(max(v) == 11);
+    REQUIRE(min(-v) == -11);
+    REQUIRE(max(-v) == 2);
+  }
+
   SECTION("output")
   {
     SmallVector<int> x{5};
diff --git a/tests/test_SubItemArrayPerItemVariant.cpp b/tests/test_SubItemArrayPerItemVariant.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6e3413aec062f9c785592884650a706706f21088
--- /dev/null
+++ b/tests/test_SubItemArrayPerItemVariant.cpp
@@ -0,0 +1,204 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/SubItemArrayPerItemVariant.hpp>
+#include <utils/Messenger.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("SubItemArrayPerItemVariant", "[mesh]")
+{
+  std::shared_ptr mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+  const Connectivity<3>& connectivity = *mesh->shared_connectivity();
+
+  using R1   = TinyVector<1>;
+  using R2   = TinyVector<2>;
+  using R3   = TinyVector<3>;
+  using R1x1 = TinyMatrix<1>;
+  using R2x2 = TinyMatrix<2>;
+  using R3x3 = TinyMatrix<3>;
+
+  SECTION("NodeArrayPerCell<double>")
+  {
+    NodeArrayPerCell<double> node_array{connectivity, 2};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_NOTHROW(v.get<NodeArrayPerCell<double>>());
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<int64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<uint64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<R1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<R2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<R3>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<R1x1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<R2x2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<R3x3>>(), "error: invalid SubItemArrayPerItem type");
+  }
+
+  SECTION("NodeArrayPerFace<R1>")
+  {
+    NodeArrayPerFace<R1> node_array{connectivity, 2};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<const double>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<const int64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<const uint64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_NOTHROW(v.get<NodeArrayPerFace<const R1>>());
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<const R2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<const R3>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<const R1x1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<const R2x2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<const R3x3>>(), "error: invalid SubItemArrayPerItem type");
+  }
+
+  SECTION("NodeArrayPerEdge<int64_t>")
+  {
+    NodeArrayPerEdge<int64_t> node_array{connectivity, 3};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<double>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_NOTHROW(v.get<NodeArrayPerEdge<int64_t>>());
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<uint64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R3>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R1x1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R2x2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R3x3>>(), "error: invalid SubItemArrayPerItem type");
+  }
+
+  SECTION("EdgeArrayPerCell<R2>")
+  {
+    EdgeArrayPerCell<R2> node_array{connectivity, 3};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerCell<double>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerCell<int64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerCell<uint64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerCell<R1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_NOTHROW(v.get<EdgeArrayPerCell<R2>>());
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerCell<R3>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerCell<R1x1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerCell<R2x2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerCell<R3x3>>(), "error: invalid SubItemArrayPerItem type");
+  }
+
+  SECTION("EdgeArrayPerFace<R1>")
+  {
+    EdgeArrayPerFace<R1> node_array{connectivity, 1};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerFace<const double>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerFace<int64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerFace<uint64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_NOTHROW(v.get<EdgeArrayPerFace<R1>>());
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerFace<R2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerFace<R3>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerFace<R1x1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerFace<R2x2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerFace<R3x3>>(), "error: invalid SubItemArrayPerItem type");
+  }
+
+  SECTION("EdgeArrayPerNode<double>")
+  {
+    EdgeArrayPerNode<double> node_array{connectivity, 2};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_NOTHROW(v.get<EdgeArrayPerNode<double>>());
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerNode<int64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerNode<uint64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerNode<R1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerNode<R2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerNode<R3>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerNode<R1x1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerNode<R2x2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<EdgeArrayPerNode<R3x3>>(), "error: invalid SubItemArrayPerItem type");
+  }
+
+  SECTION("FaceArrayPerCell<R3x3>")
+  {
+    FaceArrayPerCell<R3x3> node_array{connectivity, 1};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerCell<double>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerCell<int64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerCell<uint64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerCell<R1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerCell<R2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerCell<R3>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerCell<R1x1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerCell<R2x2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_NOTHROW(v.get<FaceArrayPerCell<R3x3>>());
+  }
+
+  SECTION("FaceArrayPerEdge<R2x2>")
+  {
+    FaceArrayPerEdge<R2x2> node_array{connectivity, 2};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerEdge<double>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerEdge<int64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerEdge<uint64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerEdge<R1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerEdge<R2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerEdge<R3>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerEdge<R1x1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_NOTHROW(v.get<FaceArrayPerEdge<R2x2>>());
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerEdge<R3x3>>(), "error: invalid SubItemArrayPerItem type");
+  }
+
+  SECTION("FaceArrayPerNode<uint64_t>")
+  {
+    FaceArrayPerNode<uint64_t> node_array{connectivity, 2};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerNode<double>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerNode<int64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_NOTHROW(v.get<FaceArrayPerNode<uint64_t>>());
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerNode<R1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerNode<R2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerNode<R3>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerNode<R1x1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerNode<R2x2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<FaceArrayPerNode<R3x3>>(), "error: invalid SubItemArrayPerItem type");
+  }
+
+  SECTION("NodeArrayPerCell<R1x1>")
+  {
+    NodeArrayPerCell<R1x1> node_array{connectivity, 1};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<double>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<int64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<uint64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<R1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<R2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<R3>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_NOTHROW(v.get<NodeArrayPerCell<R1x1>>());
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<R2x2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerCell<R3x3>>(), "error: invalid SubItemArrayPerItem type");
+  }
+
+  SECTION("NodeArrayPerFace<R3>")
+  {
+    NodeArrayPerFace<R3> node_array{connectivity, 3};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<double>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<int64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<uint64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<R1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<R2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_NOTHROW(v.get<NodeArrayPerFace<R3>>());
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<R1x1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<R2x2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerFace<R3x3>>(), "error: invalid SubItemArrayPerItem type");
+  }
+
+  SECTION("NodeArrayPerEdge<double>")
+  {
+    NodeArrayPerEdge<double> node_array{connectivity, 2};
+    SubItemArrayPerItemVariant v(node_array);
+    REQUIRE_NOTHROW(v.get<NodeArrayPerEdge<double>>());
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<int64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<uint64_t>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R3>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R1x1>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R2x2>>(), "error: invalid SubItemArrayPerItem type");
+    REQUIRE_THROWS_WITH(v.get<NodeArrayPerEdge<R3x3>>(), "error: invalid SubItemArrayPerItem type");
+  }
+}
diff --git a/tests/test_TinyVector.cpp b/tests/test_TinyVector.cpp
index 9c4dc2e5a2c4b185904fcf04a706fa70b572a197..6a3942e84b1c95680d8c63080b5c8b37330f139b 100644
--- a/tests/test_TinyVector.cpp
+++ b/tests/test_TinyVector.cpp
@@ -87,6 +87,23 @@ TEST_CASE("TinyVector", "[algebra]")
     REQUIRE(crossProduct(a, b) == TinyVector<3, int>(-16, 6, 7));
   }
 
+  SECTION("min/max")
+  {
+    TinyVector<5, int> u{1, 7, 6, 2, 9};
+
+    REQUIRE(min(u) == 1);
+    REQUIRE(max(u) == 9);
+    REQUIRE(min(-u) == -9);
+    REQUIRE(max(-u) == -1);
+
+    TinyVector<5, int> v{1, 11, 6, -2, 9};
+
+    REQUIRE(min(v) == -2);
+    REQUIRE(max(v) == 11);
+    REQUIRE(min(-v) == -11);
+    REQUIRE(max(-v) == 2);
+  }
+
 #ifndef NDEBUG
   SECTION("output with signaling NaN")
   {
diff --git a/tests/test_Vector.cpp b/tests/test_Vector.cpp
index e31bf166713b70f69ca108d7dace6dc07179dc5b..0d8d0e898321070f74b3cf99f8fb0e04686c9b0d 100644
--- a/tests/test_Vector.cpp
+++ b/tests/test_Vector.cpp
@@ -359,6 +359,60 @@ TEST_CASE("Vector", "[algebra]")
     REQUIRE(z[4] == 4);
   }
 
+  SECTION("unary minus")
+  {
+    Vector<int> x{5};
+    x[0] = 2;
+    x[1] = 3;
+    x[2] = 5;
+    x[3] = 7;
+    x[4] = 8;
+
+    Vector<const int> y = x;
+
+    Vector<int> z = -x;
+
+    REQUIRE(z[0] == -2);
+    REQUIRE(z[1] == -3);
+    REQUIRE(z[2] == -5);
+    REQUIRE(z[3] == -7);
+    REQUIRE(z[4] == -8);
+
+    z = -y;
+    REQUIRE(z[0] == -2);
+    REQUIRE(z[1] == -3);
+    REQUIRE(z[2] == -5);
+    REQUIRE(z[3] == -7);
+    REQUIRE(z[4] == -8);
+  }
+
+  SECTION("min/max")
+  {
+    Vector<int> u(5);
+    u[0] = 1;
+    u[1] = 7;
+    u[2] = 6;
+    u[3] = 2;
+    u[4] = 9;
+
+    REQUIRE(min(u) == 1);
+    REQUIRE(max(u) == 9);
+    REQUIRE(min(-u) == -9);
+    REQUIRE(max(-u) == -1);
+
+    Vector<int> v(5);
+    v[0] = 1;
+    v[1] = 11;
+    v[2] = 6;
+    v[3] = -2;
+    v[4] = 9;
+
+    REQUIRE(min(v) == -2);
+    REQUIRE(max(v) == 11);
+    REQUIRE(min(-v) == -11);
+    REQUIRE(max(-v) == 2);
+  }
+
   SECTION("output")
   {
     Vector<int> x{5};
diff --git a/tools/pgs-pygments.sh b/tools/pgs-pygments.sh
index 98f90c55e028546aa8fa49fc069f06b1fe3f057a..168760cc83772fd8f35629cbf999314c320a1c91 100755
--- a/tools/pgs-pygments.sh
+++ b/tools/pgs-pygments.sh
@@ -1,4 +1,4 @@
-#!/bin/env bash
+#! /bin/bash
 
 arguments=()