text:
#include "./ut_support/UnitTestSupport.hpp"

#include <swizzle/ast/Matcher.hpp>
#include <swizzle/ast/nodes/Attribute.hpp>
#include <swizzle/ast/nodes/Bitfield.hpp>
#include <swizzle/ast/nodes/BitfieldField.hpp>
#include <swizzle/ast/nodes/CharLiteral.hpp>
#include <swizzle/ast/nodes/Comment.hpp>
#include <swizzle/ast/nodes/Enum.hpp>
#include <swizzle/ast/nodes/EnumField.hpp>
#include <swizzle/ast/nodes/Extern.hpp>
#include <swizzle/ast/nodes/Import.hpp>
#include <swizzle/ast/nodes/MultilineComment.hpp>
#include <swizzle/ast/nodes/Namespace.hpp>
#include <swizzle/ast/nodes/StringLiteral.hpp>
#include <swizzle/ast/nodes/Struct.hpp>
#include <swizzle/ast/nodes/StructField.hpp>
#include <swizzle/ast/nodes/TypeAlias.hpp>

#include <swizzle/lexer/Tokenizer.hpp>
#include <swizzle/parser/Parser.hpp>

#include <boost/filesystem.hpp>
#include <boost/utility/string_view.hpp>
#include <deque>

namespace {

    using namespace swizzle::ast;
    using namespace swizzle::lexer;
    using namespace swizzle::parser;

    struct CreateTokenCallback
    {
        CreateTokenCallback(std::deque<TokenInfo>& tokens)
            : tokens_(tokens)
        {
        }

        void operator()(const TokenInfo& token)
        {
            tokens_.push_back(token);
        }

    private:
        std::deque<TokenInfo>& tokens_;
    };

    struct ParserFixture
    {
        void tokenize(const boost::string_view& sv)
        {
            for(std::size_t position = 0, end = sv.length(); position < end; ++position)
            {
                tokenizer.consume(sv, position);
            }

            tokenizer.flush();
        }

        void parse()
        {
            for(const auto token : tokens)
            {
                parser.consume(token);
            }

            parser.finalize();
        }

        std::deque<TokenInfo> tokens;

        CreateTokenCallback callback = CreateTokenCallback(tokens);
        Tokenizer<CreateTokenCallback> tokenizer = Tokenizer<CreateTokenCallback>("test.swizzle", callback);

        Parser parser;
    };

    TEST_FIXTURE(ParserFixture, verifyConstruction)
    {
    }

    struct WhenInputIsComment : public ParserFixture
    {
        const boost::string_view sv = boost::string_view("// comment");
    };

