#include <catch2/catch.hpp>

#include <language/ast/ASTBuilder.hpp>
#include <language/ast/ASTNodeAffectationExpressionBuilder.hpp>
#include <language/ast/ASTNodeDataTypeBuilder.hpp>
#include <language/ast/ASTNodeDeclarationToAffectationConverter.hpp>
#include <language/ast/ASTNodeExpressionBuilder.hpp>
#include <language/ast/ASTNodeTypeCleaner.hpp>
#include <language/ast/ASTSymbolTableBuilder.hpp>
#include <language/utils/ASTPrinter.hpp>
#include <utils/Demangle.hpp>

#include <pegtl/string_input.hpp>

#include <sstream>

#define CHECK_AFFECTATION_RESULT(data, variable_name, expected_value)         \
  {                                                                           \
    string_input input{data, "test.pgs"};                                     \
    auto ast = ASTBuilder::build(input);                                      \
                                                                              \
    ASTSymbolTableBuilder{*ast};                                              \
    ASTNodeDataTypeBuilder{*ast};                                             \
                                                                              \
    ASTNodeDeclarationToAffectationConverter{*ast};                           \
    ASTNodeTypeCleaner<language::var_declaration>{*ast};                      \
                                                                              \
    ASTNodeExpressionBuilder{*ast};                                           \
    ExecutionPolicy exec_policy;                                              \
    ast->execute(exec_policy);                                                \
                                                                              \
    auto symbol_table = ast->m_symbol_table;                                  \
                                                                              \
    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 value      = std::get<decltype(expected_value)>(attributes.value()); \
                                                                              \
    REQUIRE(value == expected_value);                                         \
  }

#define CHECK_AFFECTATION_THROWS_WITH(data, error_message)              \
  {                                                                     \
    string_input input{data, "test.pgs"};                               \
    auto ast = ASTBuilder::build(input);                                \
                                                                        \
    ASTSymbolTableBuilder{*ast};                                        \
    ASTNodeDataTypeBuilder{*ast};                                       \
                                                                        \
    ASTNodeDeclarationToAffectationConverter{*ast};                     \
    ASTNodeTypeCleaner<language::var_declaration>{*ast};                \
                                                                        \
    REQUIRE_THROWS_WITH(ASTNodeExpressionBuilder{*ast}, error_message); \
  }

// clazy:excludeall=non-pod-global-static