    TEST_FIXTURE(WhenInputIsComment, verifyConsume)
    {
        auto matcher = Matcher().hasChildOf<nodes::Comment>();
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));
    }

    struct WhenInputIsMultilineComment : public ParserFixture
    {
        const boost::string_view sv = boost::string_view("// multi-line \\\n   comment");
    };

    TEST_FIXTURE(WhenInputIsMultilineComment, verifyConsume)
    {
        auto matcher = Matcher().hasChildOf<nodes::MultilineComment>();
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));
    }

    struct WhenInputIsImportAndFileDoesntExist : public ParserFixture
    {
        const boost::string_view sv= boost::string_view("import Flan;");
    };

    TEST_FIXTURE(WhenInputIsImportAndFileDoesntExist, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::ParserError);
    }

    struct WhenInputIsImport : public ParserFixture
    {
        WhenInputIsImport()
        {
            boost::filesystem::fstream file(testFile, std::ios::out | std::ios::app);
        }

        ~WhenInputIsImport()
        {
            boost::filesystem::remove(testFile);
        }

        const boost::string_view sv = boost::string_view("import Flan;");
        const boost::filesystem::path testFile = boost::filesystem::path("Flan.swizzle");
    };

    TEST_FIXTURE(WhenInputIsImport, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Import>().bind("import");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("import_0");
        REQUIRE CHECK(node);

        const auto& import = static_cast<nodes::Import&>(*node);
        CHECK_EQUAL("Flan.swizzle", import.path());
    }

    struct WhenInputIsImportWithNamespace : public ParserFixture
    {
        WhenInputIsImportWithNamespace()
        {
            boost::filesystem::create_directories(testFile.parent_path());
            boost::filesystem::fstream file(testFile, std::ios::out | std::ios::app);
        }

        ~WhenInputIsImportWithNamespace()
        {
            boost::filesystem::remove(testFile);
            boost::filesystem::remove_all("./foo");
        }

        const boost::string_view sv = boost::string_view("import foo::bar::Flan;");
        const boost::filesystem::path testFile = boost::filesystem::path("foo/bar/Flan.swizzle");
    };

    TEST_FIXTURE(WhenInputIsImportWithNamespace, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Import>().bind("import");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("import_0");
        REQUIRE CHECK(node);

        const auto& import = static_cast<nodes::Import&>(*node);
        CHECK_EQUAL("foo/bar/Flan.swizzle", import.path());
    }

    struct WhenInputIsExtern : public ParserFixture
    {
        const boost::string_view sv = boost::string_view("extern Magui;");
    };

    TEST_FIXTURE(WhenInputIsExtern, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Extern>().bind("extern");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("extern_0");
        REQUIRE CHECK(node);

        const auto& external = static_cast<nodes::Extern&>(*node);
        CHECK_EQUAL("Magui", external.externType().token().to_string());
    }

    struct WhenInputIsExternWithNamespace : public ParserFixture
    {
        const boost::string_view sv = boost::string_view("extern foo::Magui;");
    };

    TEST_FIXTURE(WhenInputIsExternWithNamespace, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Extern>().bind("extern");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("extern_0");
        REQUIRE CHECK(node);

        const auto& external = static_cast<nodes::Extern&>(*node);
        CHECK_EQUAL("foo::Magui", external.externType().token().to_string());
    }

    struct WhenInputIsExternWithMultipleNamespaces : public ParserFixture
    {
        const boost::string_view sv = boost::string_view("extern foo::bar::baz::Magui;");
    };

    TEST_FIXTURE(WhenInputIsExternWithMultipleNamespaces, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Extern>().bind("extern");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("extern_0");
        REQUIRE CHECK(node);

        const auto& external = static_cast<nodes::Extern&>(*node);
        CHECK_EQUAL("foo::bar::baz::Magui", external.externType().token().to_string());
    }

    struct WhenInputIsNamespace : public ParserFixture
    {
        const boost::string_view sv = boost::string_view("namespace foo;");
    };

    TEST_FIXTURE(WhenInputIsNamespace, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Namespace>().bind("namespace");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("namespace_0");
        REQUIRE CHECK(node);

        const auto& nameSpace = static_cast<nodes::Namespace&>(*node);
        CHECK_EQUAL("foo", nameSpace.info().token().to_string());
    }

    struct WhenInputIsNestedNamespace : public ParserFixture
    {
        const boost::string_view sv = boost::string_view("namespace foo::bar;");
    };

    TEST_FIXTURE(WhenInputIsNestedNamespace, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Namespace>().bind("namespace");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("namespace_0");
        REQUIRE CHECK(node);

        const auto& nameSpace = static_cast<nodes::Namespace&>(*node);
        CHECK_EQUAL("foo::bar", nameSpace.info().token().to_string());
    }

    struct WhenInputIsMultipleNestedNamespaces : public ParserFixture
    {
        const boost::string_view sv = boost::string_view("namespace foo::bar::baz;");
    };

    TEST_FIXTURE(WhenInputIsMultipleNestedNamespaces, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Namespace>().bind("namespace");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("namespace_0");
        REQUIRE CHECK(node);

        const auto& nameSpace = static_cast<nodes::Namespace&>(*node);
        CHECK_EQUAL("foo::bar::baz", nameSpace.info().token().to_string());
    }

    struct WhenInputIsEnum : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "iron," "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnum, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Enum>().bind("enum");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("enum_0");
        REQUIRE CHECK(node);

        const auto& Enum = static_cast<nodes::Enum&>(*node);
        CHECK_EQUAL("foo::Metal", Enum.name());
        CHECK_EQUAL("u8", Enum.underlying().token().to_string());

        auto fieldsMatcher = Matcher().getChildrenOf<nodes::EnumField>().bind("fields");
        CHECK(fieldsMatcher(node));

        const auto field0 = fieldsMatcher.bound("fields_0");
        REQUIRE CHECK(field0);

        const auto& f0 = static_cast<nodes::EnumField&>(*field0);
        CHECK_EQUAL("iron", f0.name().token().to_string());
        CHECK_EQUAL("u8", f0.underlying().token().to_string());
        CHECK_EQUAL(0U, boost::get<std::uint8_t>(f0.value()));

        const auto field1 = fieldsMatcher.bound("fields_1");
        REQUIRE CHECK(field1);

        const auto& f1 = static_cast<nodes::EnumField&>(*field1);
        CHECK_EQUAL("copper", f1.name().token().to_string());
        CHECK_EQUAL("u8", f1.underlying().token().to_string());
        CHECK_EQUAL(1U, boost::get<std::uint8_t>(f1.value()));

        const auto field2 = fieldsMatcher.bound("fields_2");
        REQUIRE CHECK(field2);

        const auto& f2 = static_cast<nodes::EnumField&>(*field2);
        CHECK_EQUAL("gold", f2.name().token().to_string());
        CHECK_EQUAL("u8", f2.underlying().token().to_string());
        CHECK_EQUAL(2U, boost::get<std::uint8_t>(f2.value()));
    }

    struct WhenInputIsEnumWithAttribute : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@volatile" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "iron," "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithAttribute, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsEnumWithKeyValueAttributeNumeric : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@volatile=1" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "iron," "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithKeyValueAttributeNumeric, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsEnumWithKeyValueAttributeHex : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@volatile=0x02" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "iron," "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithKeyValueAttributeHex, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsEnumWithKeyValueAttributeWithChar : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@volatile='a'" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "iron," "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithKeyValueAttributeWithChar, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsEnumWithKeyValueAttributeWithStringLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@volatile=\"boo\"" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "iron," "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithKeyValueAttributeWithStringLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsEnumWithAttributeOnEnumValue : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "@blah" "\n"
            "\t" "iron," "\n"
            "@blooie" "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithAttributeOnEnumValue, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsEnumWithKeyValueAttributeOnEnumValueKeyIsNumericLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "@blah=1" "\n"
            "\t" "iron," "\n"
            "@blooie" "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithKeyValueAttributeOnEnumValueKeyIsNumericLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsEnumWithKeyValueAttributeOnEnumValueKeyIsHexLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "@blah=0x04" "\n"
            "\t" "iron," "\n"
            "@blooie" "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithKeyValueAttributeOnEnumValueKeyIsHexLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsEnumWithKeyValueAttributeOnEnumValueKeyIsCharLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "@blah='a'" "\n"
            "\t" "iron," "\n"
            "@blooie" "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithKeyValueAttributeOnEnumValueKeyIsCharLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsEnumWithKeyValueAttributeOnEnumValueKeyIsStringLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "@blah=\"boo\"" "\n"
            "\t" "iron," "\n"
            "@blooie" "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithKeyValueAttributeOnEnumValueKeyIsStringLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsEnumWithHexLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "iron," "\n"
            "\t" "copper = 0x04," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithHexLiteral, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Enum>().bind("enum");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("enum_0");
        REQUIRE CHECK(node);

        const auto& Enum = static_cast<nodes::Enum&>(*node);
        CHECK_EQUAL("foo::Metal", Enum.name());
        CHECK_EQUAL("u8", Enum.underlying().token().to_string());

        auto fieldsMatcher = Matcher().getChildrenOf<nodes::EnumField>().bind("fields");
        CHECK(fieldsMatcher(node));

        const auto field0 = fieldsMatcher.bound("fields_0");
        REQUIRE CHECK(field0);

        const auto& f0 = static_cast<nodes::EnumField&>(*field0);
        CHECK_EQUAL("iron", f0.name().token().to_string());
        CHECK_EQUAL("u8", f0.underlying().token().to_string());
        CHECK_EQUAL(0U, boost::get<std::uint8_t>(f0.value()));

        const auto field1 = fieldsMatcher.bound("fields_1");
        REQUIRE CHECK(field1);

        const auto& f1 = static_cast<nodes::EnumField&>(*field1);
        CHECK_EQUAL("copper", f1.name().token().to_string());
        CHECK_EQUAL("u8", f1.underlying().token().to_string());
        CHECK_EQUAL(4U, boost::get<std::uint8_t>(f1.value()));

        const auto field2 = fieldsMatcher.bound("fields_2");
        REQUIRE CHECK(field2);

        const auto& f2 = static_cast<nodes::EnumField&>(*field2);
        CHECK_EQUAL("gold", f2.name().token().to_string());
        CHECK_EQUAL("u8", f2.underlying().token().to_string());
        CHECK_EQUAL(5U, boost::get<std::uint8_t>(f2.value()));
    }

    struct WhenInputIsEnumWithCharLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "iron," "\n"
            "\t" "copper = 'a'," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithCharLiteral, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Enum>().bind("enum");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("enum_0");
        REQUIRE CHECK(node);

        const auto& Enum = static_cast<nodes::Enum&>(*node);
        CHECK_EQUAL("foo::Metal", Enum.name());
        CHECK_EQUAL("u8", Enum.underlying().token().to_string());

        auto fieldsMatcher = Matcher().getChildrenOf<nodes::EnumField>().bind("fields");
        CHECK(fieldsMatcher(node));

        const auto field0 = fieldsMatcher.bound("fields_0");
        REQUIRE CHECK(field0);

        const auto& f0 = static_cast<nodes::EnumField&>(*field0);
        CHECK_EQUAL("iron", f0.name().token().to_string());
        CHECK_EQUAL("u8", f0.underlying().token().to_string());
        CHECK_EQUAL(0U, boost::get<std::uint8_t>(f0.value()));

        const auto field1 = fieldsMatcher.bound("fields_1");
        REQUIRE CHECK(field1);

        const auto& f1 = static_cast<nodes::EnumField&>(*field1);
        CHECK_EQUAL("copper", f1.name().token().to_string());
        CHECK_EQUAL("u8", f1.underlying().token().to_string());
        CHECK_EQUAL(97U, boost::get<std::uint8_t>(f1.value()));

        const auto field2 = fieldsMatcher.bound("fields_2");
        REQUIRE CHECK(field2);

        const auto& f2 = static_cast<nodes::EnumField&>(*field2);
        CHECK_EQUAL("gold", f2.name().token().to_string());
        CHECK_EQUAL("u8", f2.underlying().token().to_string());
        CHECK_EQUAL(98U, boost::get<std::uint8_t>(f2.value()));
    }

    struct WhenInputIsEnumWithNumericLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "iron," "\n"
            "\t" "copper = 200," "\n"
            "\t" "gold," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithNumericLiteral, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Enum>().bind("enum");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("enum_0");
        REQUIRE CHECK(node);

        const auto& Enum = static_cast<nodes::Enum&>(*node);
        CHECK_EQUAL("foo::Metal", Enum.name());
        CHECK_EQUAL("u8", Enum.underlying().token().to_string());

        auto fieldsMatcher = Matcher().getChildrenOf<nodes::EnumField>().bind("fields");
        CHECK(fieldsMatcher(node));

        const auto field0 = fieldsMatcher.bound("fields_0");
        REQUIRE CHECK(field0);

        const auto& f0 = static_cast<nodes::EnumField&>(*field0);
        CHECK_EQUAL("iron", f0.name().token().to_string());
        CHECK_EQUAL("u8", f0.underlying().token().to_string());
        CHECK_EQUAL(0U, boost::get<std::uint8_t>(f0.value()));

        const auto field1 = fieldsMatcher.bound("fields_1");
        REQUIRE CHECK(field1);

        const auto& f1 = static_cast<nodes::EnumField&>(*field1);
        CHECK_EQUAL("copper", f1.name().token().to_string());
        CHECK_EQUAL("u8", f1.underlying().token().to_string());
        CHECK_EQUAL(200U, boost::get<std::uint8_t>(f1.value()));

        const auto field2 = fieldsMatcher.bound("fields_2");
        REQUIRE CHECK(field2);

        const auto& f2 = static_cast<nodes::EnumField&>(*field2);
        CHECK_EQUAL("gold", f2.name().token().to_string());
        CHECK_EQUAL("u8", f2.underlying().token().to_string());
        CHECK_EQUAL(201U, boost::get<std::uint8_t>(f2.value()));
    }

    struct WhenInputIsEnumWithDifferentLiterals : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "iron = 0x04," "\n"
            "\t" "copper = 'a'," "\n"
            "\t" "gold = 200," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithDifferentLiterals, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Enum>().bind("enum");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("enum_0");
        REQUIRE CHECK(node);

        const auto& Enum = static_cast<nodes::Enum&>(*node);
        CHECK_EQUAL("foo::Metal", Enum.name());
        CHECK_EQUAL("u8", Enum.underlying().token().to_string());

        auto fieldsMatcher = Matcher().getChildrenOf<nodes::EnumField>().bind("fields");
        CHECK(fieldsMatcher(node));

        const auto field0 = fieldsMatcher.bound("fields_0");
        REQUIRE CHECK(field0);

        const auto& f0 = static_cast<nodes::EnumField&>(*field0);
        CHECK_EQUAL("iron", f0.name().token().to_string());
        CHECK_EQUAL("u8", f0.underlying().token().to_string());
        CHECK_EQUAL(4U, boost::get<std::uint8_t>(f0.value()));

        const auto field1 = fieldsMatcher.bound("fields_1");
        REQUIRE CHECK(field1);

        const auto& f1 = static_cast<nodes::EnumField&>(*field1);
        CHECK_EQUAL("copper", f1.name().token().to_string());
        CHECK_EQUAL("u8", f1.underlying().token().to_string());
        CHECK_EQUAL(97U, boost::get<std::uint8_t>(f1.value()));

        const auto field2 = fieldsMatcher.bound("fields_2");
        REQUIRE CHECK(field2);

        const auto& f2 = static_cast<nodes::EnumField&>(*field2);
        CHECK_EQUAL("gold", f2.name().token().to_string());
        CHECK_EQUAL("u8", f2.underlying().token().to_string());
        CHECK_EQUAL(200U, boost::get<std::uint8_t>(f2.value()));
    }

    struct WhenInputIsEnumWithDifferentLiteralsAndFieldsBetween : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "// this is a comment" "\n"
            "\t" "iron = 0x04," "\n"
            "\t" "iron2," "\n"
            "\t" "copper = 'a'," "\n"
            "\t" "// this is a multi-line \\" "\n"
            "\t" "   comment" "\n"
            "\t" "copper2," "\n"
            "\t" "gold = 200," "\n"
            "\t" "gold2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithDifferentLiteralsAndFieldsBetween, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Enum>().bind("enum");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("enum_0");
        REQUIRE CHECK(node);

        const auto& Enum = static_cast<nodes::Enum&>(*node);
        CHECK_EQUAL("foo::Metal", Enum.name());
        CHECK_EQUAL("u8", Enum.underlying().token().to_string());

        auto fieldsMatcher = Matcher().getChildrenOf<nodes::EnumField>().bind("fields");
        CHECK(fieldsMatcher(node));

        const auto field0 = fieldsMatcher.bound("fields_0");
        REQUIRE CHECK(field0);

        const auto& f0 = static_cast<nodes::EnumField&>(*field0);
        CHECK_EQUAL("iron", f0.name().token().to_string());
        CHECK_EQUAL("u8", f0.underlying().token().to_string());
        CHECK_EQUAL(4U, boost::get<std::uint8_t>(f0.value()));

        const auto field1 = fieldsMatcher.bound("fields_1");
        REQUIRE CHECK(field1);

        const auto& f1 = static_cast<nodes::EnumField&>(*field1);
        CHECK_EQUAL("iron2", f1.name().token().to_string());
        CHECK_EQUAL("u8", f1.underlying().token().to_string());
        CHECK_EQUAL(5U, boost::get<std::uint8_t>(f1.value()));

        const auto field2 = fieldsMatcher.bound("fields_2");
        REQUIRE CHECK(field2);

        const auto& f2 = static_cast<nodes::EnumField&>(*field2);
        CHECK_EQUAL("copper", f2.name().token().to_string());
        CHECK_EQUAL("u8", f2.underlying().token().to_string());
        CHECK_EQUAL(97U, boost::get<std::uint8_t>(f2.value()));

        const auto field3 = fieldsMatcher.bound("fields_3");
        REQUIRE CHECK(field3);

        const auto& f3 = static_cast<nodes::EnumField&>(*field3);
        CHECK_EQUAL("copper2", f3.name().token().to_string());
        CHECK_EQUAL("u8", f3.underlying().token().to_string());
        CHECK_EQUAL(98U, boost::get<std::uint8_t>(f3.value()));

        const auto field4 = fieldsMatcher.bound("fields_4");
        REQUIRE CHECK(field4);

        const auto& f4 = static_cast<nodes::EnumField&>(*field4);
        CHECK_EQUAL("gold", f4.name().token().to_string());
        CHECK_EQUAL("u8", f4.underlying().token().to_string());
        CHECK_EQUAL(200U, boost::get<std::uint8_t>(f4.value()));

        const auto field5 = fieldsMatcher.bound("fields_5");
        REQUIRE CHECK(field5);

        const auto& f5 = static_cast<nodes::EnumField&>(*field5);
        CHECK_EQUAL("gold2", f5.name().token().to_string());
        CHECK_EQUAL("u8", f5.underlying().token().to_string());
        CHECK_EQUAL(201U, boost::get<std::uint8_t>(f5.value()));
    }

    struct WhenInputIsEnumWhichReusesValues : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "gold = 300," "\n"
            "\t" "silver = 300," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWhichReusesValues, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsEnumWithOutOfRangeNumericLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "gold = 300," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithOutOfRangeNumericLiteral, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsEnumWithOutOfRangeHexLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "gold = 0xabba," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumWithOutOfRangeHexLiteral, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsBitfield : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Field1 : u8 {" "\n"
            "\t" "f1 : 0," "\n"
            "\t" "// comment" "\n"
            "\t" "f2 : 1..2," "\n"
            "\t" "// multi-line \\" "\n"
            "\t" "   comment" "\n"
            "\t" "f3 : 3..4," "\n"
            "\t" "f4 : 5," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfield, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Bitfield>().bind("bitfield");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto bitfieldNode = matcher.bound("bitfield_0");
        REQUIRE CHECK(bitfieldNode);

        const auto& bitfield = static_cast<nodes::Bitfield&>(*bitfieldNode);
        CHECK_EQUAL("foo::Field1", bitfield.name());
        CHECK_EQUAL("u8", bitfield.underlying().token().value());

        auto fieldMatcher = Matcher().getChildrenOf<nodes::BitfieldField>().bind("fields");
        REQUIRE CHECK(fieldMatcher(bitfieldNode));

        const auto field0 = fieldMatcher.bound("fields_0");
        REQUIRE CHECK(field0);

        const auto& f0 = static_cast<nodes::BitfieldField&>(*field0);
        CHECK_EQUAL("f1", f0.name().token().value());
        CHECK_EQUAL("u8", f0.underlying().token().value());
        CHECK_EQUAL(0U, f0.beginBit());
        CHECK_EQUAL(0U, f0.endBit());

        const auto field1 = fieldMatcher.bound("fields_1");
        REQUIRE CHECK(field1);

        const auto& f1 = static_cast<nodes::BitfieldField&>(*field1);
        CHECK_EQUAL("f2", f1.name().token().value());
        CHECK_EQUAL("u8", f1.underlying().token().value());
        CHECK_EQUAL(1U, f1.beginBit());
        CHECK_EQUAL(2U, f1.endBit());

        const auto field2 = fieldMatcher.bound("fields_2");
        REQUIRE CHECK(field2);

        const auto& f2 = static_cast<nodes::BitfieldField&>(*field2);
        CHECK_EQUAL("f3", f2.name().token().value());
        CHECK_EQUAL("u8", f2.underlying().token().value());
        CHECK_EQUAL(3U, f2.beginBit());
        CHECK_EQUAL(4U, f2.endBit());

        const auto field3 = fieldMatcher.bound("fields_3");
        REQUIRE CHECK(field3);

        const auto& f3 = static_cast<nodes::BitfieldField&>(*field3);
        CHECK_EQUAL("f4", f3.name().token().value());
        CHECK_EQUAL("u8", f3.underlying().token().value());
        CHECK_EQUAL(5U, f3.beginBit());
        CHECK_EQUAL(5U, f3.endBit());
    }

    struct WhenInputIsBitfieldWithAttribute : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@vogue" "\n"
            "bitfield Field1 : u8 {" "\n"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 1..2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldWithAttribute, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsBitfieldWithKeyValueAttributeNumericLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@vogue=1" "\n"
            "bitfield Field1 : u8 {" "\n"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 1..2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldWithKeyValueAttributeNumericLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsBitfieldWithKeyValueAttributeHexLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@vogue=0x02" "\n"
            "bitfield Field1 : u8 {" "\n"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 1..2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldWithKeyValueAttributeHexLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsBitfieldWithKeyValueAttributeCharLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@vogue='a'" "\n"
            "bitfield Field1 : u8 {" "\n"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 1..2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldWithKeyValueAttributeCharLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsBitfieldWithKeyValueAttributeStringLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@vogue=\"boo\"" "\n"
            "bitfield Field1 : u8 {" "\n"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 1..2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldWithKeyValueAttributeStringLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsBitfieldWithAttributeOnBitfield : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Field1 : u8 {" "\n"
            "@b1"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 1..2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldWithAttributeOnBitfield, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsBitfieldWithAttributeOnBitfieldNumericLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Field1 : u8 {" "\n"
            "@b1=2"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 1..2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldWithAttributeOnBitfieldNumericLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsBitfieldWithAttributeOnBitfieldHexLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Field1 : u8 {" "\n"
            "@b1=0x08"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 1..2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldWithAttributeOnBitfieldHexLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsBitfieldWithAttributeOnBitfieldCharLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Field1 : u8 {" "\n"
            "@b1='c'"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 1..2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldWithAttributeOnBitfieldCharLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsBitfieldWithAttributeOnBitfieldStringLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Field1 : u8 {" "\n"
            "@b1=\"boo\""
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 1..2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldWithAttributeOnBitfieldStringLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsBitfieldReassigningBits_1 : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Field1 : u8 {" "\n"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 0..2," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldReassigningBits_1, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsBitfieldReassigningBits_2 : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Field1 : u8 {" "\n"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 1..2," "\n"
            "\t" "f3 : 0," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldReassigningBits_2, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsBitfieldNotStartingAtLowBit : ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Field1 : u8 {" "\n"
            "\t" "f1 : 7," "\n"
            "\t" "f2 : 6..4," "\n"
            "\t" "f3 : 3," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldNotStartingAtLowBit, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsBitfieldAndEndBitIsTooLow : ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Field1 : u8 {" "\n"
            "\t" "f1 : 0," "\n"
            "\t" "f2 : 3..2," "\n"
            "\t" "f3 : 5," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitfieldAndEndBitIsTooLow, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsBitFieldAndFieldRangeExceedsUnderlyingType : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Field1 : u8 {" "\n"
            "\t" "f1 : 5," "\n"
            "\t" "f2 : 6..7," "\n"
            "\t" "f3 : 8," "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsBitFieldAndFieldRangeExceedsUnderlyingType, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsStruct : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Myfield {" "\n"
            "\t" "// field 1 is blah blah blah" "\n"
            "\t" "u8 field1;" "\n"
            "\t" "// field 2 will be blah \\" "\n"
            "\t" "   blah blah" "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStruct, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Struct>().bind("struct");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("struct_0");
        REQUIRE CHECK(node);

        const auto& s = static_cast<nodes::Struct&>(*node);
        CHECK_EQUAL("foo::Myfield", s.name());

        auto fieldsMatcher = Matcher().getChildrenOf<nodes::StructField>().bind("fields");
        REQUIRE CHECK(fieldsMatcher(node));

        const auto f0_node = fieldsMatcher.bound("fields_0");
        REQUIRE CHECK(f0_node);

        const auto& f0 = static_cast<nodes::StructField&>(*f0_node);
        CHECK_EQUAL("field1", f0.name().token().value());
        CHECK_EQUAL("u8", f0.type());
        CHECK(!f0.isConst());
        CHECK(!f0.isArray());
        CHECK(!f0.isVector());
    }

    struct WhenInputIsNestedStruct : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n\n"

            "// struct 1" "\n"
            "struct Struct1 {" "\n"
            "\t" "u8 field1;" "\n"
            "}" "\n\n"

            "// struct 2" "\n"
            "struct Struct2 {" "\n"
            "\t" "f32 slope;" "\n"
            "\t" "Struct1 s1;" "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsNestedStruct, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Struct>().bind("struct");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("struct_1");
        REQUIRE CHECK(node);

        auto fieldMatcher = Matcher().getChildrenOf<nodes::StructField>().bind("fields");
        CHECK(fieldMatcher(node));

        const auto field1_node = fieldMatcher.bound("fields_1");
        REQUIRE CHECK(field1_node);

        const auto& s1 = static_cast<nodes::StructField&>(*field1_node);
        CHECK_EQUAL("foo::Struct1", s1.type());
        CHECK_EQUAL("s1", s1.name().token().value());
    }

    struct WhenInputIsNestedStruct_2 : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n\n"

            "// struct 1" "\n"
            "struct Struct1 {" "\n"
            "\t" "u8 field1;" "\n"
            "}" "\n\n"

            "// struct 2" "\n"
            "struct Struct2 {" "\n"
            "\t" "f32 slope;" "\n"
            "\t" "foo::Struct1 s1;" "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsNestedStruct_2, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructWithAttribute : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@amigo" "\n"
            "struct Struct1 {" "\n"
                "u8 field;" "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithAttribute, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::Struct>().bind("struct");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("struct_0");
        REQUIRE CHECK(node);

        auto attributeMatcher = Matcher().getChildrenOf<nodes::Attribute>().bind("attribute");
        CHECK(attributeMatcher(node));

        const auto attributeNode = attributeMatcher.bound("attribute_0");
        REQUIRE CHECK(attributeNode);

        const auto& attribute = static_cast<nodes::Attribute&>(*attributeNode);
        CHECK_EQUAL("@amigo", attribute.info().token().value());
    }

    struct WhenInputIsStructWithArray : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8[10] name;"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithArray, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructWithVector : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 size;" "\n"
                "u8[size] name;" "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithVector, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate AST
    }

    struct WhenInputIsStructWithVectorAndNestedSizeMember : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 size;" "\n"
            "}"
            "struct Struct2 {"
                "Struct1 s1;"
                "u16[s1.size] name;"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithVectorAndNestedSizeMember, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructWithDoubleNestedVectorSizeMember : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 size;" "\n"
            "}"
            "struct Struct2 {"
                "Struct1 s1;"
            "}"
            "struct Struct3 {"
                "Struct2 s2;"
                "f32[s2.s1.size] buffer;"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithDoubleNestedVectorSizeMember, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructWithFieldAttribute : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {" "\n"
                "@align=\"left\" @padding=' '" "\n"
                "u8[20] name;" "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithFieldAttribute, verifyConsume)
    {
        tokenize(sv);
        parse();

        auto structMatcher = Matcher().getChildrenOf<nodes::Struct>().bind("struct");
        auto structFieldMatcher = Matcher().getChildrenOf<nodes::StructField>().bind("field");
        auto attributeMatcher = Matcher().getChildrenOf<nodes::Attribute>().bind("attribute");

        REQUIRE CHECK(structMatcher(parser.ast().root()));
        const auto structNode = structMatcher.bound("struct_0");
        REQUIRE CHECK(structNode);

        REQUIRE CHECK(structFieldMatcher(structNode));
        const auto structFieldNode = structFieldMatcher.bound("field_0");
        REQUIRE CHECK(structFieldNode);

        REQUIRE CHECK(attributeMatcher(structFieldNode));

        auto attribute0Node = attributeMatcher.bound("attribute_0");
        REQUIRE CHECK(attribute0Node);

        const auto& attribute0 = static_cast<nodes::Attribute&>(*attribute0Node);
        CHECK_EQUAL("@padding", attribute0.info().token().value());

        REQUIRE CHECK_EQUAL(1U, attribute0Node->children().size());
        const auto& value0 = dynamic_cast<nodes::CharLiteral&>(*attribute0Node->children()[0]);
        CHECK_EQUAL("' '", value0.info().token().value());

        auto attribute1Node = attributeMatcher.bound("attribute_1");
        REQUIRE CHECK(attribute1Node);

        const auto& attribute1 = static_cast<nodes::Attribute&>(*attribute1Node);
        CHECK_EQUAL("@align", attribute1.info().token().value());

        REQUIRE CHECK_EQUAL(1U, attribute1Node->children().size());
        const auto& value1 = dynamic_cast<nodes::StringLiteral&>(*attribute1Node->children()[0]);
        CHECK_EQUAL("\"left\"", value1.info().token().value());
    }

    struct WhenInputIsABitfieldUsedInAStruct : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "bitfield Bitfield1 : u8 {"
                "field1 : 0,"
                "field2 : 1..2,"
                "field3 : 3,"
                "field4 : 4,"
                "field5 : 5,"
                "field6 : 6,"
                "field7 : 7,"
            "}"
            "struct Struct1 {"
                "Bitfield1 b1;"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsABitfieldUsedInAStruct, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsEnumUsedInAStruct : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "enum Metal : u8 {" "\n"
            "\t" "iron," "\n"
            "\t" "copper," "\n"
            "\t" "gold," "\n"
            "}"
            "struct Struct1 {"
                "Metal metal;"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsEnumUsedInAStruct, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructWithConstMemberAssignedNumericLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "const u8 value = 10;"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithConstMemberAssignedNumericLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate AST
    }

    struct WhenInputIsStructWithConstMemberAssignedNumericLiteralWhichOverflowsUnderlying : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "const u8 value = 300;"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithConstMemberAssignedNumericLiteralWhichOverflowsUnderlying, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), std::runtime_error);
    }

    struct WhenInputIsStructWithConstMemberAssignedHexLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "const u8 value = 0x0a;"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithConstMemberAssignedHexLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructWithConstMemberAssignedHexLiteralWhichOverflowsUnderyling : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "const u8 value = 0x0afa;"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithConstMemberAssignedHexLiteralWhichOverflowsUnderyling, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), std::runtime_error);
    }

    struct WhenInputIsStructWithConstMemberAssignedCharLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "const u8 value = 'c';"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithConstMemberAssignedCharLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructWithConstMemberAssignedStringLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "const u8[5] value = \"hello\";"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithConstMemberAssignedStringLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate AST
    }

    struct WhenInputIsStructWithConstMemberAssignedStringLiteralWhichWouldBeTruncated : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "const u8[4] value = \"hello\";"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithConstMemberAssignedStringLiteralWhichWouldBeTruncated, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsStructWithAMemberAssignedNumericLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 value = 10;"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithAMemberAssignedNumericLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructWithAMemberAssignedHexLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 value = 0x0f;"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithAMemberAssignedHexLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructWithAMemberAssignedCharLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 value = 'c';"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithAMemberAssignedCharLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructWithAMemberAssignedStringLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8[5] value = \"hello\";"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithAMemberAssignedStringLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate AST
    }

    struct WhenInputIsStructWithVariableBlock : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 field1;"
            "}"
            "struct Struct2 {"
                "u16 field1;"
            "}"
            "struct Struct3 {"
                "u32 field1;"
            "}"
            "struct Struct4 {"
                "u8 blockType;"
                "variable_block : blockType {"
                    "case 'a': Struct1,"
                    "case 'b': Struct2,"
                    "case 'c': Struct3,"
                "}"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithVariableBlock, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate the AST
    }

    struct WhenInputIsStructWithVariableBlockAndNestedField : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;"
            "struct Struct1 {"
                "u8 blockType;"
            "}"
            "struct Struct2 {"
                "Struct1 s1;"
            "}"
            "struct Struct3 {"
                "u8 foo;"
                "u8 bar;"
            "}"
            "struct Struct4 {"
                "u16 foo;"
                "u16 bar;"
            "}"
            "struct Struct5 {"
                "Struct2 s2;"
                "variable_block : s2.s1.blockType {"
                    "case 'a': Struct3,"
                    "case 'b': Struct4,"
                "}"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithVariableBlockAndNestedField, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate the AST
    }

    struct WhenInputIsStructCaseStatementsAreNumericLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 field1;"
            "}"
            "struct Struct2 {"
                "u16 field1;"
            "}"
            "struct Struct3 {"
                "u32 field1;"
            "}"
            "struct Struct4 {"
                "u8 blockType;"
                "variable_block : blockType {"
                    "case 1: Struct1,"
                    "case 2: Struct2,"
                    "case 3: Struct3,"
                "}"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructCaseStatementsAreNumericLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate the AST
    }

    struct WhenInputIsStructCaseStatementsAreHexLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 field1;"
            "}"
            "struct Struct2 {"
                "u16 field1;"
            "}"
            "struct Struct3 {"
                "u32 field1;"
            "}"
            "struct Struct4 {"
                "u8 blockType;"
                "variable_block : blockType {"
                    "case 0x01: Struct1,"
                    "case 0x02: Struct2,"
                    "case 0x03: Struct3,"
                "}"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructCaseStatementsAreHexLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate the AST
    }

    struct WhenInputIsStructCaseStatementsAreHexLiteralThatOverflowTheSwitchField : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 field1;"
            "}"
            "struct Struct2 {"
                "u16 field1;"
            "}"
            "struct Struct3 {"
                "u32 field1;"
            "}"
            "struct Struct4 {"
                "u8 blockType;"
                "variable_block : blockType {"
                    "case 0x01: Struct1,"
                    "case 0x02: Struct2,"
                    "case 0xffaa: Struct3,"
                "}"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructCaseStatementsAreHexLiteralThatOverflowTheSwitchField, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), std::runtime_error);
    }

    struct WhenInputIsStructCaseStatementsAreStringLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 field1;"
            "}"
            "struct Struct2 {"
                "u16 field1;"
            "}"
            "struct Struct3 {"
                "u32 field1;"
            "}"
            "struct Struct4 {"
                "u8[4] blockType;"
                "variable_block : blockType {"
                    "case \"ab\": Struct1,"
                    "case \"a\": Struct2,"
                    "case \"b\": Struct3,"
                "}"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructCaseStatementsAreStringLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructCaseStatementsAreStringLiteralAndSwitchingFieldIsVector : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 field1;"
            "}"
            "struct Struct2 {"
                "u16 field1;"
            "}"
            "struct Struct3 {"
                "u32 field1;"
            "}"
            "struct Struct4 {"
                "u8 length;"
                "u8[length] blockType;"
                "variable_block : blockType {"
                    "case \"ab\": Struct1,"
                    "case \"a\": Struct2,"
                    "case \"b\": Struct3,"
                "}"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructCaseStatementsAreStringLiteralAndSwitchingFieldIsVector, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsStructCaseStatementsAreStringLiteralAndCaseOverflowsSwitchField : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 field1;"
            "}"
            "struct Struct2 {"
                "u16 field1;"
            "}"
            "struct Struct3 {"
                "u32 field1;"
            "}"
            "struct Struct4 {"
                "u8[2] blockType;"
                "variable_block : blockType {"
                    "case \"abc\": Struct1,"
                    "case \"a\": Struct2,"
                    "case \"b\": Struct3,"
                "}"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructCaseStatementsAreStringLiteralAndCaseOverflowsSwitchField, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsStructCaseStatementsAreStringLiteralAndSwitchFieldIsNotArrayOrVector : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct Struct1 {"
                "u8 field1;"
            "}"
            "struct Struct2 {"
                "u16 field1;"
            "}"
            "struct Struct3 {"
                "u32 field1;"
            "}"
            "struct Struct4 {"
                "u8 blockType;"
                "variable_block : blockType {"
                    "case \"ab\": Struct1,"
                    "case \"a\": Struct2,"
                    "case \"b\": Struct3,"
                "}"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructCaseStatementsAreStringLiteralAndSwitchFieldIsNotArrayOrVector, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsStructWithVerifyBlockAndCaseValueOverflowsSwitchField : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;"
            "struct Struct1 {"
                "u8 field1;"
                "f32 field2;"
            "}"
            "struct Struct2 {"
                "f64 field1;"
                "f64 field2;"
            "}"
            "struct Struct3 {"
                "u8 blockType;"
                "variable_block : blockType {"
                    "case 200: Struct1,"
                    "case 300: Struct2,"
                "}"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithVerifyBlockAndCaseValueOverflowsSwitchField, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), std::runtime_error);
    }

    //-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_
    //
    // TODO: more struct coverage
    //
    //-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_

    struct WhenInputIsStructWithNoFields : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct S1 {"
                " // comment " "\n"
            "}"
        );
    };

    TEST_FIXTURE(WhenInputIsStructWithNoFields, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }

    struct WhenInputIsAUsingStatementOfBuiltinType : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "using Indicator = u8;" "\n"
        );
    };

    TEST_FIXTURE(WhenInputIsAUsingStatementOfBuiltinType, verifyConsume)
    {
        auto matcher = Matcher().getChildrenOf<nodes::TypeAlias>().bind("using");
        CHECK(!matcher(parser.ast().root()));

        tokenize(sv);
        parse();

        CHECK(matcher(parser.ast().root()));

        const auto node = matcher.bound("using_0");
        REQUIRE CHECK(node);

        const auto& typeAlias = static_cast<nodes::TypeAlias&>(*node);
        CHECK_EQUAL("Indicator", typeAlias.aliasedType().token().value());
        CHECK_EQUAL("u8", typeAlias.existingType().token().value());
    }

    struct WhenInputIsAUsingStatementOfDefinedStruct : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "struct S1{u8 field1;}" "\n"

            "using Struct1 = S1;"
        );
    };

    TEST_FIXTURE(WhenInputIsAUsingStatementOfDefinedStruct, verifyConsume)
    {
        tokenize(sv);
        parse();
    }

    struct WhenInputIsAUsingStatementWithAttribute : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@attribute" "\n"
            "using Indicator = u8;"
        );
    };

    TEST_FIXTURE(WhenInputIsAUsingStatementWithAttribute, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate AST
    }

    struct WhenInputIsAUsingStatementWithKeyValueAttribute : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@attribute=\"value\"" "\n"
            "using Indicator = u8;"
        );
    };

    TEST_FIXTURE(WhenInputIsAUsingStatementWithKeyValueAttribute, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate AST
    }

    struct WhenInputIsAUsingStatementWithKeyValueAttributeAndCharLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@attribute='a'" "\n"
            "using Indicator = u8;"
        );
    };

    TEST_FIXTURE(WhenInputIsAUsingStatementWithKeyValueAttributeAndCharLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate AST
    }

    struct WhenInputIsAUsingStatementWithKeyValueAttributeAndHexLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@attribute=0x02" "\n"
            "using Indicator = u8;"
        );
    };

    TEST_FIXTURE(WhenInputIsAUsingStatementWithKeyValueAttributeAndHexLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate AST
    }

    struct WhenInputIsAusingStatementWithKeyValueAttributeAndNumericLiteral : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "@attribute=42" "\n"
            "using Indicator = f64;"
        );
    };

    TEST_FIXTURE(WhenInputIsAusingStatementWithKeyValueAttributeAndNumericLiteral, verifyConsume)
    {
        tokenize(sv);
        parse();

        // TODO: validate AST
    }

    struct WhenInputIsAUsingStatementButTypeIsNotDefined : public ParserFixture
    {
        const boost::string_view sv = boost::string_view(
            "namespace foo;" "\n"
            "using Indicator = bar::UndefinedType;" "\n"
        );
    };

    TEST_FIXTURE(WhenInputIsAUsingStatementButTypeIsNotDefined, verifyConsume)
    {
        tokenize(sv);
        CHECK_THROW(parse(), swizzle::SyntaxError);
    }
}

ext: cpp

max_stars_repo_name: SenorAgosto/Swizzle

max_issues_repo_name: SenorAgosto/Swizzle

max_forks_repo_name: SenorAgosto/Swizzle


text:
#include "drakeGradientUtil.h"
#include "drakeGeometryUtil.h"
#include "testUtil.h"
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <unsupported/Eigen/KroneckerProduct> // unsupported...
#include <iostream>
#include <stdexcept>
#include <vector>
#include <array>
#include <random>

using namespace Eigen;

template<typename Derived>
void setLinearIndices(MatrixBase<Derived>& A)
{
  for (int col = 0; col < A.cols(); col++) {
    for (int row = 0; row < A.rows(); row++) {
      A(row, col) = row + col * A.rows() + 1;
    }
  }
}

void testTransposeGrad(int ntests)
{
  const int rows_X = 8;
  const int cols_X = 6;
  const int numel_X = rows_X * cols_X;
  const int nq = 34;
//  Matrix<double, numel_X, nq> dX;
//  MatrixXd dX(numel_X, nq);

//  Matrix<double, numel_X, nq> dX_transpose;

  for (int testnr = 0; testnr < ntests; testnr++) {
    Matrix<double, numel_X, Dynamic> dX = Matrix<double, numel_X, nq>::Random();
//    std::cout << "dX:\n" << dX << std::endl << std::endl;
    auto dX_transpose = transposeGrad(dX, rows_X);
    volatile auto vol = dX_transpose; // volatile to make sure that the result doesn't get discarded in compiler optimization

//    transposeGrad(dX, rows_X, dX_transpose);
//    std::cout << "dX_transpose:\n" << dX_transpose << std::endl;
  }
}