TEST_CASE("AffectationProcessor", "[language]")
{
  SECTION("Affectations")
  {
    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("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^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[0] = -1;", "x", (TinyVector<1>{-1}));
      CHECK_AFFECTATION_RESULT("let x : R^1; x[0] = true;", "x", (TinyVector<1>{true}));
      CHECK_AFFECTATION_RESULT("let x : R^1; x[0] = false;", "x", (TinyVector<1>{false}));
      CHECK_AFFECTATION_RESULT("let x : R^1; x[0] = -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] = -1; x[1] = true;", "x", (TinyVector<2>{-1, true}));
      CHECK_AFFECTATION_RESULT("let x : R^2; x[0] = true; x[1] = false;", "x", (TinyVector<2>{true, false}));
      CHECK_AFFECTATION_RESULT("let x : R^2; x[0] = -0.3; x[1] = 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] = -1; x[1] = true; x[2] = false;", "x",
                               (TinyVector<3>{-1, true, false}));
      CHECK_AFFECTATION_RESULT("let x : R^3; x[0] = -0.3; x[1] = 12; x[2] = 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[0,0] = -1;", "x", (TinyMatrix<1>{-1}));
      CHECK_AFFECTATION_RESULT("let x : R^1x1; x[0,0] = true;", "x", (TinyMatrix<1>{true}));
      CHECK_AFFECTATION_RESULT("let x : R^1x1; x[0,0] = false;", "x", (TinyMatrix<1>{false}));
      CHECK_AFFECTATION_RESULT("let x : R^1x1; x[0,0] = -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,0] = -1; x[0,1] = true; x[1,0] = 2; x[1,1] = 3.3;", "x",
                               (TinyMatrix<2>{-1, true, 2, 3.3}));
      CHECK_AFFECTATION_RESULT("let x : R^2x2; x[0,0] = true; x[0,1] = false; x[1,0] = 2.1; x[1,1] = -1;", "x",
                               (TinyMatrix<2>{true, false, 2.1, -1}));
      CHECK_AFFECTATION_RESULT("let x : R^2x2; x[0,0] = -0.3; x[0,1] = 12; x[1,0] = 1.3; x[1,1] = 7;", "x",
                               (TinyMatrix<2>{-0.3, 12, 1.3, 7}));

      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,0] = -1; x[0,1] = true; x[0,2] = false; x[1,0] = -11; x[1,1] = 4; "
                               "x[1,2] = 3; x[2,0] = 6; x[2,1] = -3; x[2,2] = 5;",
                               "x", (TinyMatrix<3>{-1, true, false, -11, 4, 3, 6, -3, 5}));

      CHECK_AFFECTATION_RESULT("let x : R^3x3, x = 0;", "x", (TinyMatrix<3>{zero}));
    }
  }

  SECTION("+=")
  {
    SECTION("N")
    {
      CHECK_AFFECTATION_RESULT("let n : N, n = 1; n += 3;", "n", 4ul);
      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let n : N, n = 1; n += m;", "n", 3ul);
      CHECK_AFFECTATION_RESULT("let n : N, n = 1; n += true;", "n", 2ul);
      CHECK_AFFECTATION_RESULT("let n : N, n = 3; n += false;", "n", 3ul);
    }

    SECTION("Z")
    {
      CHECK_AFFECTATION_RESULT("let z : Z, z = 1; z += 3;", "z", 4l);
      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let z : Z, z = 1; z += m;", "z", 3l);
      CHECK_AFFECTATION_RESULT("let z : Z, z = 1; z += true;", "z", 2l);
      CHECK_AFFECTATION_RESULT("let z : Z, z = 3; z += false;", "z", 3l);
    }

    SECTION("R")
    {
      CHECK_AFFECTATION_RESULT("let r : R, r = 1.2; r += 2.3;", "r", 3.5);
      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let r : R, r = 1.3; r += m;", "r", 3.3);
      CHECK_AFFECTATION_RESULT("let r : R, r = 1.1; r += true;", "r", 2.1);
      CHECK_AFFECTATION_RESULT("let r : R, r = 3.3; r += false;", "r", 3.3);
      CHECK_AFFECTATION_RESULT("let r : R, r = 2; r += 1.1;", "r", 3.1);
    }

    SECTION("R^1")
    {
      CHECK_AFFECTATION_RESULT("let x : R^1, x = -1; let y : R^1, y = 1; x += y;", "x",
                               (TinyVector<1>{-1} + TinyVector<1>{1}));
      CHECK_AFFECTATION_RESULT("let x : R^1, x = 2; x[0] += 1;", "x", (TinyVector<1>{2} + TinyVector<1>{1}));
    }

    SECTION("R^2")
    {
      CHECK_AFFECTATION_RESULT("let x : R^2, x = (-1, true); let y : R^2, y = (1,3); x += y;", "x",
                               (TinyVector<2>{-1, true} + TinyVector<2>{1, 3}));
      CHECK_AFFECTATION_RESULT("let x : R^2, x = (-1, true); x[0] += 2; x[1] += 1;", "x",
                               (TinyVector<2>{-1, true} + TinyVector<2>{2, 1}));
    }

    SECTION("R^3")
    {
      CHECK_AFFECTATION_RESULT("let x : R^3, x = (-1, true, false); let y : R^3, y = (1,2,3); x += y;", "x",
                               (TinyVector<3>{-1, true, false} + TinyVector<3>{1, 2, 3}));
      CHECK_AFFECTATION_RESULT("let x : R^3, x = (-0.3, 12, 6.2); x[0] += 1; x[1] += -3; x[2] += 1;", "x",
                               (TinyVector<3>{-0.3, 12, 6.2} + TinyVector<3>{1, -3, 1}));
    }
  }

  SECTION("-=")
  {
    SECTION("N")
    {
      CHECK_AFFECTATION_RESULT("let n : N, n = 3; n -= 2;", "n", 1ul);
      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let n : N, n = 4; n -= m;", "n", 2ul);
      CHECK_AFFECTATION_RESULT("let n : N, n = 1; n -= true;", "n", 0ul);
      CHECK_AFFECTATION_RESULT("let n : N, n = 3; n -= false;", "n", 3ul);
    }

    SECTION("Z")
    {
      CHECK_AFFECTATION_RESULT("let z : Z, z = 1; z -= 3;", "z", -2l);
      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let z : Z, z = 1; z -= m;", "z", -1l);
      CHECK_AFFECTATION_RESULT("let z : Z, z = 1; z -= true;", "z", 0l);
      CHECK_AFFECTATION_RESULT("let z : Z, z = 3; z -= false;", "z", 3l);
    }

    SECTION("R")
    {
      CHECK_AFFECTATION_RESULT("let r : R, r = 1.1; r -= 2;", "r", (1.1 - 2l));
      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let r : R, r = 1.3; r -= m;", "r", (1.3 - 2ul));
      CHECK_AFFECTATION_RESULT("let r : R, r = 1.1; r -= true;", "r", (1.1 - true));
      CHECK_AFFECTATION_RESULT("let r : R, r = 3.3; r -= false;", "r", 3.3);
      CHECK_AFFECTATION_RESULT("let r : R, r = 2; r -= 1.1;", "r", (2. - 1.1));
    }

    SECTION("R^1")
    {
      CHECK_AFFECTATION_RESULT("let x : R^1, x = -1; let y : R^1, y = 1; x -= y;", "x",
                               (TinyVector<1>{-1} - TinyVector<1>{1}));
      CHECK_AFFECTATION_RESULT("let x : R^1, x = 2; x[0] -= 1;", "x", (TinyVector<1>{2} - TinyVector<1>{1}));
    }

    SECTION("R^2")
    {
      CHECK_AFFECTATION_RESULT("let x : R^2, x = (-1, true); let y : R^2, y = (1,3); x -= y;", "x",
                               (TinyVector<2>{-1, true} - TinyVector<2>{1, 3}));
      CHECK_AFFECTATION_RESULT("let x : R^2, x = (-1, true); x[0] -= 2; x[1] -= 1;", "x",
                               (TinyVector<2>{-1, true} - TinyVector<2>{2, 1}));
    }

    SECTION("R^3")
    {
      CHECK_AFFECTATION_RESULT("let x : R^3, x = (-1, true, false); let y : R^3, y = (1,2,3); x-=y;", "x",
                               (TinyVector<3>{-1, true, false} - TinyVector<3>{1, 2, 3}));
      CHECK_AFFECTATION_RESULT("let x : R^3, x = (-0.3, 12, 6.2); x[0] -= 1; x[1] -= -3; x[2] -= 1;", "x",
                               (TinyVector<3>{-0.3, 12, 6.2} - TinyVector<3>{1, -3, 1}));
    }
  }

  SECTION("*=")
  {
    SECTION("N")
    {
      CHECK_AFFECTATION_RESULT("let n : N, n = 3; n *= 2;", "n", 6ul);
      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let n : N, n = 4; n *= m;", "n", 8ul);
      CHECK_AFFECTATION_RESULT("let n : N, n = 1; n *= true;", "n", 1ul);
      CHECK_AFFECTATION_RESULT("let n : N, n = 3; n *= false;", "n", 0ul);
    }

    SECTION("Z")
    {
      CHECK_AFFECTATION_RESULT("let z : Z, z = 1; z *= 3;", "z", 3l);
      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let z : Z, z = -2; z *= m;", "z", -4l);
      CHECK_AFFECTATION_RESULT("let z : Z, z = 1; z *= true;", "z", 1l);
      CHECK_AFFECTATION_RESULT("let z : Z, z = 3; z *= false;", "z", 0l);
    }

    SECTION("R")
    {
      CHECK_AFFECTATION_RESULT("let r : R, r = 1.1; r *= 2;", "r", (1.1 * 2l));
      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let r : R, r = 1.3; r *= m;", "r", (1.3 * 2ul));
      CHECK_AFFECTATION_RESULT("let r : R, r = 1.1; r *= true;", "r", (1.1 * true));
      CHECK_AFFECTATION_RESULT("let r : R, r = 3.3; r *= false;", "r", (3.3 * false));
      CHECK_AFFECTATION_RESULT("let r : R, r = 2; r *= 1.1;", "r", (2. * 1.1));
    }

    SECTION("R^1")
    {
      CHECK_AFFECTATION_RESULT("let x : R^1, x = 2; x *= 2;", "x", (TinyVector<1>{TinyVector<1>{2} *= 2}));
      CHECK_AFFECTATION_RESULT("let x : R^1, x = 2; x[0] *= 1.3;", "x", (TinyVector<1>{2 * 1.3}));
    }

    SECTION("R^2")
    {
      CHECK_AFFECTATION_RESULT("let x : R^2, x = (-1, true);  x *= 3;", "x",
                               (TinyVector<2>{TinyVector<2>{-1, true} *= 3}));
      CHECK_AFFECTATION_RESULT("let x : R^2, x = (-1, true); x[0] *= 2; x[1] *= 3;", "x",
                               (TinyVector<2>{-1 * 2, true * 3}));
    }

    SECTION("R^3")
    {
      CHECK_AFFECTATION_RESULT("let x : R^3, x = (-1, true, false); x*=5.2;", "x",
                               (TinyVector<3>{TinyVector<3>{-1, true, false} *= 5.2}));
      CHECK_AFFECTATION_RESULT("let x : R^3, x = (-0.3, 12, 6.2); x[0] *= -1; x[1] *= -3; x[2] *= 2;", "x",
                               (TinyVector<3>{-0.3 * -1, 12 * -3, 6.2 * 2}));
    }

    SECTION("R^1x1")
    {
      CHECK_AFFECTATION_RESULT("let x : R^1x1, x = 2; x *= 2;", "x", (TinyMatrix<1>{TinyMatrix<1>{2} *= 2}));
      CHECK_AFFECTATION_RESULT("let x : R^1x1, x = 2; x[0,0] *= 1.3;", "x", (TinyMatrix<1>{2 * 1.3}));
    }

    SECTION("R^2x2")
    {
      CHECK_AFFECTATION_RESULT("let x : R^2x2, x = (-1, true, 3, 6);  x *= 3;", "x",
                               (TinyMatrix<2>{TinyMatrix<2>{-1, true, 3, 6} *= 3}));
      CHECK_AFFECTATION_RESULT("let x : R^2x2, x = (-1, true, 3, 6); x[0,0] *= 2; x[1,1] *= 3;", "x",
                               (TinyMatrix<2>{-1 * 2, true, 3, 6 * 3}));
    }

    SECTION("R^3x3")
    {
      CHECK_AFFECTATION_RESULT("let x : R^3x3, x = (-1, true, false, 2, -3, 11, 5, -4, 2); x*=5.2;", "x",
                               (TinyMatrix<3>{TinyMatrix<3>{-1, true, false, 2, -3, 11, 5, -4, 2} *= 5.2}));
      CHECK_AFFECTATION_RESULT("let x : R^3x3, x = (-0.3, 12, 6.2, 2, -3, 11, 5, -4, 2); x[0,0] *= -1; x[0,1] *= -3; "
                               "x[0,2] *= 2; x[1,1] *= 2; x[2,1] *= 6; x[2,2] *= 2;",
                               "x", (TinyMatrix<3>{-0.3 * -1, 12 * -3, 6.2 * 2, 2, -3 * 2, 11, 5, (-4) * 6, 2 * 2}));
    }
  }

  SECTION("/=")
  {
    SECTION("N")
    {
      CHECK_AFFECTATION_RESULT("let n : N, n = 4; n /= 2;", "n", 2ul);
      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let n : N, n = 6; n /= m;", "n", 3ul);
      CHECK_AFFECTATION_RESULT("let n : N, n = 1; n /= true;", "n", 1ul);
    }

    SECTION("Z")
    {
      CHECK_AFFECTATION_RESULT("let z : Z, z = 7; z /= -3;", "z", -2l);
      CHECK_AFFECTATION_RESULT("let m : N, m = 3; let z : Z, z = 6; z /= m;", "z", 2l);
      CHECK_AFFECTATION_RESULT("let z : Z, z = 6; z /= true;", "z", 6l);
    }

    SECTION("R")
    {
      CHECK_AFFECTATION_RESULT("let r : R, r = 1.1; r /= 2;", "r", (1.1 / 2l));
      CHECK_AFFECTATION_RESULT("let m : N, m = 2; let r : R, r = 1.3; r /= m;", "r", (1.3 / 2ul));
      CHECK_AFFECTATION_RESULT("let r : R, r = 1.1; r /= true;", "r", (1.1 / true));
      CHECK_AFFECTATION_RESULT("let r : R, r = 2; r /= 1.1;", "r", (2. / 1.1));
    }

    SECTION("R^1")
    {
      CHECK_AFFECTATION_RESULT("let x : R^1, x = 2; x[0] /= 1.3;", "x", (TinyVector<1>{2 / 1.3}));
    }

    SECTION("R^2")
    {
      CHECK_AFFECTATION_RESULT("let x : R^2, x = (-1, true); x[0] /= 2; x[1] /= 3;", "x",
                               (TinyVector<2>{-1. / 2., true / 3.}));
    }

    SECTION("R^3")
    {
      CHECK_AFFECTATION_RESULT("let x : R^3, x = (-0.3, 12, 6.2); x[0] /= -1.2; x[1] /= -3.1; x[2] /= 2.4;", "x",
                               (TinyVector<3>{-0.3 / -1.2, 12 / -3.1, 6.2 / 2.4}));
    }

    SECTION("R^1x1")
    {
      CHECK_AFFECTATION_RESULT("let x : R^1x1, x = 2; x[0,0] /= 1.3;", "x", (TinyMatrix<1>{2 / 1.3}));
    }

    SECTION("R^2x2")
    {
      CHECK_AFFECTATION_RESULT("let x : R^2x2, x = (-1, true, 3, 1); x[0,0] /= 2; x[0,1] /= 3; x[1,0] /= 0.5; x[1,1] "
                               "/= 4;",
                               "x", (TinyMatrix<2>{-1. / 2., true / 3., 3 / 0.5, 1. / 4}));
    }

    SECTION("R^3x3")
    {
      CHECK_AFFECTATION_RESULT("let x : R^3x3, x = (-0.3, 12, 6.2, 1.2, 3, 5, 1, 11, 2); x[0,0] /= -1.2; x[0,1] /= "
                               "-3.1; x[0,2] /= 2.4; x[1,0] /= -1.6; x[1,1] /= -3.1; x[1,2] /= 2.4; x[2,0] /= 0.4; "
                               "x[2,1] /= -1.7; x[2,2] /= 1.2;",
                               "x",
                               (TinyMatrix<3>{-0.3 / -1.2, 12 / -3.1, 6.2 / 2.4, 1.2 / -1.6, 3 / -3.1, 5 / 2.4, 1 / 0.4,
                                              11 / -1.7, 2 / 1.2}));
    }
  }

  SECTION("errors")
  {
    SECTION("invalid implicit conversions")
    {
      SECTION("-> B")
      {
        CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 1; let b : B; b = n;", "undefined affectation type: B = N");
        CHECK_AFFECTATION_THROWS_WITH("let b : B; b = 1;", "undefined affectation type: B = Z");
        CHECK_AFFECTATION_THROWS_WITH("let b : B; b = 2.3;", "undefined affectation type: B = R");
        CHECK_AFFECTATION_THROWS_WITH("let b : B; b = \"foo\";", "undefined affectation type: B = string");
      }

      SECTION("-> N")
      {
        CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2.3;", "undefined affectation type: N = R");
        CHECK_AFFECTATION_THROWS_WITH("let n : N, n = \"bar\";", "undefined affectation type: N = string");

        CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n += 1.1;", "undefined affectation type: N += R");
        CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n += \"foo\";", "undefined affectation type: N += string");

        CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n -= 1.1;", "undefined affectation type: N -= R");
        CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n -= \"bar\";", "undefined affectation type: N -= string");

        CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n *= 2.51;", "undefined affectation type: N *= R");
        CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n *= \"foobar\";", "undefined affectation type: N *= string");

        CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n /= 2.51;", "undefined affectation type: N /= R");
        CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n /= \"foo\";", "undefined affectation type: N /= string");
      }

      SECTION("-> Z")
      {
        CHECK_AFFECTATION_THROWS_WITH("let z : Z, z = -2.3;", "undefined affectation type: Z = R");
        CHECK_AFFECTATION_THROWS_WITH("let z : Z, z = \"foobar\";", "undefined affectation type: Z = string");

        CHECK_AFFECTATION_THROWS_WITH("let z : Z, z = 2; z += 1.1;", "undefined affectation type: Z += R");
        CHECK_AFFECTATION_THROWS_WITH("let z : Z, z = 2; z += \"foo\";", "undefined affectation type: Z += string");

        CHECK_AFFECTATION_THROWS_WITH("let z : Z, z = 2; z -= 2.1;", "undefined affectation type: Z -= R");
        CHECK_AFFECTATION_THROWS_WITH("let z : Z, z = 2; z -= \"bar\";", "undefined affectation type: Z -= string");

        CHECK_AFFECTATION_THROWS_WITH("let z : Z, z = 2; z *= -2.51;", "undefined affectation type: Z *= R");
        CHECK_AFFECTATION_THROWS_WITH("let z : Z, z = 2; z *= \"foobar\";", "undefined affectation type: Z *= string");

        CHECK_AFFECTATION_THROWS_WITH("let z : Z, z = 4; z /= -2.;", "undefined affectation type: Z /= R");
        CHECK_AFFECTATION_THROWS_WITH("let z : Z, z = 2; z /= \"foo\";", "undefined affectation type: Z /= string");
      }

      SECTION("-> R")
      {
        CHECK_AFFECTATION_THROWS_WITH("let x : R, x = \"foobar\";", "undefined affectation type: R = string");
        CHECK_AFFECTATION_THROWS_WITH("let x : R, x = 2.3; x += \"foo\";", "undefined affectation type: R += string");
        CHECK_AFFECTATION_THROWS_WITH("let x : R, x = 2.1; x -= \"bar\";", "undefined affectation type: R -= string");
        CHECK_AFFECTATION_THROWS_WITH("let x : R, x = 1.2; x *= \"foobar\";",
                                      "undefined affectation type: R *= string");
        CHECK_AFFECTATION_THROWS_WITH("let x : R, x =-2.3; x /= \"foo\";", "undefined affectation type: R /= string");
      }

      SECTION("-> R^n")
      {
        CHECK_AFFECTATION_THROWS_WITH("let x : R^2, x = \"foobar\";", "undefined affectation type: R^2 = string");
        CHECK_AFFECTATION_THROWS_WITH("let x : R^3, x = \"foobar\";", "undefined affectation type: R^3 = string");

        CHECK_AFFECTATION_THROWS_WITH("let x : R^2, x = 3.2;", "undefined affectation type: R^2 = R");
        CHECK_AFFECTATION_THROWS_WITH("let x : R^3, x = 2.3;", "undefined affectation type: R^3 = R");

        CHECK_AFFECTATION_THROWS_WITH("let x : R^2, x = 4;", "invalid integral value (0 is the solely valid value)");
        CHECK_AFFECTATION_THROWS_WITH("let x : R^3, x = 3;", "invalid integral value (0 is the solely valid value)");

        CHECK_AFFECTATION_THROWS_WITH("let x : R^1, x = 0; let y : R^2, y = x;",
                                      "undefined affectation type: R^2 = R^1");
        CHECK_AFFECTATION_THROWS_WITH("let x : R^1, x = 0; let y : R^3, y = x;",
                                      "undefined affectation type: R^3 = R^1");

        CHECK_AFFECTATION_THROWS_WITH("let x : R^2, x = 0; let y : R^1, y = x;",
                                      "undefined affectation type: R^1 = R^2");
        CHECK_AFFECTATION_THROWS_WITH("let x : R^2, x = 0; let y : R^3, y = x;",
                                      "undefined affectation type: R^3 = R^2");

        CHECK_AFFECTATION_THROWS_WITH("let x : R^3, x = 0; let y : R^1, y = x;",
                                      "undefined affectation type: R^1 = R^3");
        CHECK_AFFECTATION_THROWS_WITH("let x : R^3, x = 0; let y : R^2, y = x;",
                                      "undefined affectation type: R^2 = R^3");
      }
    }
  }
}