void testMatGradMultMat(int ntests, bool check)
{
  const int nq = 34;
  for (int testnr = 0; testnr < ntests; testnr++) {

//    MatrixXd A = MatrixXd::Random(8, 6).eval();
//    MatrixXd B = MatrixXd::Random(A.cols(), 9).eval();
    auto A = Matrix<double, 8, 6>::Random().eval();
    auto B = Matrix<double, A.ColsAtCompileTime, 9>::Random().eval();

    MatrixXd dA = MatrixXd::Random(A.size(), nq).eval();
    MatrixXd dB = MatrixXd::Random(B.size(), nq).eval();
    //    auto dA = Matrix<double, A.SizeAtCompileTime, nq>::Random().eval();
    //    auto dB = Matrix<double, B.SizeAtCompileTime, nq>::Random().eval();
    //
    //    MatrixXd A = MatrixXd::Random(8, 6).eval();
    //    MatrixXd B = MatrixXd::Random(A.cols(), 9).eval();
    //    auto A = Matrix<double, 5, 3>::Random().eval();
    //    auto B = Matrix<double, A.ColsAtCompileTime, 2>::Random().eval();
    //
    //    MatrixXd dA = MatrixXd::Random(A.size(), nq).eval();
    //    MatrixXd dB = MatrixXd::Random(B.size(), nq).eval();
    //    auto dA = Matrix<double, A.SizeAtCompileTime, nq>::Random().eval();
    //    auto dB = Matrix<double, B.SizeAtCompileTime, nq>::Random().eval();

    auto dAB = matGradMultMat(A, B, dA, dB).eval();
    volatile auto vol = dAB; // volatile to make sure that the result doesn't get discarded in compiler optimization

    if (check) {
      auto dAB_check = Eigen::kroneckerProduct(Eigen::MatrixXd::Identity(B.cols(), B.cols()), A) * dB
          + Eigen::kroneckerProduct(B.transpose(), Eigen::MatrixXd::Identity(A.rows(), A.rows())) * dA;

      if (!dAB.isApprox(dAB_check, 1e-10)) {
        throw std::runtime_error("wrong.");
      }
    }
  }
}

void testMatGradMult(int ntests, bool check) {
  const int nq = 34;
  const int A_rows = 8;
  const int A_cols = 6;
  for (int testnr = 0; testnr < ntests; testnr++) {
//    MatrixXd dA = MatrixXd::Random(A_rows * A_cols, nq).eval();
    auto dA = MatrixXd::Random(A_rows * A_cols, nq).eval();
    auto b = Matrix<double, A_cols, 1>::Random().eval();
    auto dAb = matGradMult(dA, b).eval();

    if (check) {
      auto A = Matrix<double, A_rows, A_cols>::Random().eval();
      auto db = MatrixXd::Zero(b.rows(), nq).eval();
      auto dAb_check = matGradMultMat(A, b, dA, db);
//      std::cout << dAb << std::endl << std::endl;
//      std::cout << dAb_check << std::endl << std::endl;

      if (!dAb.isApprox(dAb_check, 1e-10)) {
        throw std::runtime_error("wrong.");
      }
    }
  }
}

void testGetSubMatrixGradient(int ntests) {
  const int A_rows = 4;
  const int A_cols = 4;
  const int nq = 34;

  std::array<int, 3> rows{ {0, 1, 2} };
  std::array<int, 3> cols{ {0, 1, 2} };

//  std::array<int, 3> rows{ {0, 1, 2} };
//  std::array<int, 3> cols{ {0, 1, 2} };

  for (int testnr = 0; testnr < ntests; testnr++) {
    auto dA = Matrix<double, A_rows * A_cols, Dynamic>::Random(A_rows * A_cols, nq).eval();
//    Matrix<double, A_rows * A_cols, Dynamic> dA = Matrix<double, A_rows * A_cols, Dynamic>::Random(A_rows * A_cols, nq);

    auto dA_submatrix = getSubMatrixGradient<Eigen::Dynamic>(dA, rows, cols, A_rows, 1, 2);
    volatile auto vol = dA_submatrix.eval();
//    std::cout << "dA:\n" << dA << "\n\n";
//    std::cout << "dA_submatrix:\n" << dA_submatrix << "\n\n";
  }
}

void testSetSubMatrixGradient(int ntests, bool check) {
  const int A_rows = 4;
  const int A_cols = 4;
  const int nq = 34;

  std::array<int, 3> rows{ {0, 1, 2} };
  std::array<int, 3> cols{ {0, 1, 2} };

  int q_start = 2;
  const int q_subvector_size = 3;
  MatrixXd dA_submatrix = MatrixXd::Random(rows.size() * cols.size(), q_subvector_size);

  for (int testnr = 0; testnr < ntests; testnr++) {
    auto dA = Matrix<double, A_rows * A_cols, Eigen::Dynamic>::Random(A_rows * A_cols, nq).eval();
    setSubMatrixGradient<Eigen::Dynamic>(dA, dA_submatrix, rows, cols, A_rows, q_start, q_subvector_size);

    if (check) {
      auto dA_submatrix_back = getSubMatrixGradient<Eigen::Dynamic>(dA, rows, cols, A_rows, q_start, q_subvector_size);
      if (!dA_submatrix_back.isApprox(dA_submatrix, 1e-10)) {
//        std::cout << "dA_submatrix" << dA_submatrix << std::endl << std::endl;
//        std::cout << "dA_submatrix_back" << dA_submatrix_back << std::endl << std::endl;
        throw std::runtime_error("wrong.");
      }
    }
  }
}

int main(int argc, char **argv) {
  testMatGradMultMat(1000, true);
  testMatGradMult(1000, true);
  testSetSubMatrixGradient(1000, true);

  int ntests = 100000;
  std::cout << "testTransposeGrad elapsed time: " << measure<>::execution(testTransposeGrad, ntests) << std::endl;

  std::cout << "testMatGradMultMat elapsed time: " << measure<>::execution(testMatGradMultMat, ntests, false) << std::endl;

  std::cout << "testMatGradMult elapsed time: " << measure<>::execution(testMatGradMult, ntests, false) << std::endl;

  std::cout << "testGetSubMatrixGradient elapsed time: " << measure<>::execution(testGetSubMatrixGradient, ntests) << std::endl;

  std::cout << "testSetSubMatrixGradient elapsed time: " << measure<>::execution(testSetSubMatrixGradient, ntests, false) << std::endl;

  return 0;
}

max_stars_repo_name: andybarry/drake

max_issues_repo_name: andybarry/drake

max_forks_repo_name: andybarry/drake


text:
//==============================================================================
//         Copyright 2003 - 2011   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#define NT2_UNIT_MODULE "nt2::meta::settings_of"

#include <boost/type_traits/is_same.hpp>
#include <nt2/sdk/meta/settings_of.hpp>

#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/basic.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>

////////////////////////////////////////////////////////////////////////////////
// Check settings_of random non-container types
////////////////////////////////////////////////////////////////////////////////
NT2_TEST_CASE( value_settings_type )
{
  using nt2::_0D;
  using nt2::settings;
  using boost::is_same;
  using nt2::matlab_index_;
  using nt2::matlab_order_;
  using nt2::unaligned_;
  using nt2::meta::settings_of;

  NT2_TEST_TYPE_IS( settings_of<float>::type
                  , settings(matlab_index_, _0D, matlab_order_, unaligned_)
                  );

  NT2_TEST_TYPE_IS( settings_of<float&>::type
                  , settings(matlab_index_, _0D, matlab_order_, unaligned_)
                  );

  NT2_TEST_TYPE_IS( settings_of<float const>::type
                  , settings(matlab_index_, _0D, matlab_order_, unaligned_)
                  );

  NT2_TEST_TYPE_IS( settings_of<float const&>::type
                  , settings(matlab_index_, _0D, matlab_order_, unaligned_)
                  );
}

////////////////////////////////////////////////////////////////////////////////
// Check settings_of container type
////////////////////////////////////////////////////////////////////////////////
struct some_container
{
  typedef nt2::settings settings_type( nt2::C_index_, nt2::of_size_<1,3,3,7> );
};

NT2_TEST_CASE( container_settings_type )
{
  using nt2::settings;
  using nt2::C_index_;
  using nt2::of_size_;
  using boost::is_same;
  using nt2::meta::settings_of;

  NT2_TEST_TYPE_IS( settings_of<some_container>::type
                  , settings(C_index_, of_size_<1,3,3,7>)
                  );

  NT2_TEST_TYPE_IS( settings_of<some_container&>::type
                  , settings(C_index_, of_size_<1,3,3,7>)
                  );

  NT2_TEST_TYPE_IS( settings_of<some_container const>::type
                  , settings(C_index_, of_size_<1,3,3,7>)
                  );

  NT2_TEST_TYPE_IS( settings_of<some_container const&>::type
                  , settings(C_index_, of_size_<1,3,3,7>)
                  );
}


text:
//==================================================================================================
/*!
  @file

  @copyright 2015 NumScale SAS
  @copyright 2015 J.T. Lapreste

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_FUNCTION_GENERIC_REALLOG_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_FUNCTION_GENERIC_REALLOG_HPP_INCLUDED

#include <boost/dispatch/function/overload.hpp>
#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/simd/operator/function/details/assert_utils.hpp>
#include <boost/simd/function/simd/is_nltz.hpp>
#include <boost/simd/function/simd/log.hpp>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  namespace bs = boost::simd;
 BOOST_DISPATCH_OVERLOAD ( reallog_
                          , (typename A0)
                             , bd::cpu_
                            , bd::generic_< bd::floating_<A0> >
                            )
 {
   BOOST_FORCEINLINE A0 operator() ( A0 const& a0) const BOOST_NOEXCEPT
   {
     BOOST_ASSERT_MSG(boost::simd::assert_all(is_nltz(a0)),
                      "reallog cannot produce complex result.");
     return log(a0);
   }
 };
} } }

#endif

ext: hpp