#include <boost/asio.hpp>
#include "vdf.h"

using boost::asio::ip::tcp;

const int max_length = 2048;
std::mutex socket_mutex;

int process_number;
// Segments are 2^16, 2^18, ..., 2^30
// Best case it'll be able to proof for up to 2^36 due to 64-wesolowski restriction.
int segments = 8;
int thread_count = 3;

void PrintInfo(std::string input) {
    std::cout << "VDF Client: " << input << "\n";
    std::cout << std::flush;
}

char disc[350];
char disc_size[5];
int disc_int_size;

void WriteProof(uint64_t iteration, Proof& result, tcp::socket& sock) {
    // Writes the number of iterations
    std::vector<unsigned char> bytes = ConvertIntegerToBytes(integer(iteration), 8);

    // Writes the y, with prepended size
    std::vector<unsigned char> y_size = ConvertIntegerToBytes(integer(result.y.size()), 8);
    bytes.insert(bytes.end(), y_size.begin(), y_size.end());
    bytes.insert(bytes.end(), result.y.begin(), result.y.end());

    // Writes the witness type.
    std::vector<unsigned char> witness_type = ConvertIntegerToBytes(integer(result.witness_type), 1);
    bytes.insert(bytes.end(), witness_type.begin(), witness_type.end());

    bytes.insert(bytes.end(), result.proof.begin(), result.proof.end());
    std::string str_result = BytesToStr(bytes);

    const uint32_t length = str_result.size();
    std::vector<unsigned char> prefix_bytes = ConvertIntegerToBytes(integer(length), 4);
    std::string prefix = BytesToStr(prefix_bytes);

    PrintInfo("Sending proof");
    {
        std::lock_guard<std::mutex> lock(socket_mutex);
        boost::asio::write(sock, boost::asio::buffer(prefix_bytes, 4));
        boost::asio::write(sock, boost::asio::buffer(str_result.c_str(), str_result.size()));
    }
    PrintInfo("Sended proof");
}

void CreateAndWriteProof(ProverManager& pm, uint64_t iteration, bool& stop_signal, tcp::socket& sock) {
    Proof result = pm.Prove(iteration);
    if (stop_signal == true) {
        PrintInfo("Got stop signal before completing the proof!");
        return ;
    }
    WriteProof(iteration, result, sock);
}

void CreateAndWriteProofTwoWeso(integer& D, form f, uint64_t iters, TwoWesolowskiCallback* weso, bool& stop_signal, tcp::socket& sock) {
    Proof result = ProveTwoWeso(D, f, iters, 0, weso, 0, stop_signal);
    if (stop_signal) {
        PrintInfo("Got stop signal before completing the proof!");
        return ;
    }
    WriteProof(iters, result, sock);
}

void InitSession(tcp::socket& sock) {
    boost::system::error_code error;

    memset(disc,0x00,sizeof(disc)); // For null termination
    memset(disc_size,0x00,sizeof(disc_size)); // For null termination

    boost::asio::read(sock, boost::asio::buffer(disc_size, 3), error);
    disc_int_size = atoi(disc_size);
    boost::asio::read(sock, boost::asio::buffer(disc, disc_int_size), error);

    if (error == boost::asio::error::eof)
        return ; // Connection closed cleanly by peer.
    else if (error)
        throw boost::system::system_error(error); // Some other error.

    if (getenv( "warn_on_corruption_in_production" )!=nullptr) {
        warn_on_corruption_in_production=true;
    }
    if (is_vdf_test) {
        PrintInfo( "=== Test mode ===" );
    }
    if (warn_on_corruption_in_production) {
        PrintInfo( "=== Warn on corruption enabled ===" );
    }
    assert(is_vdf_test); //assertions should be disabled in VDF_MODE==0
    init_gmp();
    allow_integer_constructor=true; //make sure the old gmp allocator isn't used
    set_rounding_mode();
}

void FinishSession(tcp::socket& sock) {
    try {
        // Tell client I've stopped everything, wait for ACK and close.
        boost::system::error_code error;

        PrintInfo("Stopped everything! Ready for the next challenge.");

        std::lock_guard<std::mutex> lock(socket_mutex);
        boost::asio::write(sock, boost::asio::buffer("STOP", 4));

        char ack[5];
        memset(ack,0x00,sizeof(ack));
        boost::asio::read(sock, boost::asio::buffer(ack, 3), error);
        assert (strncmp(ack, "ACK", 3) == 0);
    } catch (std::exception& e) {
        PrintInfo("Exception in thread: " + to_string(e.what()));
    }
}

uint64_t ReadIteration(tcp::socket& sock) {
    boost::system::error_code error;
    char data[20];
    memset(data, 0, sizeof(data));
    boost::asio::read(sock, boost::asio::buffer(data, 2), error);
    int size = (data[0] - '0') * 10 + (data[1] - '0');
    memset(data, 0, sizeof(data));
    boost::asio::read(sock, boost::asio::buffer(data, size), error);
    uint64_t iters = 0;
    for (int i = 0; i < size; i++)
        iters = iters * 10 + data[i] - '0';
    return iters;
}

void SessionFastAlgorithm(tcp::socket& sock) {
    InitSession(sock);
    try {
        integer D(disc);
        integer L=root(-D, 4);
        form f=form::generator(D);
        PrintInfo("Discriminant = " + to_string(D.impl));

        std::vector<std::thread> threads;
        const bool multi_proc_machine = (std::thread::hardware_concurrency() >= 16) ? true : false;
        WesolowskiCallback* weso = new FastAlgorithmCallback(segments, D, multi_proc_machine);
        FastStorage* fast_storage = NULL;
        if (multi_proc_machine) {
            fast_storage = new FastStorage((FastAlgorithmCallback*)weso);
        }
        bool stopped = false;
        std::thread vdf_worker(repeated_square, f, std::ref(D), std::ref(L), weso, fast_storage, std::ref(stopped));
        ProverManager pm(D, (FastAlgorithmCallback*)weso, fast_storage, segments, thread_count);
        pm.start();

        // Tell client that I'm ready to get the challenges.
        boost::asio::write(sock, boost::asio::buffer("OK", 2));

        while (!stopped) {
            uint64_t iters = ReadIteration(sock);
            if (iters == 0) {
                PrintInfo("Got stop signal!");
                stopped = true;
                pm.stop();
                vdf_worker.join();
                for (int t = 0; t < threads.size(); t++) {
                    threads[t].join();
                }
                if (fast_storage != NULL) {
                    delete(fast_storage);
                }
                delete(weso);
            } else {
                PrintInfo("Received iteration: " + to_string(iters));
                threads.push_back(std::thread(CreateAndWriteProof, std::ref(pm), iters, std::ref(stopped), std::ref(sock)));
            }
        }
    } catch (std::exception& e) {
        PrintInfo("Exception in thread: " + to_string(e.what()));
    }
    FinishSession(sock);
}

void SessionOneWeso(tcp::socket& sock) {
    InitSession(sock);
    try {
        integer D(disc);
        integer L=root(-D, 4);
        form f=form::generator(D);
        PrintInfo("Discriminant = " + to_string(D.impl));

        // Tell client that I'm ready to get the challenges.
        boost::asio::write(sock, boost::asio::buffer("OK", 2));

        uint64_t iter = ReadIteration(sock);
        bool stopped = false;
        WesolowskiCallback* weso = new OneWesolowskiCallback(D, iter);
        FastStorage* fast_storage = NULL;
        std::thread vdf_worker(repeated_square, f, std::ref(D), std::ref(L), weso, fast_storage, std::ref(stopped));

        Proof proof = ProveOneWesolowski(iter, D, (OneWesolowskiCallback*)weso, stopped);
        WriteProof(iter, proof, sock);

        iter = ReadIteration(sock);
        while (iter != 0) {
            std::cout << "Warning: did not receive stop signal\n";
            iter = ReadIteration(sock);
        }
        stopped = true;
        vdf_worker.join();
        delete(weso);
    } catch (std::exception& e) {
        PrintInfo("Exception in thread: " + to_string(e.what()));
    }
    FinishSession(sock);
}

void SessionTwoWeso(tcp::socket& sock) {
    const int kMaxProcessesAllowed = 3;
    InitSession(sock);
    try {
        integer D(disc);
        integer L=root(-D, 4);
        form f = form::generator(D);
        PrintInfo("Discriminant = " + to_string(D.impl));

        // Tell client that I'm ready to get the challenges.
        boost::asio::write(sock, boost::asio::buffer("OK", 2));

        bool stopped = false;
        bool stop_vector[100];
        std::vector<std::thread> threads;
        // (iteration, thread_id)
        std::set<std::pair<uint64_t, uint64_t> > seen_iterations;
        WesolowskiCallback* weso = new TwoWesolowskiCallback(D);
        FastStorage* fast_storage = NULL;
        std::thread vdf_worker(repeated_square, f, std::ref(D), std::ref(L), weso, fast_storage, std::ref(stopped));

        while (!stopped) {
            uint64_t iters = ReadIteration(sock);
            if (iters == 0) {
                PrintInfo("Got stop signal!");
                stopped = true;
                for (int i = 0; i < threads.size(); i++)
                    stop_vector[i] = true;
                for (int t = 0; t < threads.size(); t++) {
                    threads[t].join();
                }
                vdf_worker.join();
                delete(weso);
            } else {
                uint64_t max_iter = 0;
                uint64_t max_iter_thread_id = -1;
                uint64_t min_iter = 1ULL << 62;
                bool unique = true;
                for (auto active_iter: seen_iterations) {
                    if (active_iter.first > max_iter) {
                        max_iter = active_iter.first;
                        max_iter_thread_id = active_iter.second;
                    }
                    if (active_iter.first < min_iter) {
                        min_iter = active_iter.first;
                    }
                    if (active_iter.first == iters) {
                        unique = false;
                        break;
                    }
                }
                if (!unique) {
                    PrintInfo("Duplicate iteration " + to_string(iters) + "... Ignoring.");
                    continue;
                }
                if (iters >= kMaxProcessesAllowed - 500000) {
                    PrintInfo("Too big iter... ignoring");
                    continue;
                }
                if (threads.size() < kMaxProcessesAllowed || iters < min_iter) {
                    seen_iterations.insert({iters, threads.size()});
                    PrintInfo("Running proving for iter: " + to_string(iters));
                    stop_vector[threads.size()] = false;
                    threads.push_back(std::thread(CreateAndWriteProofTwoWeso, std::ref(D), f, iters,
                                      (TwoWesolowskiCallback*)weso, std::ref(stop_vector[threads.size()]),
                                      std::ref(sock)));
                    if (threads.size() > kMaxProcessesAllowed) {
                        PrintInfo("Stopping proving for iter: " + to_string(max_iter));
                        stop_vector[max_iter_thread_id] = true;
                        seen_iterations.erase({max_iter, max_iter_thread_id});
                    }
                }
            }
        }
    } catch (std::exception& e) {
        PrintInfo("Exception in thread: " + to_string(e.what()));
    }
    FinishSession(sock);
}

int gcd_base_bits=50;
int gcd_128_max_iter=3;

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 4)
    {
      std::cerr << "Usage: ./vdf_client <host> <port>\n";
      return 1;
    }

    if(hasAVX2())
    {
      gcd_base_bits=63;
      gcd_128_max_iter=2;
    }

    boost::asio::io_service io_service;

    tcp::resolver resolver(io_service);
    tcp::resolver::query query(tcp::v6(), argv[1], argv[2], boost::asio::ip::resolver_query_base::v4_mapped);
    tcp::resolver::iterator iterator = resolver.resolve(query);

    tcp::socket s(io_service);
    boost::asio::connect(s, iterator);
    fast_algorithm = false;
    two_weso = false;
    boost::system::error_code error;
    char prover_type_buf[5];
    boost::asio::read(s, boost::asio::buffer(prover_type_buf, 1), error);
    // Check for "S" (simple weso), "N" (n-weso), or "T" (2-weso)
    if (prover_type_buf[0] == 'S') {
        SessionOneWeso(s);
    }
    if (prover_type_buf[0] == 'N') {
        fast_algorithm = true;
        SessionFastAlgorithm(s);
    }
    if (prover_type_buf[0] == 'T') {
        two_weso = true;
        SessionTwoWeso(s);
    }
  } catch (std::exception& e) {
    std::cerr << "Exception: " << e.what() << "\n";
  }
  return 0;
}


#include <cstdint>

#include <algorithm>
#include <exception>
#include <set>
#include <sstream>
#include <string>
#include <tuple>
#include <vector>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/locale.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/optional.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/variant.hpp>
#include <boost/variant/get.hpp>

#include "common.hpp"
#include "regex_parser.hpp"
#include "config.hpp"

namespace fusion = boost::fusion;
namespace x3 = boost::spirit::x3;

static_assert(sizeof(char32_t) == sizeof(wchar_t), "wchar is not 32bit, but it's required for spirit x3");

namespace ast {
    using optional_n = boost::optional<unsigned int>;

    using multiplier_amount = unsigned int;

    struct multiplier_range {
        optional_n min;
        optional_n max;

        multiplier_range() = default;
        multiplier_range(const optional_n& min, const optional_n& max) : min(min), max(max) {}
    };

    struct multiplier_plus {};
    struct multiplier_question {};
    struct multiplier_star {};

    using multiplier = boost::variant<multiplier_range, multiplier_amount, multiplier_plus, multiplier_question, multiplier_star>;

    using character = char32_t;

    struct character_range {
        character begin;
        character end;

        character_range() = default;
        character_range(character begin, character end) : begin(begin), end(end) {}
    };

    using characterclass_element = boost::variant<character_range, character>;

    using characterclass = std::vector<characterclass_element>;

    using word = std::vector<character>;

    using chunkcontent = boost::variant<characterclass, word>;

    struct chunk {
        chunkcontent content;
        boost::optional<multiplier> amount;
    };

    using regex = std::vector<chunk>;

    using fusion::operator<<;
}

BOOST_FUSION_ADAPT_STRUCT(ast::multiplier_range, min, max)
BOOST_FUSION_ADAPT_STRUCT(ast::character_range, begin, end)
BOOST_FUSION_ADAPT_STRUCT(ast::chunk, content, amount)

namespace parser {
    static x3::rule<class multiplier_amount, ast::multiplier_amount> multiplier_amount = "multiplier_amount";
    static x3::rule<class multiplier_range, ast::multiplier_range> multiplier_range = "multiplier_range";
    static x3::rule<class multiplier_plus, ast::multiplier_plus> multiplier_plus = "multiplier_plus";
    static x3::rule<class multiplier_question, ast::multiplier_question> multiplier_question = "multiplier_question";
    static x3::rule<class multiplier_star, ast::multiplier_star> multiplier_star = "multiplier_star";
    static x3::rule<class multiplier, ast::multiplier> multiplier = "multiplier";
    static x3::rule<class character, ast::character> character = "character";
    static x3::rule<class character_range, ast::character_range> character_range = "character_range";
    static x3::rule<class characterclass_element, ast::characterclass_element> characterclass_element = "characterclass_element";
    static x3::rule<class characterclass, ast::characterclass> characterclass = "characterclass";
    static x3::rule<class word, ast::word> word = "word";
    static x3::rule<class chunkcontent, ast::chunkcontent> chunkcontent = "chunkcontent";
    static x3::rule<class chunk, ast::chunk> chunk = "chunk";
    static x3::rule<class regex, ast::regex> regex = "regex";

    auto const multiplier_amount_def = '{' >> x3::uint_ >> '}';
    auto const multiplier_range_def = '{' >> -x3::uint_ >> ',' >> -x3::uint_ >> '}';
    auto const multiplier_plus_def = x3::omit['+'];
    auto const multiplier_question_def = x3::omit['?'];
    auto const multiplier_star_def = x3::omit['*'];
    auto const multiplier_def = multiplier_range | multiplier_amount | multiplier_plus | multiplier_question | multiplier_star;
    auto const character_def = x3::char_ - '[' - ']' - '{' - '}' - '+' - '*' - '?' - '-' - static_cast<wchar_t>(0x00000000) - static_cast<wchar_t>(0xffffffff);
    auto const character_range_def = character >> '-' >> character;
    auto const characterclass_element_def = character_range | character;
    auto const characterclass_def = '[' >> +(characterclass_element) >> ']';
    auto const wordelement_def = characterclass | character;
    auto const word_def = +character;
    auto const chunkcontent_def = characterclass | word;
    auto const chunk_def = chunkcontent >> (-multiplier);
    auto const regex_def = +chunk;

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
    BOOST_SPIRIT_DEFINE(
        multiplier_amount,
        multiplier_range,
        multiplier_plus,
        multiplier_question,
        multiplier_star,
        multiplier,
        character,
        character_range,
        characterclass_element,
        characterclass,
        word,
        chunkcontent,
        chunk,
        regex
    )
#pragma clang diagnostic pop
}

ast::regex parse_ast(const std::u32string& input) {
    ast::regex result;
    auto it = input.begin();
    auto end = input.end();
    bool r = phrase_parse(it, end, parser::regex, x3::standard_wide::space, result);
    if (r && it == end) {
        return result;
    } else {
        std::string msg("malformed regex: ");
        auto pos = static_cast<std::size_t>(it - input.begin());
        std::stringstream errmsg;
        errmsg << msg << boost::locale::conv::utf_to_utf<char>(input) << std::endl
            << std::string(msg.size() + pos, ' ') << "^";
        throw user_error(errmsg.str());
    }
}

namespace graph {
    using slot_inner_t = std::vector<std::uint32_t>;
    using slot_t = std::shared_ptr<slot_inner_t>;

    slot_t make_slot(std::initializer_list<uint32_t> data) {
        return std::make_shared<slot_inner_t>(data);
    }

    struct node {
        std::vector<std::pair<char32_t, slot_t>> next;
        std::uint32_t id;

        node(std::uint32_t& id) : id(id++) {}
    };
    using node_t = std::shared_ptr<node>;
    using graph_t = std::vector<node_t>;
}

namespace transformers {
    using collection_slots_t = std::vector<graph::slot_t>;
    using transformer_result_t = std::pair<graph::graph_t, collection_slots_t>;

    class characterclass_element_visitor : public boost::static_visitor<ast::character_range> {
        public:
            ast::character_range operator()(const ast::character& character) const {
                return ast::character_range(character, character);
            }

            ast::character_range operator()(const ast::character_range& character_range) const {
                return character_range;
            }
    };

    class generic_visitor : public boost::static_visitor<transformer_result_t> {
        public:
            generic_visitor(std::uint32_t& id, collection_slots_t slots) : id(id), slots(slots) {}

        protected:
            std::uint32_t& id;
            collection_slots_t slots;
    };

    class character_transformer : public generic_visitor {
        public:
            using generic_visitor::generic_visitor;

            transformer_result_t operator()(const ast::character& character) const {
                // 1. create new node
                auto result = std::make_shared<graph::node>(id);

                // 2. connect last ones to this one
                for (auto& last : slots) {
                    last->push_back(result->id);
                }

                // 3. fill this one
                result->next.push_back(std::make_pair(0, graph::make_slot({serial::id_fail})));
                result->next.push_back(std::make_pair(character, graph::make_slot({})));
                result->next.push_back(std::make_pair(character + 1, graph::make_slot({serial::id_fail})));

                // 4. get slots
                collection_slots_t slots_new{std::get<1>(result->next[1])};

                // 5. done
                return {graph::graph_t{std::move(result)}, slots_new};
            }
    };

    class chunkcontent_transformer : public generic_visitor {
        public:
            using generic_visitor::generic_visitor;

            transformer_result_t operator()(const ast::word& word) const {
                graph::graph_t result_nodes;
                collection_slots_t slots_new = slots;
                for (const auto& character : word) {
                    auto sub_result = character_transformer(id, slots_new)(character);
                    result_nodes.insert(result_nodes.end(), std::get<0>(sub_result).begin(), std::get<0>(sub_result).end());
                    slots_new = std::get<1>(sub_result);
                }
                return std::make_pair(result_nodes, slots_new);
            }

            transformer_result_t operator()(const ast::characterclass& characterclass) const {
                // 1. create new node
                auto result = std::make_shared<graph::node>(id);

                // 2. connect last ones to this one
                for (auto& last : slots) {
                    last->push_back(result->id);
                }

                // 3. prepare and merge ranges
                std::vector<ast::character_range> ranges;
                for (const auto& x : characterclass) {
                    ranges.push_back(boost::apply_visitor(characterclass_element_visitor(), x));
                }
                std::sort(ranges.begin(), ranges.end(), [](const ast::character_range& a, const ast::character_range& b) {
                    return a.begin < b.begin;
                });
                std::vector<ast::character_range> ranges_dedup;
                for (const auto& x : ranges) {
                    if (ranges_dedup.empty()) {
                        ranges_dedup.push_back(x);
                    } else {
                        auto& last = ranges_dedup[ranges_dedup.size() - 1];
                        if (x.begin <= last.end + 1) {
                            last.end = x.end;
                        } else {
                            ranges_dedup.push_back(x);
                        }
                    }
                }
                if (ranges_dedup.size() > cfg::max_ranges) {
                    throw user_error("Too many ranges in character class!");
                }

                // 4. fill this one
                result->next.push_back(std::make_pair(0, graph::make_slot({serial::id_fail})));
                collection_slots_t slots_new{};
                char32_t last_char = 0;
                for (const auto& r : ranges_dedup) {
                    if (last_char > 0) { // add range up to this element, skip first
                        result->next.push_back(std::make_pair(last_char + 1, graph::make_slot({serial::id_fail})));
                    }
                    result->next.push_back(std::make_pair(r.begin, graph::make_slot({})));
                    slots_new.push_back(std::get<1>(result->next[result->next.size() - 1]));
                    last_char = r.end;
                }
                result->next.push_back(std::make_pair(last_char + 1, graph::make_slot({serial::id_fail})));

                // 4. done
                return {graph::graph_t{std::move(result)}, slots_new};
            }
    };

    class multiplier_transformator : public generic_visitor {
        public:
            multiplier_transformator(std::uint32_t& id, collection_slots_t slots, const ast::chunkcontent& content) : generic_visitor(id, slots), content(content) {}

            transformer_result_t operator()(const ast::multiplier_amount& amount) const {
                return doit(amount, ast::optional_n(amount));
            }

            transformer_result_t operator()(const ast::multiplier_range& range) const {
                std::size_t min = 0;
                if (range.min) {
                    min = *(range.min);
                }
                if (range.max && *(range.max) < min) {
                    throw user_error("Illegal regex multiplier!");
                }
                return doit(min, range.max);
            }

            transformer_result_t operator()(const ast::multiplier_plus& /*plus*/) const {
                return doit(1, boost::none);
            }

            transformer_result_t operator()(const ast::multiplier_question& /*question*/) const {
                return doit(0, ast::optional_n(1));
            }

            transformer_result_t operator()(const ast::multiplier_star& /*star*/) const {
                return doit(0, boost::none);
            }

        protected:
            const ast::chunkcontent& content;

            transformer_result_t doit(std::size_t min, ast::optional_n max) const {
                if (min > cfg::max_multiplier) {
                    throw user_error("multiplier minimum is too large!");
                }
                if (max && *max > cfg::max_multiplier) {
                    throw user_error("multiplier maximum is too large!");
                }
                graph::graph_t nodes_result;

                // 1. start with the words we need at least
                collection_slots_t slots_current = slots;
                graph::graph_t nodes_current;
                std::size_t i = 0;
                for (; i < min; ++i) {
                    auto sub_result = boost::apply_visitor(chunkcontent_transformer(id, slots_current), content);
                    nodes_result.insert(nodes_result.end(), nodes_current.begin(), nodes_current.end());
                    std::tie(nodes_current, slots_current) = sub_result;
                }

                // 2. add optional words
                collection_slots_t slots_result;
                if (max) {
                    // some maximum => emit nodes, add all of them to result
                    // also emit 1 additional word, so we can link it to FAIL

                    // a) create nodes
                    for (; i <= *max; ++i) {
                        auto sub_result = boost::apply_visitor(chunkcontent_transformer(id, slots_current), content);
                        nodes_result.insert(nodes_result.end(), nodes_current.begin(), nodes_current.end());
                        slots_result.insert(slots_result.end(), slots_current.begin(), slots_current.end());
                        std::tie(nodes_current, slots_current) = sub_result;
                    }

                    // b) now link the last node to FAIL, do NOT add it to slots_result
                    nodes_result.insert(nodes_result.end(), nodes_current.begin(), nodes_current.end());
                    for (auto& slot : slots_current) {
                        slot->push_back(serial::id_fail);
                    }
                } else {
                    // no max => create new word and link slots to nodes current (loop)

                    // a) create node
                    auto sub_result = boost::apply_visitor(chunkcontent_transformer(id, slots_current), content);
                    nodes_result.insert(nodes_result.end(), nodes_current.begin(), nodes_current.end());
                    slots_result.insert(slots_result.end(), slots_current.begin(), slots_current.end());
                    std::tie(nodes_current, slots_current) = sub_result;

                    // b) link it
                    nodes_result.insert(nodes_result.end(), nodes_current.begin(), nodes_current.end());
                    for (auto& slot : slots_current) {
                        slot->push_back(nodes_current[0]->id);
                    }
                    slots_result.insert(slots_result.end(), slots_current.begin(), slots_current.end());
                }

                // done
                return {nodes_result, slots_result};
            }
    };

    class chunk_transfomer : public generic_visitor {
        public:
            using generic_visitor::generic_visitor;

            transformer_result_t operator()(const ast::chunk& chunk) const {
                if (chunk.amount) {
                    return boost::apply_visitor(multiplier_transformator(id, slots, chunk.content), *(chunk.amount));
                } else {
                    return boost::apply_visitor(chunkcontent_transformer(id, slots), chunk.content);
                }
            }
    };
}

graph::graph_t ast_to_graph(const ast::regex& r) {
    // start graph
    std::uint32_t id = 0;
    graph::graph_t nodes;
    nodes.push_back(std::make_shared<graph::node>(id)); // FAIL node
    nodes.push_back(std::make_shared<graph::node>(id)); // OK node
    transformers::collection_slots_t slots; // no slots yet

    // iterate over entire regex
    for (const auto& chunk : r) {
        auto sub_result = transformers::chunk_transfomer(id, slots)(chunk);
        nodes.insert(nodes.end(), std::get<0>(sub_result).begin(), std::get<0>(sub_result).end());
        slots = std::get<1>(sub_result);
    }

    // fill remaining slots with good outcome
    for (auto& last : slots) {
        last->push_back(serial::id_ok);
    }

    sanity_assert(id == nodes.size(), "Some nodes are lost :(");;
    return nodes;
}

template <typename T>
void write_to_buffer(serial::buffer& b, std::size_t base, T element) {
    static_assert(sizeof(serial::word) == 4, "ups, need to rewrite the serializer!");
    static_assert(sizeof(T) % sizeof(serial::word) == 0, "that doesn't match the word boundaries!");

    // XXX: check big vs little endian!
    for (std::size_t i = 0; i < sizeof(T) / sizeof(serial::word); ++i) {
        b[base + i] = (element >> (8 * 4 * i)) & 0xffffffff;
    }
}

serial::graph serialize(const graph::graph_t& g) {
    // 1. calculate maximum numbers for allocations
    std::size_t n = g.size();
    std::size_t m = 0;
    std::size_t o = 0;
    for (const auto& node : g) {
        m = std::max(m, node->next.size());
        for (const auto& value_slot : node->next) {
            o = std::max(o, std::get<1>(value_slot)->size());
        }
    }

    // 2. create buffer
    serial::graph result(n, o);
    // at this point, the dispatch table exists

    // 3. write data
    for (std::size_t i_node = 0; i_node < n; ++i_node) {
        const auto& node = g[i_node];
        std::size_t base_node = result.size();

        // write current size to dispatch table
        write_to_buffer(result.data, i_node, static_cast<serial::id>(base_node));

        // start node by writing its size
        result.grow(1);
        write_to_buffer(result.data, base_node, static_cast<serial::id>(node->next.size()));

        // write node body
        std::size_t base_node_body = base_node + 1;
        for (std::size_t i_value_slot = 0; i_value_slot < node->next.size(); ++i_value_slot) {
            const auto& value_slot = node->next[i_value_slot];

            std::size_t base_value_slot = base_node_body + i_value_slot * (1 + o);
            serial::character c = std::get<0>(value_slot);

            // write character that belongs to slot
            result.grow(1);
            write_to_buffer(result.data, base_value_slot, c);

            // write fixed size, sorted, dedup data to slot
            std::size_t base_value_slot_payload = base_value_slot + 1;
            std::vector<std::uint32_t> entries_sorted(std::get<1>(value_slot)->begin(), std::get<1>(value_slot)->end());
            std::sort(entries_sorted.begin(), entries_sorted.end());
            entries_sorted.erase(std::unique(entries_sorted.begin(), entries_sorted.end()), entries_sorted.end());
            for (std::size_t i_slot_entry = 0; i_slot_entry < o; ++i_slot_entry) {
                result.grow(1);
                if (i_slot_entry < entries_sorted.size()) {
                    std::size_t base_slot_entry = base_value_slot_payload + i_slot_entry;
                    serial::id id = entries_sorted[i_slot_entry];
                    write_to_buffer(result.data, base_slot_entry, id);
                } // else => data is 0
            }
        }
    }

    // 4. done
    return result;
}

serial::graph string_to_graph(const std::u32string& input) {
    auto r = parse_ast(input);
    if (r.empty()) {
        throw user_error("Empty regex is not allowed!");
    }

    auto g = ast_to_graph(r);

    return serialize(g);
}


#pragma once
#include "vector.hpp"
#include "matrix.hpp"
#include "quat.hpp"
#include "bits.hpp"
#include "abstbuff.hpp"
#include "error.hpp"
#include "optional.hpp"
#include <cassert>
#include <cstring>
#include <boost/regex_fwd.hpp>
#include "structure/angle.hpp"

namespace spn {
	//! Y軸を上とした時のZベクトルに対するXベクトルを算出, またはベクトルに垂直なベクトルを得る
	template <bool A>
	VecT<3,A> GetVerticalVec(const VecT<3,A>& zvec) {
		using VT = VecT<3,A>;
		auto vt = zvec % VT(0,1,0);
		if(vt.len_sq() < 1e-6f)
			vt = zvec % VT(1,0,0);
		return vt.normalization();
	}
	template <bool A>
	spn::Optional<VecT<3,A>> NormalFromPoints(const VecT<3,A>& v0,const VecT<3,A>& v1, const VecT<3,A>& v2) {
		auto tmp = (v1 - v0) % (v2 - v0);
		if(tmp.len_sq() < 1e-6f)
			return spn::none;
		return tmp.normalization();
	}
	//! 任意の戻り値を返す関数 (定義のみ)
	template <class T>
	T ReturnT();
	//! 関数の戻り値型を取得
	/*! ReturnType<T>::type */
	template <class T>
	struct ReturnType;
	template <class RT, class... Args>
	struct ReturnType<RT (*)(Args...)> {
		using type = RT; };
	template <class RT, class OBJ, class... Args>
	struct ReturnType<RT (OBJ::*)(Args...)> {
		using type = RT; };
	template <class RT, class OBJ, class... Args>
	struct ReturnType<RT (OBJ::*)(Args...) const> {
		using type = RT; };
	inline uint32_t ARGBtoRGBA(uint32_t val) {
		return (val & 0xff00ff00) | ((val>>16)&0xff) | ((val&0xff)<<16);
	}
	inline uint32_t SetAlphaR(uint32_t val) {
		return (val & 0x00ffffff) | ((val&0xff)<<24);
	}

	//! ポインタ読み替え変換
	template <class T0, class T1>
	inline T1 RePret(const T0& val) {
		static_assert(sizeof(T0) == sizeof(T1), "invalid reinterpret value");
		return *reinterpret_cast<const T1*>(&val);
	}
	//! aがb以上だったらaからsizeを引いた値を返す
	inline int CndSub(int a, int b, int size) {
		return a - (size & ((b - a - 1) >> 31));
	}
	//! aがb以上だったらaからbを引いた値を返す
	inline int CndSub(int a, int b) {
		return CndSub(a, b, b);
	}
	//! aがbより小さかったらaにsizeを足した値を返す
	inline int CndAdd(int a, int b, int size) {
		return a + (size & ((a - b) >> 31));
	}
	//! aが0より小さかったらaにsizeを足した値を返す
	inline int CndAdd(int a, int size) {
		return CndAdd(a, 0, size);
	}
	//! aがlowerより小さかったらsizeを足し、upper以上だったらsizeを引く
	inline int CndRange(int a, int lower, int upper, int size) {
		return a + (size & ((a - lower) >> 31))
					- (size & ((upper - a - 1) >> 31));
	}
	//! aが0より小さかったらsizeを足し、size以上だったらsizeを引く
	inline int CndRange(int a, int size) {
		return CndRange(a, 0, size, size);
	}
	//! ポインタの読み替え
	template <class T, class T2>
	T* ReinterpretPtr(T2* ptr) {
		using TD = typename std::decay<T2>::type;
		static_assert(std::is_integral<TD>::value || std::is_floating_point<TD>::value, "typename T must number");
		return reinterpret_cast<T*>(ptr);
	}
	//! リファレンスの読み替え
	template <class T, class T2>
	T& ReinterpretRef(T2& val) {
		return *reinterpret_cast<T*>(&val);
	}
	//! 値の読み替え
	template <class T, class T2>
	T ReinterpretValue(const T2& val) {
		return *reinterpret_cast<const T*>(&val);
	}
	constexpr uint32_t FloatOne = 0x3f800000;		//!< 1.0fの、整数表現
	//! 引数がプラスなら1, マイナスなら-1を返す
	float inline PlusMinus1(float val) {
		auto ival = spn::ReinterpretValue<uint32_t>(val);
		return spn::ReinterpretValue<float>(FloatOne | (ival & 0x80000000));
	}
	//! PlusMinus1(float)の引数がintバージョン
	float inline PlusMinus1(int val) {
		return spn::ReinterpretValue<float>(FloatOne | ((val>>31) & 0x80000000)); }
	//! PlusMinus1(float)の引数がunsigned intバージョン
	float inline PlusMinus1(unsigned int val) { return PlusMinus1(static_cast<int>(val)); }
	//! 2次元ベクトルを係数で混ぜ合わせる
	Vec2 MixVector(const float (&cf)[3], const Vec2& p0, const Vec2& p1, const Vec2& p2);
	//! 重心座標を計算
	void BarycentricCoord(float ret[3], const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec2& pos);
	//! クラメルの公式で使う行列式を計算
	float CramerDet(const Vec3& v0, const Vec3& v1, const Vec3& v2);
	float CramerDet(const Vec2& v0, const Vec2& v1);
	//! 3元1次方程式の解をクラメルの公式を用いて計算
	/*! @param detInv[in] 行列式(v0,v1,v2)の逆数 */
	Vec3 CramersRule(const Vec3& v0, const Vec3& v1, const Vec3& v2, const Vec3& a0, float detInv);
	//! 2元1次方程式を計算
	Vec2 CramersRule(const Vec2& v0, const Vec2& v1, const Vec2& a0, float detInv);
	//! グラム・シュミットの正規直交化法
	void GramSchmidtOrth(Vec3& v0, Vec3& v1, Vec3& v2);

	struct YawPitchDist {
		RadF	yaw, pitch;
		float	distance;
		//! 方向ベクトルをYaw,Pitch,Distanceに分解
		static YawPitchDist FromPos(const spn::Vec3& pos);
		//! YawPitchDistの位置から座標原点を見る姿勢
		std::pair<spn::Vec3, spn::Quat> toOffsetRot() const;
	};
	std::ostream& operator << (std::ostream& os, const YawPitchDist& ypd);

	struct AffineParts {
		AVec3 offset,
				scale;
		AQuat rotation;
		//! アフィン成分分解
		static AffineParts Decomp(const AMat43& m);
	};
	//! ソースコード文字列に行番号を付ける
	/*! \param[in] src 元のソースコード
		\param[in] numOffset 最初の数値(0や負数だとその分は番号を振らない)
		\param[in] viewNum 行番号の表示を開始する位置
		\param[in] bPrevLR 最初に改行を入れるか
		\param[in] bPostLR 最後に改行を入れるか*/
	std::string AddLineNumber(const std::string& src, int numOffset, int viewNum, bool bPrevLR, bool bPostLR);

	using OPLinePos = spn::Optional<std::pair<int, typename std::string::const_iterator>>;
	//! regexに合致する最初の行
	/*!	\param[in] str 検索する文字列
		\param[in] re 検索パターン(行単位)
		\retval 合致する行が見つかった時: std::pair<先頭からの行番号, その位置>
		\retval 見つからなかった時: spn::none*/
	OPLinePos RegexFindFirst(const std::string& str, const boost::regex& re);
	//! regexに合致する最後の行
	OPLinePos RegexFindLast(const std::string& str, const boost::regex& re);
}


/*
 * Copyright 2015 David A. Boyuka II
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * cblq-setops-nary2.cpp
 *
 *  Created on: Mar 25, 2014
 *      Author: David A. Boyuka II
 */

#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>

#include <boost/smart_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/dynamic_bitset.hpp>
#include <boost/iterator/counting_iterator.hpp>

#include "pique/region/cblq/cblq.hpp"
#include "pique/setops/setops.hpp"
#include "pique/setops/cblq/cblq-setops.hpp"

struct child_ind_helper {
	typedef uint16_t lookup_index_t;
	static constexpr int BITS_PER_CODE = 2;
	static constexpr int BITS_PER_LOOKUP = sizeof(lookup_index_t) * 8;
	static constexpr int CODES_PER_LOOKUP = BITS_PER_LOOKUP / BITS_PER_CODE;
	static constexpr uint64_t LOOKUP_ENTRIES = 1ULL << BITS_PER_LOOKUP;
	static constexpr lookup_index_t LOOKUP_MASK = ~(lookup_index_t)0;

	uint64_t children[CODES_PER_LOOKUP];
	int nchildren;

	static child_ind_helper CHILD_IND_TABLE[LOOKUP_ENTRIES];
	static bool CHILD_IND_TABLE_INIT;

	static void ensure_init();
};

bool child_ind_helper::CHILD_IND_TABLE_INIT = false;
child_ind_helper child_ind_helper::CHILD_IND_TABLE[LOOKUP_ENTRIES];
void child_ind_helper::ensure_init() {
	if (CHILD_IND_TABLE_INIT)
		return;

	for (uint64_t word = 0; word < LOOKUP_ENTRIES; word++) {
		uint64_t *out = CHILD_IND_TABLE[word].children;
		for (int i = 0; i < CODES_PER_LOOKUP; i++) {
			if (word & (0b10 << i * BITS_PER_CODE)) {// Two code
				*out = i;
				++out;
			}
		}
		CHILD_IND_TABLE[word].nchildren = out - CHILD_IND_TABLE[word].children;
	}
	CHILD_IND_TABLE_INIT = true;
}

template<int ndim>
inline static int count_two_codes(typename CBLQRegionEncoding<ndim>::cblq_word_t word) {
	static constexpr int NUM_LOOKUPS = (CBLQRegionEncoding<ndim>::CODES_PER_WORD - 1) / child_ind_helper::CODES_PER_LOOKUP + 1;

	int children = 0;
	for (int i = 0; i < NUM_LOOKUPS; ++i) {
		const typename child_ind_helper::lookup_index_t lookup_key = ((word >> (i * child_ind_helper::BITS_PER_LOOKUP)) & child_ind_helper::LOOKUP_MASK);
		children += child_ind_helper::CHILD_IND_TABLE[lookup_key].nchildren;
	}

	return children;
}

template<int ndim>
inline static void append_child_inds(typename CBLQRegionEncoding<ndim>::cblq_word_t word, uint64_t base_ind, typename std::vector< int64_t >::iterator &out_inds_it) {
	static constexpr int NUM_LOOKUPS = (CBLQRegionEncoding<ndim>::CODES_PER_WORD - 1) / child_ind_helper::CODES_PER_LOOKUP + 1;

	if (CBLQRegionEncoding<ndim>::CODES_PER_WORD <= child_ind_helper::CODES_PER_LOOKUP) {
		const child_ind_helper &lookup = child_ind_helper::CHILD_IND_TABLE[word];

		const uint64_t *child_ind_offset = lookup.children;
		const int nchildren = lookup.nchildren;

		for (int i = 0; i < CBLQRegionEncoding<ndim>::CODES_PER_WORD; ++i)
			out_inds_it[i] = base_ind + child_ind_offset[i]; // This will overflow the valid end of out_level_inds, but we allocated enough extra sentinel space earlier so that it won't segfault

		out_inds_it += nchildren;
	} else {
		for (int lookupnum = 0; lookupnum < NUM_LOOKUPS; ++lookupnum) {
			const typename child_ind_helper::lookup_index_t lookup_key = (word >> (lookupnum * child_ind_helper::BITS_PER_LOOKUP)) & child_ind_helper::LOOKUP_MASK;
			const child_ind_helper &lookup = child_ind_helper::CHILD_IND_TABLE[lookup_key];

			const uint64_t *child_ind_offset = lookup.children;
			const int nchildren = lookup.nchildren;

			for (int i = 0; i < child_ind_helper::CODES_PER_LOOKUP; ++i)
				out_inds_it[i] = base_ind + child_ind_offset[i] + lookupnum * child_ind_helper::CODES_PER_LOOKUP;

			out_inds_it += nchildren;
		}
	}
}

// Handles postprocessing after iterating over a CBLQ level:
// 1. Densely remaps inds produced by the level for use during the next level
// 2. Maps any 3-codes into 1-codes
// 3. Computes the level_len for the output CBLQ's next level
template<int ndim, NArySetOperation op>
inline static void level_postprocess(
		typename std::vector< typename CBLQRegionEncoding<ndim>::cblq_word_t >::iterator next_level_word_it,
		const std::vector< uint64_t > &out_level_inds_operand_ends,
		std::vector< int64_t > &inds /* in: output of prev level, out: input of next level */,
		uint64_t &level_len /* in: prev level, out: next level */)
{
	static const int64_t CHUNK_SIZE = 1ULL << 13;
	using cblq_word_t = typename CBLQRegionEncoding<ndim>::cblq_word_t;

	assert(CHUNK_SIZE % CBLQRegionEncoding<ndim>::CODES_PER_WORD == 0);

	// First, compute the compaction mapping for the outinds (remove all indices pointing to NO-OP and DELETE actions)
	const int64_t max_ind = level_len * CBLQRegionEncoding<ndim>::CODES_PER_WORD; // One ind per code in the output of this level
	std::vector< int64_t > ind_mapping;

	const size_t num_operands = out_level_inds_operand_ends.size();

	int64_t mapped_ind = 0;

	auto scan_word_it = next_level_word_it;

	std::vector< typename std::vector<int64_t>::iterator > ind_update_its;
	std::vector< typename std::vector<int64_t>::iterator > ind_update_end_its;
	for (auto operand_end_it = out_level_inds_operand_ends.begin(); operand_end_it != out_level_inds_operand_ends.end(); ++operand_end_it) {
		uint64_t operand_end_off = *operand_end_it;
		ind_update_its.push_back(ind_update_end_its.empty() ? inds.begin() : ind_update_end_its.back());
		ind_update_end_its.push_back(inds.begin() + operand_end_off);
	}

	for (int64_t chunk_begin_ind = 0; chunk_begin_ind < max_ind; chunk_begin_ind += CHUNK_SIZE) {
		const int64_t chunk_end_ind = std::min(chunk_begin_ind + CHUNK_SIZE, max_ind);
		const int64_t cur_chunk_size = chunk_end_ind - chunk_begin_ind;
		ind_mapping.clear();
		ind_mapping.resize(cur_chunk_size, -1); // Initially, all inds map to -1 (i.e. DELETE)

		for (int64_t cur_ind = 0; cur_ind < cur_chunk_size; ++scan_word_it) {
			cblq_word_t word = *scan_word_it;
			if (op == NArySetOperation::UNION) {
				word &= ~((word & CBLQRegionEncoding<ndim>::ONE_CODES_WORD) << 1); // Fix 3-codes -> 1-codes
			} else if (op == NArySetOperation::INTERSECTION) {
				// FF->FF
				// FT->FT
				// TF->FF
				// TT->TF

				// XY &= Y1, XY &= 1(~X)

				word &= (word << 1) | CBLQRegionEncoding<ndim>::ONE_CODES_WORD;
				word &= (~word >> 1) | CBLQRegionEncoding<ndim>::TWO_CODES_WORD;
			} else
				abort();
			*scan_word_it = word;

			for (int codepos = 0; codepos < CBLQRegionEncoding<ndim>::CODES_PER_WORD; ++codepos, ++cur_ind) {
				if (word & 0b10 << (codepos * CBLQRegionEncoding<ndim>::BITS_PER_CODE))
					ind_mapping[cur_ind] = mapped_ind++;
			}
		}

		// Densely remap all outinds produced
		for (int operand = 0; operand < num_operands; operand++) {
			auto ind_update_it = ind_update_its[operand];
			auto ind_update_end_it = ind_update_end_its[operand];
			for (; ind_update_it != ind_update_end_it; ++ind_update_it) {
				const int64_t ind = *ind_update_it;
				if (ind >= 0) {
					if (ind >= chunk_end_ind)
						break;
					else
						*ind_update_it = ind_mapping[ind - chunk_begin_ind];
				}
			}
			ind_update_its[operand] = ind_update_it;
		}
	}

	level_len = mapped_ind; // Exclusive upper bound on all mapped outinds
}

template<int ndim>
template<NArySetOperation op>
inline boost::shared_ptr< CBLQRegionEncoding<ndim> >
CBLQSetOperationsNAry3Fast<ndim>::nary_set_op_impl(RegionEncodingCPtrCIter cblq_it, RegionEncodingCPtrCIter cblq_end_it) const {
	const boost::optional< bool > common_has_dense_suffix = CBLQRegionEncoding<ndim>::deduce_common_suffix_density(cblq_it, cblq_end_it);
	assert(common_has_dense_suffix); // all operands must have compatible suffix density (i.e., each the same/empty)

	const int levels = (*cblq_it)->get_num_levels();
	const bool has_dense_suffix = *common_has_dense_suffix;
	const int non_dense_levels = has_dense_suffix ? levels - 1 : levels;
	const uint64_t domain_size = (*cblq_it)->get_domain_size();

	// Initialize iterators to traverse through the words of each CBLQ operand passed to this function (as well as the implicit "this" CBLQ)
	size_t num_operands = 0;
	std::vector< typename std::vector<cblq_word_t>::const_iterator > cblq_word_its;
	std::vector< typename std::vector<cblq_word_t>::const_iterator > cblq_word_end_its;
	std::vector< typename std::vector<uint64_t>::const_iterator > cblq_level_len_its;
	for (auto it = cblq_it; it != cblq_end_it; it++) {
		const CBLQRegionEncoding<ndim> &cblq = **it; // first * = deref iterator, second * = deref pointer to CBLQ that the iterator was holding
		// Ensure uniform level count and suffix denseness across all CBLQs
		assert(cblq.get_num_levels() == levels);
		//assert(cblq.has_dense_suffix == has_dense_suffix); // now checked above
		assert(cblq.domain_size == domain_size);

		num_operands++;
		cblq_word_its.push_back(cblq.words.cbegin());
		cblq_word_end_its.push_back(cblq.words.cend());
		cblq_level_len_its.push_back(cblq.level_lens.cbegin());
	}

	// Allocate a new CBLQ for output
	boost::shared_ptr< CBLQRegionEncoding<ndim> > output = boost::make_shared< CBLQRegionEncoding<ndim> >(domain_size);
	output->has_dense_suffix = has_dense_suffix;
	output->level_lens.resize(levels);

	std::vector<cblq_word_t> &output_words = output->words;

	// Input operand target index array. Produced in the previous step, indicates which output CBLQ word should be modified by each input word
	// Outind arrays are packed back-to-back, since it is known how many belong to each operand (= operand.level_lens[level])
	std::vector<int64_t> in_level_inds(num_operands, 0); // Fill with 0 indices for each operand

	// Output operand target index array. Built up during processing of a level, then dense-compacted and moved to in_level_outinds
	std::vector<int64_t> out_level_inds;
	std::vector<uint64_t> out_level_inds_operand_ends(num_operands);

	// Size of level for the next iteration (computed at the end of each iteration for the next iteration)
	size_t next_level_len = 1;

	// For each CBLQ level
	for (int level = 0; level < non_dense_levels; level++) {
		output->level_lens[level] = next_level_len; // The number of words we will output this level is already known: it's the number of non-delete actions that are queued

		// Allocate space in out_level_inds
		if (level + 1 < levels) {
			uint64_t expected_out_inds = 0; // The number of inds produced by the next level is
			if (level + 1 < non_dense_levels) { // Next level is the dense suffix
				for (size_t operand = 0; operand < num_operands; ++operand)
					expected_out_inds += *(cblq_level_len_its[operand] + 1); // Add the number of words in the next level
			} else {
				for (auto it = cblq_it; it != cblq_end_it; ++it)
					expected_out_inds += (*it)->dense_suffix->get_num_semiwords(); // Add the number of semiwords in the next level
			}
			out_level_inds.resize(expected_out_inds + CBLQRegionEncoding<ndim>::CODES_PER_WORD /* Extra sentinel to allow intentional overrun */);
		} else {
			out_level_inds.resize(CBLQRegionEncoding<ndim>::CODES_PER_WORD /* Extra sentinel to allow intentional overrun */);
		}

		// Allocate space in output_words and set up the base iterator
		const size_t prev_output_words_size = output_words.size();
		output_words.resize(prev_output_words_size + next_level_len, (op == NArySetOperation::UNION) ? CBLQRegionEncoding<ndim>::ZERO_CODES_WORD : CBLQRegionEncoding<ndim>::ONE_CODES_WORD);
		const auto level_output_words_it = output_words.begin() + prev_output_words_size;

		// Set up in and out ind iterators
		auto in_inds_it = in_level_inds.cbegin();
		auto out_inds_it = out_level_inds.begin();

		// For each operand, apply all CBLQ words to the proper locations in the output (or skip them if they are to be deleted)
		for (size_t operand = 0; operand < num_operands; operand++) {
			auto &cblq_level_len_it = cblq_level_len_its[operand];
			const uint64_t level_len = *cblq_level_len_it;
			++cblq_level_len_it;

			auto cblq_word_it = cblq_word_its[operand]; // Get the CBLQ word iterator for this CBLQ
			auto cblq_word_end_it = cblq_word_it + level_len;

			// For each action in which this operand participates
			while (cblq_word_it != cblq_word_end_it) {
				const int64_t ind = *in_inds_it;
				++in_inds_it;

				if (ind >= 0) { // BINARY SETOP
					cblq_word_t oper_cblq_word = *cblq_word_it;
					++cblq_word_it;

					if (op == NArySetOperation::UNION) {
						level_output_words_it[ind] |= oper_cblq_word; // Works for all cases but 1 union 2 resulting in 3. This is cleaned up later
					} else if (op == NArySetOperation::INTERSECTION) {
						level_output_words_it[ind] |= (oper_cblq_word & CBLQRegionEncoding<ndim>::TWO_CODES_WORD); // Add any 2 codes from oper_cblq_word to output
						level_output_words_it[ind] &= (oper_cblq_word | (oper_cblq_word >> 1) | CBLQRegionEncoding<ndim>::TWO_CODES_WORD); // Convert 1-codes to 0-codes and 3-codes to 2-codes for any 0-code in the input
					} else
						abort();

					const uint64_t base_ind = ind * CBLQRegionEncoding<ndim>::CODES_PER_WORD;
					append_child_inds<ndim>(oper_cblq_word, base_ind, out_inds_it); // This will overflow the valid end of out_level_inds, but we allocated enough extra sentinel space earlier so that it won't segfault
				} else { // DELETE
					// Skip the specified number of words, counting the number of two codes therein
					int64_t skip_children = 0;
					for (int64_t i = 0; i < -ind; ++i) {
						skip_children += count_two_codes<ndim>(*cblq_word_it);
						++cblq_word_it;
					}
					// If there were any skipped two codes, enqueue a delete action covering all of them now
					if (skip_children) {
						*out_inds_it = -skip_children;
						++out_inds_it;
					}
				}
			}

			cblq_word_its[operand] = cblq_word_it;
			out_level_inds_operand_ends[operand] = out_inds_it - out_level_inds.begin();
		}

		// Post-process the output of the previous step
		if (level < levels - 1) {
			out_level_inds.resize(out_level_inds.size() - CBLQRegionEncoding<ndim>::CODES_PER_WORD); // Remove the extra sentinel for overflow so that the vector is tight (level_postprocess relies on an accurate size())
			level_postprocess<ndim, op>(level_output_words_it, out_level_inds_operand_ends, out_level_inds, next_level_len); // updates out_level_inds and next_level_len
			in_level_inds = std::move(out_level_inds);
			out_level_inds.clear();
		}
	}

	if (has_dense_suffix) {
		using semiword_block_t = typename CBLQSemiwords<ndim>::block_t;
		const int BITS_PER_BLOCK = std::numeric_limits<semiword_block_t>::digits;

		// Set the (non-dense) leaf level length to 0
		output->level_lens[levels - 1] = 0;

		// Collect dense suffix information and iterators
		std::vector< uint64_t > dense_level_lens;
		std::vector< typename std::vector< semiword_block_t >::const_iterator > in_block_its;
		for (auto it = cblq_it; it != cblq_end_it; it++) {
			const CBLQRegionEncoding<ndim> &cblq = **it;
			dense_level_lens.push_back(cblq.dense_suffix->get_num_semiwords());
			cblq.dense_suffix->ensure_padded();
			in_block_its.push_back(cblq.dense_suffix->semiwords.begin());
		}

		// Set up output
		CBLQSemiwords<ndim> &output_semiwords = *output->dense_suffix;
		std::vector< semiword_block_t > &output_blocks = output_semiwords.semiwords;

		output_semiwords.num_semiwords = next_level_len;
		output_blocks.clear();
		output_blocks.resize(next_level_len / CBLQSemiwords<ndim>::SEMIWORDS_PER_BLOCK + 1, 0);

		// Process dense suffixes
		auto in_inds_it = in_level_inds.begin();
		for (size_t operand = 0; operand < num_operands; ++operand) {
			const uint64_t dense_level_len = dense_level_lens[operand];
			auto in_block_it = in_block_its[operand];

			semiword_block_t cur_block = *in_block_it;
			uint64_t in_block_shift = 0;
			for (uint64_t i = 0; i < dense_level_len;) {
				const int64_t ind = *in_inds_it;
				++in_inds_it;

				if (ind >= 0) {
					// Compute intersection via De Morgan's Law (negation of union of negations). This is the inner negation.
					const semiword_block_t semiword =
							op == NArySetOperation::UNION ?
									((cur_block >> in_block_shift) & CBLQSemiwords<ndim>::SEMIWORD_MASK) :
									(((~cur_block) >> in_block_shift) & CBLQSemiwords<ndim>::SEMIWORD_MASK);

					const uint64_t ind_block = ind / CBLQSemiwords<ndim>::SEMIWORDS_PER_BLOCK;
					const int ind_shift = ind % CBLQSemiwords<ndim>::SEMIWORDS_PER_BLOCK * CBLQRegionEncoding<ndim>::BITS_PER_SEMIWORD;

					output_blocks[ind_block] |= (semiword << ind_shift);

					++i;
					in_block_shift += CBLQRegionEncoding<ndim>::BITS_PER_SEMIWORD;
					if (in_block_shift == BITS_PER_BLOCK) {
						cur_block = *++in_block_it;
						in_block_shift = 0;
					}
				} else {
					const int64_t skip = -ind;
					in_block_shift += (skip * CBLQRegionEncoding<ndim>::BITS_PER_SEMIWORD);
					i += skip;

					if (in_block_shift >= BITS_PER_BLOCK) {
						in_block_it += in_block_shift / BITS_PER_BLOCK;
						in_block_shift = in_block_shift % BITS_PER_BLOCK;
						cur_block = *in_block_it;
					}
				}
			}
		}

		// Compute intersection via De Morgan's Law (negation of union of negations). This is the outer negation.
		if (op == NArySetOperation::INTERSECTION) {
			for (auto it = output_blocks.begin(); it != output_blocks.end(); ++it)
				*it = ~*it;
		}
	}

	return output;
}

template<int ndim>
boost::shared_ptr< CBLQRegionEncoding<ndim> >
CBLQSetOperationsNAry3Fast<ndim>::nary_set_op_impl(RegionEncodingCPtrCIter cblq_it, RegionEncodingCPtrCIter cblq_end_it, NArySetOperation op) const {
	child_ind_helper::ensure_init();

	boost::shared_ptr< CBLQRegionEncoding<ndim> > output;
	if (op == NArySetOperation::UNION)
		output = this->nary_set_op_impl<NArySetOperation::UNION>(cblq_it, cblq_end_it);
	else if (op == NArySetOperation::INTERSECTION)
		output = this->nary_set_op_impl<NArySetOperation::INTERSECTION>(cblq_it, cblq_end_it);
	else
		return this->nary_set_op_default_impl(cblq_it, cblq_end_it, op);

	// Finally, compact the output CBLQ if requested
	if (this->conf.compact_after_setop)
		output->compact();

	return output;
}

//template<int ndim>
//boost::shared_ptr< CBLQRegionEncoding<ndim> >
//CBLQSetOperationsNAry3Fast<ndim>::binary_set_op(boost::shared_ptr< const CBLQRegionEncoding<ndim> > left, boost::shared_ptr< const CBLQRegionEncoding<ndim> > right, NArySetOperation op) const {
//	std::vector< boost::shared_ptr< CBLQRegionEncoding<ndim> > > operands = {
//		boost::const_pointer_cast< CBLQRegionEncoding<ndim> >(left),
//		boost::const_pointer_cast< CBLQRegionEncoding<ndim> >(right)
//	};
//
//	if (op == NArySetOperation::UNION || op == NArySetOperation::INTERSECTION)
//		return this->template CBLQSetOperationsNAry3Fast<ndim>::nary_set_op(operands.begin(), operands.end(), op);
//	else
//		return this->template CBLQSetOperationsFast<ndim>::binary_set_op(left, right, op);
//}

// Explicit instantiation of 2D-4D CBLQ N-ary set operations
template class CBLQSetOperationsNAry3Fast<2>;
template class CBLQSetOperationsNAry3Fast<3>;
template class CBLQSetOperationsNAry3Fast<4>;


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#include <iostream>
#include <string>
#include <boost/tuple/tuple.hpp>

#include <boost/numeric/mtl/matrix/dense2D.hpp>
#include <boost/numeric/mtl/matrix/morton_dense.hpp>
#include <boost/numeric/mtl/matrix/transposed_view.hpp>
#include <boost/numeric/mtl/matrix/parameter.hpp>
#include <boost/numeric/mtl/operation/print_matrix.hpp>
#include <boost/numeric/mtl/operation/sub_matrix.hpp>
#include <boost/numeric/mtl/recursion/matrix_recursator.hpp>
#include <boost/numeric/mtl/recursion/base_case_test.hpp>
#include <boost/numeric/mtl/recursion/for_each.hpp>

using namespace std;

template <typename Recursator>
void print_depth_first(Recursator const& recursator, string str)
{
    if (recursator.is_empty())
	return;
    cout << "\nRecursion: " << str << endl << *recursator;

    // for full recursion remove the string length limitation
    if (!recursator.is_leaf()) {
	print_depth_first(recursator.north_west(), string("north west of ") + str);
	print_depth_first(recursator.south_west(), string("south west of ") + str);
	print_depth_first(recursator.north_east(), string("north east of ") + str);
	print_depth_first(recursator.south_east(), string("south east of ") + str);
    }
}

template <typename Recursator, typename BaseCaseTest>
void recursive_print(Recursator const& recursator, string str, BaseCaseTest const& is_base)
{
    if (is_base(recursator))
	cout << "\nBase case: " << str << endl << *recursator;
    else {
	recursive_print(recursator.north_west(), string("north west of ") + str, is_base);
	recursive_print(recursator.south_west(), string("south west of ") + str, is_base);
	recursive_print(recursator.north_east(), string("north east of ") + str, is_base);
	recursive_print(recursator.south_east(), string("south east of ") + str, is_base);
    }
}

template <typename Recursator, typename BaseCaseTest>
void recursive_print_checked(Recursator const& recursator, string str, BaseCaseTest const& is_base)
{
    if (recursator.is_empty())
	return;
    if (is_base(recursator)) {
	cout << "\nBase case: " << str << endl << *recursator;
    } else {
	recursive_print_checked(recursator.north_west(), string("north west of ") + str, is_base);
	recursive_print_checked(recursator.south_west(), string("south west of ") + str, is_base);
	recursive_print_checked(recursator.north_east(), string("north east of ") + str, is_base);
	recursive_print_checked(recursator.south_east(), string("south east of ") + str, is_base);
    }
}

struct print_functor
{
    template <typename Matrix>
    void operator() (Matrix const& matrix) const
    {
	cout << matrix << endl;
    }
};

template <typename Matrix>
void test_sub_matrix(Matrix& matrix)
{
    using mtl::recursion::for_each; using mtl::recursion::max_dim_test;
    using mtl::mat::recursator; using mtl::transposed_view;

    cout << matrix << endl;

    max_dim_test              is_base(2);
    recursator<Matrix>        rec(matrix);
    recursive_print_checked(rec, "", is_base);

    cout << "\n====================\n"
	 <<   "Same with transposed\n"
	 <<   "====================\n\n";

    transposed_view<Matrix> trans_matrix(matrix);

    cout << trans_matrix;
    recursator< transposed_view<Matrix> > trans_recursator(trans_matrix);
    // print_depth_first(trans_recursator, "");
    recursive_print_checked(trans_recursator, "", is_base);

    cout << "\n=============================\n"
	 <<   "Again with recursive for_each\n"
	 <<   "=============================\n\n";

    for_each(trans_recursator, print_functor(), is_base);
}

template <typename Matrix>
void fill_matrix(Matrix& matrix)
{
    namespace traits = mtl::traits;
    using mtl::begin; using mtl::end;

    typename traits::row<Matrix>::type                                 row(matrix);
    typename traits::col<Matrix>::type                                 col(matrix);
    typename traits::value<Matrix>::type                               value(matrix);
    typedef  glas::tag::nz                                          tag;
    typedef typename traits::range_generator<tag, Matrix>::type        cursor_type;

    double x= 10.3;
    for (cursor_type cursor = begin<tag>(matrix), cend = end<tag>(matrix); cursor != cend; ++cursor) {
	value(*cursor, x);
	x+= 1.0;
    }

}

int main(int, char**)
{
    using namespace mtl;

    cout << "=====================\n"
	 << "Morton-ordered matrix\n"
	 << "=====================\n\n";

    typedef morton_dense<double,  0x55555555> matrix_type;
    matrix_type matrix(6, 5);
    fill_matrix(matrix);
    test_sub_matrix(matrix);

    cout << "\n=========================\n"
	 << "Doppler matrix (4x4 base)\n"
	 << "=========================\n\n";

    typedef morton_dense<double,  0x55555553> dmatrix_type;
    dmatrix_type dmatrix(6, 5);
    fill_matrix(dmatrix);
    test_sub_matrix(dmatrix);

    cout << "\n======================\n"
	 << "Row-major dense matrix\n"
	 << "======================\n\n";

    dense2D<double, mat::parameters<> >   rmatrix(non_fixed::dimensions(6, 5));
    fill_matrix(rmatrix);
    test_sub_matrix(rmatrix);

    cout << "=================================\n"
	 << "Vector-like morton-ordered matrix\n"
	 << "=================================\n\n";

    matrix_type vmatrix(17, 2);
    fill_matrix(vmatrix);
    test_sub_matrix(vmatrix);

    return 0;
}


//! 行列クラス基底ヘッダ
/*! 他行列との演算を除いたメソッドを定義 */
#if !BOOST_PP_IS_ITERATING
	#if !defined(MATRIX_H_) || INCLUDE_MATRIX_LEVEL >= 1
		#define MATRIX_H_
		#include <boost/preprocessor.hpp>
		#include <boost/serialization/access.hpp>
		#include <boost/serialization/level.hpp>
		#include <cstring>
		#include <stdexcept>
		#include <cassert>
		#include "vector.hpp"
		#include "tostring.hpp"
		#include "structure/angle.hpp"
		#include "random/matrix.hpp"

		// 定義する行列の次元(M,N)
		#define SEQ_MATDEF	((2,2))((2,3))((2,4))((4,2))((3,2))((3,3))((3,4))((4,3))((4,4))
		#define LEN_SEQ		BOOST_PP_SEQ_SIZE(SEQ_MATDEF)
		#define LEN_SEQ_M1	BOOST_PP_DEC(LEN_SEQ)

		// 要求された定義レベルを実体化
		#ifndef INCLUDE_MATRIX_LEVEL
			#define INCLUDE_MATRIX_LEVEL 0
		#endif
		#define BOOST_PP_ITERATION_PARAMS_1 (4, (0, LEN_SEQ_M1, "matrix.hpp", INCLUDE_MATRIX_LEVEL))
		#include BOOST_PP_ITERATE()
		#undef INCLUDE_MATRIX_LEVEL
	#endif
#elif BOOST_PP_ITERATION_DEPTH() == 1
	// アラインメントイテレーション
	#define BOOST_PP_ITERATION_PARAMS_2 (3, (0, 1, "matrix.hpp"))
	#include BOOST_PP_ITERATE()
#else
	#define TUP	BOOST_PP_SEQ_ELEM(BOOST_PP_FRAME_ITERATION(1), SEQ_MATDEF)
	#define DIM_M	BOOST_PP_TUPLE_ELEM(0, TUP)
	#define DIM_N	BOOST_PP_TUPLE_ELEM(1, TUP)
	#define ALIGN	BOOST_PP_ITERATION()
	#define ALIGNB	BOOLNIZE(ALIGN)
	#define ALIGN16 BOOST_PP_IF(ALIGN, alignas(16), NOTHING)
	#define MT		MatT<DIM_M, DIM_N, ALIGNB>
	#define DMAX 	BOOST_PP_MAX(DIM_M, DIM_N)
	#define DMIN 	BOOST_PP_MIN(DIM_M, DIM_N)
	#define DMUL	BOOST_PP_MUL(DIM_M, DIM_N)

	#define DIMTUPLE(n,i)				BOOST_PP_TUPLE_ELEM(i, BOOST_PP_SEQ_ELEM(n, SEQ_MATDEF))
	#define DEF_CONV_ITR(z,n,data)		data(DIMTUPLE(n,0), DIMTUPLE(n,1), 0) data(DIMTUPLE(n,0), DIMTUPLE(n,1), 1)

	#define DIM		DIM_N
	#define DEFINE_MATRIX
	#include "local_macro.hpp"

	#if BOOST_PP_FRAME_FLAGS(1) == 0
		namespace spn {
			// row-major
			template <>
			struct ALIGN16 MatT<DIM_M, DIM_N, ALIGNB> : MatBase, boost::equality_comparable<MT> {
				using Row = VecT<DIM_N, ALIGNB>;			//!< 行を表すベクトル型
				using Column = VecT<DIM_M, false>;			//!< 列を表すベクトル型
				using RowE = VecT<4, ALIGNB>;				//!< 4要素の行ベクトル型
				using AMat = MatT<DIM_M, DIM_N, true>;
				using UMat = MatT<DIM_M, DIM_N, false>;
				using UVec2 = VecT<2,false>;
				using UVec3 = VecT<3,false>;

				constexpr static int width = DIM_N,
									height = DIM_M,
								PaddingedSize = BOOST_PP_IF(ALIGN, 4, DIM_N),
								AlignedSize = DIM_M*4-(4-DIM_N),
								UnAlignedSize = DIM_M*DIM_N;
				constexpr static bool align = ALIGNB;
				union {
					//! 容量確保用
					float	data[BOOST_PP_IF(ALIGN, AlignedSize, UnAlignedSize)];
					//! 2次元配列アクセス用
					float	ma[0][PaddingedSize];
				};

				friend class boost::serialization::access;
				template <class Archive>
				void serialize(Archive& ar, const unsigned int /*ver*/) {
					ar & BOOST_SERIALIZATION_NVP(data);
				}

				// -------------------- ctor --------------------
				MatT() = default;
				MatT(const AMat& m);
				MatT(const UMat& m);
				MatT(std::initializer_list<float> il);
				MatT& mul_self();

				#define DIAGONAL2(z,n1,n0)		ma[n0][n1] = BOOST_PP_IF(BOOST_PP_EQUAL(n0,n1), s, 0);
				#define DIAGONAL(z,n,data)		BOOST_PP_REPEAT_##z(DIM_N, DIAGONAL2, n)
				MatT(float s, _TagDiagonal) {
					BOOST_PP_REPEAT(DIM_M, DIAGONAL, NOTHING)
				}
				#define ALLSET(z,n,data)	STORETHIS(n, data);
				MatT(float s, _TagAll) {
					reg128 r = reg_load1_ps(&s);
					BOOST_PP_REPEAT(DIM_M, ALLSET, r)
				}
				MatT(_TagIdentity): MatT(1.f, TagDiagonal) {}

				#define SETARRAY(z,n,src)	STORETHIS(n, reg_loadu_ps(src)); src += DIM_N;
				MatT(const float* src) {
					BOOST_PP_REPEAT(DIM_M, SETARRAY, src)
				}
				template <int N>
				float& ref1D() {
					return ma[N/DIM_N][N%DIM_N];
				}

				#define DEF_ARGS(z,n,data)	(float BOOST_PP_CAT(data,n))
				#define SET_ARGS0(z,n,data)	ref1D<n>() = BOOST_PP_CAT(data,n);
				//! 全てを指定
				MatT(BOOST_PP_SEQ_ENUM(BOOST_PP_REPEAT(DMUL, DEF_ARGS, f))) {
					BOOST_PP_REPEAT(DMUL, SET_ARGS0, f)
				}
				#define SET_ARGS1(z,n,data)	STORETHIS(n, reg_mul_ps(xmm_const::matI()[n], reg_load1_ps(&BOOST_PP_CAT(data,n))));
				#define SET_ARGS2(z,n,dummy) STORETHIS(n, reg_setzero_ps());
				//! 対角線上
				MatT(BOOST_PP_SEQ_ENUM(BOOST_PP_REPEAT(DMIN, DEF_ARGS, f))) {
					BOOST_PP_REPEAT(DMIN, SET_ARGS1, f)
					BOOST_PP_REPEAT_FROM_TO(DMIN, DIM_M, SET_ARGS2, NOTHING)
				}
				#undef SET_ARGS2
				template <bool A>
				bool operator == (const MatT<DIM_M, DIM_N, A>& m) const;

				// -------------------- query values --------------------
				#if ALIGN==1 && BOOST_PP_LESS(DIM_N,4)==1
					template <class T>
					T& getAuxRef(int m, int n) {
						return *reinterpret_cast<T*>(ma[m]+DIM_N+n);
					}
					template <class T>
					const T& getAuxRef(int m, int n) const {
						return *reinterpret_cast<const T*>(ma[m]+DIM_N+n);
					}
				#endif

				const Row& getRow(int n) const {
					if(n >= height)
						return *reinterpret_cast<const Row*>(xmm_const::cs_matI()[n]);
					return *reinterpret_cast<const Row*>(ma[n]);
				}
				Row& getRow(int n) {
					return *reinterpret_cast<Row*>(ma[n]);
				}
				//! 行を取得(最大4まで)
				/*! 足りない分は対角要素のみ1それ以外は0とみなして取得 */
				#if ALIGN==0 || DIM_N==4
					// 普通のgetRowと同義
					const Row& getRowE(int n) const {
						return getRow(n); }
				#else
					RowE getRowE(int n) const {
						if(n >= height)
							return RowE(xmm_const::matI()[n]);
						return RowE(reg_or_ps(reg_and_ps(LOADTHIS(n), xmm_const::mask()[n]), xmm_const::matI()[n]));
					}
				#endif
				template <bool A>
				void setRow(int n, const VecT<DIM_N,A>& v) {
					getRow(n) = v.loadPS();
				}
				void setColumn(int n, const Column& v) {
					for(int i=0 ; i<DIM_M ; i++)
						ma[i][n] = v.m[i];
				}
				#define SET_COLUMN(z,n,data)	(ma[n][data])
				Column getColumn(int n) const {
					if(n >= DIM_N)
						return Column(xmm_const::matI()[n]);
					return Column(BOOST_PP_SEQ_ENUM(BOOST_PP_REPEAT(DIM_M, SET_COLUMN, n)));
				}
				#undef SET_COLUMN

				#define DEF_GETROW(z,n,data)	BOOST_PP_CAT(const Row& getRow, n)() const { return *reinterpret_cast<const Row*>(ma[n]); }
				//! マクロからのアクセス用: 行毎に個別のメンバ関数を用意
				/*! マクロのColumn取得は多分、不要 */
				BOOST_PP_REPEAT(DIM_M, DEF_GETROW, NOTHING)
				#undef DEF_GETROW

				// -------------------- operators --------------------
				#define FUNC(z,n,func)		STORETHIS(n, func(LOADTHIS(n), r0));
				#define FUNC2(z,n,func)		STORETHISPS(ret.ma[n], func(LOADTHIS(n), r0));
				#define DEF_OP(op, func) MatT& operator BOOST_PP_CAT(op,=) (float s) { \
					reg128 r0 = reg_load1_ps(&s); \
					BOOST_PP_REPEAT(DIM_M, FUNC, func) \
					return *this; } \
					MatT operator op (float s) const { \
						MatT ret; \
						reg128 r0 = reg_load1_ps(&s); \
						BOOST_PP_REPEAT(DIM_M, FUNC2, func) \
						return ret; } \
					friend MatT operator op (float s, const MatT& m);

				DEF_OP(+, reg_add_ps)
				DEF_OP(-, reg_sub_ps)
				DEF_OP(*, reg_mul_ps)
				DEF_OP(/, _mmDivPs)
				#undef DEF_OP
				#undef FUNC
				#undef FUNC2

				#define FUNC3(z,n,func)		STORETHIS(n, func(LOADTHIS(n), LOADTHISPS(m.ma[n])));
				#define FUNC4(z,n,func)		STORETHISPS(ret.ma[n], func(LOADTHIS(n), LOADTHISPS(m.ma[n])));
				#define DEF_OPM(op, func) MatT& operator BOOST_PP_CAT(op,=) (const MatT& m) { \
					BOOST_PP_REPEAT(DIM_M, FUNC3, func) \
					return *this; } \
				MatT operator op (const MatT& m) const { \
					MatT ret; \
					BOOST_PP_REPEAT(DIM_M, FUNC4, func) \
					return ret; }

				DEF_OPM(+, reg_add_ps)
				DEF_OPM(-, reg_sub_ps)
				#undef FUNC3
				#undef FUNC4

				// -------------------- others --------------------
				// 行列拡張Get = Mat::getRowE()
				void identity();
				static MatT Scaling(BOOST_PP_SEQ_ENUM(BOOST_PP_REPEAT(DMIN, DEF_ARGS, f)));
				#if (DMIN == 2 || DMIN == 3) && DIM_M == 3
					//! 2次元移動
					static MatT Translation(const UVec2& v);
				#endif
				#if DIM_M == 4 && DIM_N >= 3
					//! 3次元移動
					static MatT Translation(const UVec3& v);
					static MatT LookAtLH(const UVec3& pos, const UVec3& at, const UVec3& up);
					static MatT LookDirLH(const UVec3& pos, const UVec3& dir, const UVec3& up);
					static MatT LookAtRH(const UVec3& pos, const UVec3& at, const UVec3& up);
					static MatT LookDirRH(const UVec3& pos, const UVec3& dir, const UVec3& up);
				#endif
				#if DMIN == 2
					//! 2D回転
					static MatT Rotation(RadF ang);
				#elif DMIN >= 3
						//! X軸周りの回転
						static MatT RotationX(RadF ang);
						//! Y軸周りの回転
						static MatT RotationY(RadF ang);
						//! Z軸周りの回転
						static MatT RotationZ(RadF ang);
						//! 任意軸周りの回転
						static MatT RotationAxis(const UVec3& axis, RadF ang);
					#if DMIN == 4
						//! 透視変換行列
						static MatT PerspectiveFovLH(RadF fov, float aspect, float nz, float fz);
						static MatT PerspectiveFovRH(RadF fov, float aspect, float nz, float fz);
						static MatT _PerspectiveFov(RadF fov, float aspect, float nz, float fz, float coeff);
					#endif
				#endif

				// 正方行列限定のルーチン
				#if DIM_M==DIM_N
					void transpose();
					//! 固有値計算
					float calcDeterminant() const;
					// TODO: 固有ベクトル計算
					//! 逆行列計算
					bool inversion(MatT& dst) const;
					bool inversion(MatT& dst, float det) const;
					bool invert();
					// -------- Luaへのエクスポート用 --------
					MatT addF(float s) const;
					MatT addM(const MatT& m) const;
					MatT subF(float s) const;
					MatT subM(const MatT& m) const;
					VecT<DIM_N,align> mulV(const VecT<DIM_N,align>& v) const;
					MatT mulF(float s) const;
					MatT mulM(const MatT& m) const;
					MatT divF(float s) const;
					MatT luaInvert() const;
				#endif
				std::string toString() const;
				MatT<DIM_N,DIM_M,ALIGNB> transposition() const;

				#if DMIN > 2
					//! 指定した行と列を省いた物を出力
					MatT<height-1,width-1,ALIGNB> cutRC(int row, int clm) const;
				#endif
				// ---------- < 行の基本操作 > ----------
				//! 行を入れ替える
				void rowSwap(int r0, int r1);
				//! ある行を定数倍する
				void rowMul(int r0, float s);
				//! ある行を定数倍した物を別の行へ足す
				void rowMulAdd(int r0, float s, int r1);
				// ---------- < 列の基本操作 > ----------
				//! 列を入れ替える
				void clmSwap(int c0, int c1);
				//! ある列を定数倍する
				void clmMul(int c0, float s);
				//! ある列を定数倍した物を別の行へ足す
				void clmMulAdd(int c0, float s, int c1);

				//! ある行の要素が全てゼロか判定 (誤差=EPSILON込み)
				bool isZeroRowEps(int n) const;
				bool isZeroRow(int n) const;
				//! 零行列か判定
				bool isZero() const;
				//! 各行を正規化する (最大の係数が1になるように)
				void rowNormalize();
				//! 被約形かどうか判定
				bool isEchelon() const;
				//! 被約形にする
				/*! \return 0の行数 */
				int rowReduce();

				template <class RDF>
				static MatT Random(const RDF& rdf, const RangeF& r=random::DefaultRMatRange) {
					return random::GenRMat<MatT>(rdf, r);
				}

				// TODO: DecompAffine [DIM >= 3]

				// 本来行列の積算が出来るのはDIM_N == 相手のDIM_Mの時だけだが
				// 足りない分をgetRowE()で補う事で可能にする
				// 演算結果も本当は(DIM_M, Other::DIM_N)となるが
				// convert<Tgt::DIM_M, Tgt::DIM_N>(result) とする事で合わせる
				// max(DIM_N, Other::DIM_M)
				// 演算子の出力自体はサイズそのまま
				// Mat34 a;
				// Mat44 b;
				// a * b -> 3x4
				// a * [3x4](4x4) -> 3x4
				// TODO: 入力が出力と同じサイズで、なおかつrvalueだったら&&で受け取ってそのアドレスを返す

				//! 行列サイズを変更
				/*! 縮小の場合は要素を削り
					拡大の際に足りない成分は対角=1, 他は0とする */
				// デバッグ時速度を考えるとテンプレートで実装できない(？)のでマクロ使用
				// SEQ_MATDEFの組み合わせを生成 + (Aligned | UnAligned)
				#define CONVFUNC(n0,n1,align)	BOOST_PP_CAT(BOOST_PP_CAT(BOOST_PP_CAT(convert,AFLAG(align)), n0),n1)
				#define DEF_CONV(n0,n1,align)	MatT<n0,n1,BOOLNIZE(align)> CONVFUNC(n0,n1,align)() const; \
												void convert(MatT<n0,n1,BOOLNIZE(align)>& dst) const;
				BOOST_PP_REPEAT(LEN_SEQ, DEF_CONV_ITR, DEF_CONV)
				#undef DEF_CONV
				#undef CONVFUNC
				// サイズの合わない行列(DIM_N != n0)の演算関数は定義しない
				//! 行列との積算 (3 operands)
				#define DEF_MUL0(n0,n1,align) \
					BOOST_PP_IF( \
						BOOST_PP_EQUAL(DIM_N, n0), \
						DEF_MUL, \
						NOTHING_ARG \
					)(n0,n1,align)
				#define DEF_MUL(n0,n1,align)	MatT<DIM_M, n1, ALIGNB> operator * (const MatT<n0,n1,BOOLNIZE(align)>& m) const;
				BOOST_PP_REPEAT(LEN_SEQ, DEF_CONV_ITR, DEF_MUL0)
				#undef DEF_MUL
				#undef DEF_MUL0
				//! 行列との積算 (2 operands)
				#define DEF_MULE0(n0,n1,align) \
					BOOST_PP_IF( \
						BOOST_PP_AND( \
							BOOST_PP_EQUAL(DIM_N, n0), \
							BOOST_PP_EQUAL(n1, DIM_N) \
						), \
						DEF_MULE, \
						NOTHING_ARG \
					)(n0,n1,align)
				#define DEF_MULE(n0,n1,align)	MatT& operator *= (const MatT<n0,n1,BOOLNIZE(align)>& m);
				BOOST_PP_REPEAT(LEN_SEQ, DEF_CONV_ITR, DEF_MULE0)
				#undef DEF_MULE
				#undef DEF_MULE0
				//! 行列の代入
				MT& operator = (const MT& m);
				MT& operator = (std::initializer_list<float> il);

				//! 行列との積算 (右から掛ける)
				/*! 列ベクトルとして扱う = ベクトルを転置して左から行ベクトルを掛ける */
				template <bool A>
				VecT<DIM_N,ALIGNB> operator * (const VecT<DIM_N,A>& v) const;

				friend std::ostream& operator << (std::ostream& os, const MT& m);
			};
			// 使いやすいように別名を定義 ex. MatT<2,2,true> = AMat22
			using BOOST_PP_CAT(
					BOOST_PP_CAT(
						BOOST_PP_CAT(AFLAG(ALIGN),Mat)
						,DIM_M)
					,DIM_N)
					= MatT<DIM_M,DIM_N,ALIGNB>;
		}
	BOOST_CLASS_IMPLEMENTATION(spn::MT, object_serializable)
	#elif BOOST_PP_FRAME_FLAGS(1) == 1
		namespace spn {
			#define FUNC_OP(z,n,func)	STORETHISPS(ret.ma[n], func(r, LOADTHISPS(m.ma[n])));
			#define DEF_OPERATOR_FUNC(op, func) MT operator op (float s, const MT& m) { \
				MT ret; \
				reg128 r = reg_load1_ps(&s); \
				BOOST_PP_REPEAT(DIM_M, FUNC_OP, func) \
				return ret; \
			}
			DEF_OPERATOR_FUNC(+, reg_add_ps)
			DEF_OPERATOR_FUNC(-, reg_sub_ps)
			DEF_OPERATOR_FUNC(*, reg_mul_ps)
			DEF_OPERATOR_FUNC(/, _mmDivPs)
			#undef FUNC_OP
			#undef DEF_OPERATOR_FUNC

			MT::MatT(const AMat& m) {
				for(int i=0 ; i<DIM_M ; i++)
					STORETHIS(i, LOADPS(m.ma[i]));
			}
			MT::MatT(const UMat& m) {
				for(int i=0 ; i<DIM_M ; i++)
					STORETHIS(i, LOADPSU(m.ma[i]));
			}
			MT::MatT(std::initializer_list<float> il) {
				// 足りない要素分はすべてゼロにする
				alignas(16) float tmp[DIM_M*DIM_N] = {};
				auto* pTmp = tmp;
				for(auto itr=il.begin() ; itr!=il.end() ; ++itr)
					*pTmp++ = *itr;
				for(int i=0 ; i<DIM_M ; i++)
					STORETHIS(i, LOADPSU(tmp+i*DIM_N));
			}
			// 行列拡張Get = Mat::getRowE()
			void MT::identity() {
				*this = MatT(1.0f, TagDiagonal);
			}
			std::string MT::toString() const {
				return ToString(*this);
			}
			std::ostream& operator << (std::ostream& os, const MT& m) {
				os << BOOST_PP_IF(ALIGN, 'A', ' ') << "Mat" << DIM_M << DIM_N << '[';
				for(int i=0 ; i<DIM_M ; i++) {
					for(int j=0 ; j<DIM_N-1 ; j++)
						os << m.ma[i][j] << ',';
					os << m.ma[i][DIM_N-1] << ' ';
				}
				return os << ']';
			}
			template <bool A>
			bool MT::operator == (const MatT<DIM_M, DIM_N, A>& m) const {
				for(int i=0 ; i<DIM_M ; i++) {
					if(getRow(i) != Row(m.getRow(i)))
						return false;
				}
				return true;
			}
			template bool MT::operator == (const MatT<DIM_M, DIM_N, false>&) const;
			template bool MT::operator == (const MatT<DIM_M, DIM_N, true>&) const;

			#define DEF_COPY(z,n,dummy)	STORETHISPS(ma[n], LOADTHISPS(m.ma[n]));
			MT& MT::operator = (const MT& m) {
				BOOST_PP_REPEAT(DIM_M, DEF_COPY, NOTHING)
				return *this;
			}
			#undef DEF_COPY
			MT& MT::operator = (std::initializer_list<float> il) {
				return *this = MT(il);
			}
			#define SET_ARGS2(z,n,data) (BOOST_PP_CAT(data, n))
			MT MT::Scaling(BOOST_PP_SEQ_ENUM(BOOST_PP_REPEAT(DMIN, DEF_ARGS, f))) {
				return MatT(BOOST_PP_SEQ_ENUM(BOOST_PP_REPEAT(DMIN, SET_ARGS2, f)));
			}
			#undef SET_ARGS2
			#if (DMIN == 2 || DMIN == 3) && DIM_M == 3
				MT MT::Translation(const VecT<2,false>& v) {
					MatT mt(TagIdentity);
					mt.ma[2][0] = v.x;
					mt.ma[2][1] = v.y;
					return mt;
				}
			#endif
			#if DIM_M == 4 && DIM_N >= 3
				MT MT::Translation(const UVec3& v) {
					MatT mt(TagIdentity);
					mt.ma[3][0] = v.x;
					mt.ma[3][1] = v.y;
					mt.ma[3][2] = v.z;
					return mt;
				}
				MT MT::LookAtLH(const UVec3& pos, const UVec3& at, const UVec3& up) {
					return LookDirLH(pos, (at-pos).normalization(), up);
				}
				MT MT::LookDirLH(const UVec3& pos, const UVec3& dir, const UVec3& up) {
					AVec3 xA(up % dir);
					xA.normalize();

					MatT ret;
					STORETHISPS(ret.ma[0], reg_setr_ps(xA.x, up.x, dir.x, 0));
					STORETHISPS(ret.ma[1], reg_setr_ps(xA.y, up.y, dir.y, 0));
					STORETHISPS(ret.ma[2], reg_setr_ps(xA.z, up.z, dir.z, 0));
					STORETHISPS(ret.ma[3], reg_setr_ps(-pos.dot(xA), -pos.dot(up), -pos.dot(dir), 1));
					return ret;
				}
				MT MT::LookAtRH(const UVec3& pos, const UVec3& at, const UVec3& up) {
					return LookDirLH(pos, (pos-at).normalization(), up);
				}
				MT MT::LookDirRH(const UVec3& pos, const UVec3& dir, const UVec3& up) {
					return LookDirLH(pos, -dir, up);
				}
			#endif
			#if DMIN == 2
				MT MT::Rotation(RadF ang) {
					const float S = std::sin(ang.get()),
								C = std::cos(ang.get());
					MatT mt(TagIdentity);
					mt.ma[0][0] = C;
					mt.ma[0][1] = S;
					mt.ma[1][0] = -S;
					mt.ma[1][1] = C;
					return mt;
				}
			#elif DMIN >= 3
					MT MT::RotationX(RadF ang) {
						const float C = std::cos(ang.get()),
									S = std::sin(ang.get());
						MatT mt;
						STORETHISPS(mt.ma[0], reg_setr_ps(1,	0,	0,	0));
						STORETHISPS(mt.ma[1], reg_setr_ps(0,	C,	S,	0));
						STORETHISPS(mt.ma[2], reg_setr_ps(0,	-S,	C,	0));
						#if DIM_M == 4
							STORETHISPS(mt.ma[3], reg_setr_ps(0,0,0,1));
						#endif
						return mt;
					}
					MT MT::RotationY(RadF ang) {
						const float C = std::cos(ang.get()),
									S = std::sin(ang.get());
						MatT mt;
						STORETHISPS(mt.ma[0], reg_setr_ps(C,	0,	-S,	0));
						STORETHISPS(mt.ma[1], reg_setr_ps(0,	1,	0,	0));
						STORETHISPS(mt.ma[2], reg_setr_ps(S,	0,	C,	0));
						#if DIM_M == 4
							STORETHISPS(mt.ma[3], reg_setr_ps(0,0,0,1));
						#endif
						return mt;
					}
					MT MT::RotationZ(RadF ang) {
						const float C = std::cos(ang.get()),
									S = std::sin(ang.get());
						MatT mt;
						STORETHISPS(mt.ma[0], reg_setr_ps(C,	S,	0,	0));
						STORETHISPS(mt.ma[1], reg_setr_ps(-S,	C,	0,	0));
						STORETHISPS(mt.ma[2], reg_setr_ps(0,	0,	1,	0));
						#if DIM_M == 4
							STORETHISPS(mt.ma[3], reg_setr_ps(0,0,0,1));
						#endif
						return mt;
					}
					MT MT::RotationAxis(const UVec3& axis, RadF ang) {
						const float C = std::cos(ang.get()),
									S = std::sin(ang.get()),
									RC = 1-C;
						MatT mt;
						const float M00 = C + Square(axis.x) * RC,
									M01 = axis.x * axis.y * RC + axis.z*S,
									M02 = axis.x * axis.z * RC - axis.y*S,
									M10 = axis.x * axis.y * RC - axis.z*S,
									M11 = C + Square(axis.y) * RC,
									M12 = axis.y * axis.z * RC + axis.x*S,
									M20 = axis.x * axis.z * RC + axis.y*S,
									M21 = axis.y * axis.z * RC - axis.x*S,
									M22 = C + Square(axis.z) * RC;
						STORETHISPS(mt.ma[0], reg_setr_ps(M00, M01, M02, 0));
						STORETHISPS(mt.ma[1], reg_setr_ps(M10, M11, M12, 0));
						STORETHISPS(mt.ma[2], reg_setr_ps(M20, M21, M22, 0));
						#if DIM_M == 4
							STORETHISPS(mt.ma[3], reg_setr_ps(0,0,0,1));
						#endif
						return mt;
					}
				#if DMIN == 4
					MT MT::_PerspectiveFov(RadF fov, float aspect, float nz, float fz, float coeff) {
						float h = 1.0f / std::tan(fov.get()/2),
								w = h / aspect,
								f0 = fz/(fz-nz),
								f1 = -nz*fz/(fz-nz);
						MatT ret;
						STORETHISPS(ret.ma[0], reg_setr_ps(w,0,0,0));
						STORETHISPS(ret.ma[1], reg_setr_ps(0,h,0,0));
						STORETHISPS(ret.ma[2], reg_setr_ps(0,0,f0,coeff));
						STORETHISPS(ret.ma[3], reg_setr_ps(0,0,f1*coeff,0));
						return ret;
					}
					MT MT::PerspectiveFovLH(RadF fov, float aspect, float nz, float fz) {
						return _PerspectiveFov(fov, aspect, nz, fz, 1);
					}
					MT MT::PerspectiveFovRH(RadF fov, float aspect, float nz, float fz) {
						return _PerspectiveFov(fov, aspect, nz, fz, -1);
					}
				#endif
			#endif
			bool MT::isEchelon() const {
				uint32_t clmFlagT = 0,
						clmFlagF = 0;
				int topClm = -1;
				bool bZero = false;
				for(int i=0 ; i<DIM_M ; i++) {
					int lcltop = -1;
					for(int j=0 ; j<DIM_N-1 ; j++) {
						float val = ma[i][j];
						if(std::fabs(val) >= FLOAT_EPSILON) {
							if(lcltop >= 0)
								clmFlagF |= 1<<j;
							else {
								lcltop = j;
								// 0でない成分を持つ全ての行について，先導成分が1である
								// 行の下に行くにつれて先導成分が右へずれていく
								if(topClm >= j || !(val-1.f >= FLOAT_EPSILON))
									return false;
								clmFlagT |= 1<<j;
							}
						}
					}
					if(lcltop < 0)
						bZero = true;
					else {
						// 0でない成分を持つすべての行は，0だけの行よりも先に来る
						if(bZero)
							return false;
						topClm = lcltop;
					}
				}
				// ある行の先導成分が第j列ならば，他の行の第j列は0である
				return !(clmFlagT & clmFlagF);
			}
			void MT::rowSwap(int r0, int r1) {
				reg128 xm0 = LOADTHIS(r0),
						xm1 = LOADTHIS(r1);
				STORETHIS(r1, xm0);
				STORETHIS(r0, xm1);
			}
			void MT::rowMul(int r0, float s) {
				STORETHIS(r0, reg_mul_ps(LOADTHIS(r0), reg_load1_ps(&s)));
			}
			void MT::rowMulAdd(int r0, float s, int r1) {
				reg128 xm0 = reg_mul_ps(LOADTHIS(r0), reg_load1_ps(&s)),
						xm1 = LOADTHIS(r1);
				STORETHIS(r1, reg_add_ps(xm0, xm1));
			}
			void MT::clmSwap(int c0, int c1) {
				setColumn(c1, getColumn(c0));
			}
			void MT::clmMul(int c0, float s) {
				auto clm = getColumn(c0);
				clm *= s;
				setColumn(c0, clm);
			}
			void MT::clmMulAdd(int c0, float s, int c1) {
				Column clm0 = getColumn(c0),
					clm1 = getColumn(c1);
				clm0 *= s;
				clm0 += clm1;
				setColumn(c1, clm0);
			}
			bool MT::isZeroRow(int n) const {
				return _mmIsZero(LOADTHISZ(n));
			}
			bool MT::isZeroRowEps(int n) const {
				return _mmIsZeroEps(LOADTHISZ(n));
			}
			bool MT::isZero() const {
				reg128 accum = reg_setzero_ps();
				#define ACCUM_ABS(dummy,n,dummy2)	reg_add_ps(accum, _mmAbsPs(LOADTHIS(n)));
				BOOST_PP_REPEAT(DIM_M, ACCUM_ABS, NOTHING)
				#undef ACCUM_ABS
				accum = reg_and_ps(accum, xmm_const::mask()[DIM_N]);
				return reg_movemask_ps(accum) == 0;
			}
			void MT::rowNormalize() {
				reg128 xm0 = LOADTHIS(0);
				for(int i=1 ; i<height ; i++)
					xm0 = reg_max_ps(xm0, LOADTHIS(i));
				RCP22BIT(xm0)
				for(int i=0 ; i<height ; i++)
					STORETHIS(i, reg_mul_ps(LOADTHIS(i), xm0));
			}
			int MT::rowReduce() {
				// rowNormalize();
				int rbase = 0,
					cbase = 0;
				for(;;) {
					// 行の先端が0でなく、かつ絶対値が最大の行を探す
					int idx = -1;
					float absmax = 0;
					for(int i=rbase ; i<DIM_M ; i++) {
						float v = std::fabs(ma[i][cbase]);
						if(absmax < v) {
							if(std::fabs(v) >= FLOAT_EPSILON) {
								absmax = v;
								idx = i;
							}
						}
					}
					if(idx < 0) {
						// 無かったので次の列へ
						++cbase;
						if(cbase == DIM_N) {
							// 終了
							break;
						}
						continue;
					}

					// 基準行でなければ入れ替え
					if(idx > rbase)
						rowSwap(idx, rbase);
					// 基点で割って1にする
					rowMul(rbase, spn::Rcp22Bit(ma[rbase][cbase]));
					ma[rbase][cbase] = 1;	// 精度の問題で丁度1にならない事があるので強制的に1をセット
					// 他の行の同じ列を0にする
					for(int i=0 ; i<DIM_M ; i++) {
						if(i==rbase)
							continue;

						float scale = -ma[i][cbase];
						rowMulAdd(rbase, scale, i);
						ma[i][cbase] = 0;	// 上と同じく精度の問題により0をセット
					}
					// 次の行,列へ移る
					++rbase;
					++cbase;
					if(rbase == DIM_M || cbase == DIM_N) {
						// 最後の行まで処理し終わるか，全て0の行しか無ければ終了
						break;
					}
				}
				return DIM_M - rbase;
			}
			#if DIM_M==DIM_N
				void MT::transpose() {
					*this = transposition();
				}
				float MT::calcDeterminant() const {
					#if DMIN == 2
						// 公式で計算
						return ma[0][0]*ma[1][1] - ma[0][1]*ma[1][0];
					#else
						float res = 0,
								s = 1;
						// 部分行列を使って計算
						for(int i=0 ; i<DIM_M ; i++) {
							auto mt = cutRC(0,i);
							res += ma[0][i] * mt.calcDeterminant() * s;
							s *= -1;
						}
						return res;
					#endif
				}
				bool MT::inversion(MatT& dst) const {
					assert(this != &dst);
					return inversion(dst, calcDeterminant());
				}
				bool MT::inversion(MatT& dst, float det) const {
					if(std::fabs(det) < FLOAT_EPSILON)
						return false;

					det = spn::Rcp22Bit(det);
					#if DIM_M==2
						dst.ma[0][0] = ma[1][1] * det;
						dst.ma[0][1] = -ma[1][0] * det;
						dst.ma[1][0] = -ma[0][1] * det;
						dst.ma[1][1] = ma[0][0] * det;
					#else
						const float c_val[2] = {1,-1};
						for(int i=0 ; i<DIM_M ; i++) {
							for(int j=0 ; j<DIM_N ; j++) {
								auto in_mat = cutRC(i,j);
								float in_det = in_mat.calcDeterminant();
								dst.ma[j][i] = c_val[(i+j)&1] * in_det * det;
							}
						}
					#endif
					return true;
				}
				bool MT::invert() {
					MT tmp(*this);
					bool b = inversion(tmp);
					if(b)
						*this = tmp;
					return b;
				}
				// -------- Luaへのエクスポート用 --------
				MT MT::addF(float s) const {
					return *this + s;
				}
				MT MT::addM(const MT& m) const {
					return *this * m;
				}
				MT MT::subF(float s) const {
					return *this - s;
				}
				MT MT::subM(const MT& m) const {
					return *this - m;
				}
				MT MT::mulF(float s) const {
					return *this * s;
				}
				MT MT::mulM(const MT& m) const {
					return *this * m;
				}
				MT MT::divF(float s) const {
					return *this / s;
				}
				MT MT::luaInvert() const {
					MT ret;
					inversion(ret);
					return ret;
				}
			#endif
			#if DMIN > 2
				//! 指定した行と列を省いた物を出力
				MatT<DIM_M-1,DIM_N-1,ALIGNB> MT::cutRC(int row, int clm) const {
					// TODO: 余力があったらマクロ展開
					MatT<height-1,width-1,ALIGNB> ret;
					// 左上
					for(int i=0 ; i<row ; i++) {
						for(int j=0 ; j<clm ; j++)
							ret.ma[i][j] = ma[i][j];
					}
					// 右上
					for(int i=0 ; i<row ; i++) {
						for(int j=clm+1 ; j<width ; j++)
							ret.ma[i][j-1] = ma[i][j];
					}
					// 左下
					for(int i=row+1 ; i<height ; i++) {
						for(int j=0 ; j<clm ; j++)
							ret.ma[i-1][j] = ma[i][j];
					}
					// 右下
					for(int i=row+1 ; i<height ; i++) {
						for(int j=clm+1 ; j<width ; j++)
							ret.ma[i-1][j-1] = ma[i][j];
					}
					return ret;
				}
			#endif
			MatT<DIM_N,DIM_M,ALIGNB> MT::transposition() const {
				MatT<DIM_N,DIM_M,ALIGNB> ret;
				#if DMIN == 2
					// DMIN == 2なら直接入れ替えたほうが速い
					ret.ma[0][0] = ma[0][0];
					ret.ma[0][1] = ma[1][0];
					ret.ma[1][0] = ma[0][1];
					ret.ma[1][1] = ma[1][1];
				#else
					// DMIN >= 3なら4x4行列として処理
					// M*4行列 = レジスタM本
					reg128	xm[4], xmt[4];
					#define LOADROWE(n) BOOST_PP_IF(BOOST_PP_GREATER_EQUAL(n, DIM_M), xmm_const::matI()[n], LOADTHIS(n))
					#define BOOST_PP_LOCAL_MACRO(n)	xm[n] = LOADROWE(n);
					#define BOOST_PP_LOCAL_LIMITS (0,BOOST_PP_SUB(DMAX, 1))
					#include BOOST_PP_LOCAL_ITERATE()

					xmt[0] = reg_unpacklo_ps(xm[0], xm[2]);
					xmt[1] = reg_unpacklo_ps(xm[1], xm[3]);
					xmt[2] = reg_unpackhi_ps(xm[0], xm[2]);
					xmt[3] = reg_unpackhi_ps(xm[1], xm[3]);

					xm[0] = reg_unpacklo_ps(xmt[0], xmt[1]);
					xm[1] = reg_unpackhi_ps(xmt[0], xmt[1]);
					xm[2] = reg_unpacklo_ps(xmt[2], xmt[3]);
					xm[3] = reg_unpackhi_ps(xmt[2], xmt[3]);
					#define BOOST_PP_LOCAL_MACRO(n) STORETHISPS(ret.ma[n], xm[n]);
					#define BOOST_PP_LOCAL_LIMITS (0,BOOST_PP_SUB(DIM_N,1))
					#include BOOST_PP_LOCAL_ITERATE()
					#undef LOADROWE
				#endif
				return ret;
			}
			/*	Pseudo-code:
				MatT<n0,n1,align> MT::convert##align##n0##n1() const {
					MatT<n0,n1,align> ret;
					// <Repeat by ITR_COPY>
					for(int i=0 ; i<n0 ; i++) {
						if(i < DIM_M)
							STOREPS_(A)##n1(ret.ma[i], LOADTHIS(i));
						else
							STOREPS_(A)##n1(ret.ma[i], xmm_const::matI()[i]);
					}
					return ret;
				}
				void MT::convert(MatT<n0,n1,align>& dst) const {
					 dst = convert##align##n0##n1();
				}
			*/
			#define ITR_COPY_I(n,dim,align)	\
				BOOST_PP_CAT( \
					BOOST_PP_CAT( \
						STOREPS_, \
						AFLAG(align) \
					), \
					dim \
				) \
				(ret.ma[n], \
					BOOST_PP_IF( \
						BOOST_PP_LESS(n,DIM_M), \
						LOADTHISI(n), \
						xmm_const::matI()[n] \
					) \
				);
			#define ITR_COPY(z,n,data)		ITR_COPY_I(n, BOOST_PP_SEQ_ELEM(0,data), BOOST_PP_SEQ_ELEM(1,data))
			#define DEF_CONV(n0,n1,align)	\
				MatT<n0,n1,BOOLNIZE(align)> MT::BOOST_PP_CAT( \
					BOOST_PP_CAT( \
						BOOST_PP_CAT(convert, AFLAG(align)), \
						n0 \
					), \
					n1)() const { \
				MatT<n0,n1,BOOLNIZE(align)> ret; \
				BOOST_PP_REPEAT(n0, ITR_COPY, (n1)(align)) \
				return ret; } \
				void MT::convert(MatT<n0,n1,BOOLNIZE(align)>& dst) const { \
					dst = BOOST_PP_CAT( \
							BOOST_PP_CAT( \
								BOOST_PP_CAT(convert, AFLAG(align)), \
								n0 \
							), \
							n1 \
						)(); }
			BOOST_PP_REPEAT(LEN_SEQ, DEF_CONV_ITR, DEF_CONV)
			#undef ITR_COPY_I
			#undef ITR_COPY
		}
		#elif BOOST_PP_FRAME_FLAGS(1) == 2
		namespace spn {
			#define DEF_MULSELF *this = *this * *this;
			MT& MT::mul_self() {
				BOOST_PP_IF(
					BOOST_PP_EQUAL(DIM_M, DIM_N),
					DEF_MULSELF,
					NOTHING
				)
				return *this;
			}
			#undef DEF_MULSELF
			// 行列の積算
			/*	Pseudo-code:
				MatT<DIM_M, n1, ALIGNB> MT::operator * (const MatT<n0,n1,align>& m) const {
					ALIGN16 MatT<DIM_M, n1, ALIGNB> ret;
					// <Repeat by MUL_OUTER>
					for(int i=0 ; i<n0 ; i++) {
						reg128 tm = LOADTHISI(i);
						reg128 accum = reg_mul_ps(reg_shuffle_ps(tm, tm, _REG_SHUFFLE(0,0,0,0)), LOADPS_I(A)n1(m.ma[0], 0));
						// <Repeat by MUL_INNER>
						for(int j=1 ; j<DIM_N ; j++)
							accum = reg_add_ps(accum, reg_mul_ps(reg_shuffle_ps(tm, tm, _REG_SHUFFLE(j,j,j,j)), LOADPS_I(A)n1(m.ma[j], j)));
						STOREPS_(A)n1(ret.ma[i], accum);
					}
					return ret;
				}
			*/
			// 対応する行列(サイズ)以外は何もコードを出力しない
			#define DEF_MUL0(n0,n1,align) \
				BOOST_PP_IF( \
					BOOST_PP_EQUAL(DIM_N, n0), \
					DEF_MUL, \
					NOTHING_ARG \
				)(n0,n1,align)
			#define DEF_MUL(n0,n1,align)	MatT<DIM_M, n1, ALIGNB> MT::operator * (const MatT<n0,n1,BOOLNIZE(align)>& m) const {\
				ALIGN16 MatT<DIM_M, n1, ALIGNB> ret; \
				BOOST_PP_REPEAT( \
					DIM_M, \
					MUL_OUTER, \
					(BOOST_PP_CAT( \
						BOOST_PP_IF( \
							align, \
							A, \
							NOTHING \
						), \
						n1 \
					))(n0) \
				) \
				return ret; \
			}
			/*	Inner(z,n, AUn1)
				accum = reg_add_ps(accum, reg_mul_ps(reg_shuffle_ps(tm, tm, _REG_SHUFFLE(n,n,n,n)), LOADPS_I(A)n1(m.ma[n], n)));
			*/
			#define MUL_INNER(z,n,AUn1)	accum = reg_add_ps( \
													accum, \
													reg_mul_ps( \
														reg_shuffle_ps(tm, tm, _REG_SHUFFLE(n,n,n,n)), \
														LOADPS_I##AUn1(m.ma[n], n) \
													) \
												);
			/*	Outer(z,i, AUn1_n0)
				reg128 tm = LOADTHISI(i);
				reg128 accum = reg_mul_ps(reg_shuffle_ps(tm, tm, _REG_SHUFFLE(0,0,0,0)), LOADPS_I(A)n1(m.ma[0], 0));
				for(int j=1 ; j<AUn1_n0[1] ; j++)
					Inner(j, AUn1_n0[0]);
				STOREPS_(A)AUn1_n0[0](ret.ma[i], accum);
			*/
			#define MUL_OUTER(z,n,AUn1_n0) { \
				reg128 tm = LOADTHISI(n); \
				reg128 accum = reg_mul_ps( \
								reg_shuffle_ps(tm, tm, _REG_SHUFFLE(0,0,0,0)), \
								BOOST_PP_CAT( \
									LOADPS_I, \
									BOOST_PP_SEQ_ELEM(0,AUn1_n0) \
								)(m.ma[0], 0) \
							); \
				BOOST_PP_REPEAT_FROM_TO( \
					1, \
					BOOST_PP_SEQ_ELEM(1,AUn1_n0), \
					MUL_INNER, \
					BOOST_PP_SEQ_ELEM(0,AUn1_n0) \
				) \
				BOOST_PP_CAT(STOREPS_, BOOST_PP_SEQ_ELEM(0, AUn1_n0))(ret.ma[n], accum); }
			BOOST_PP_REPEAT(LEN_SEQ, DEF_CONV_ITR, DEF_MUL0)
			#undef DEF_MUL0
			#undef DEF_MUL
		}
		#else
		namespace spn {
			// 自分との積算でなければ上書き演算
			/*	Pseudo-code:
				MT& MT::operator *= (const MatT<n0,n1,align>& m) {
					// 自分との積算ならば *this = *this * *this; の形で計算
					if((uintptr_t)&m == (uintptr_t)this)
						return *this = *this * *this;
					// <Repeat by MUL_OUTER2>
					for(int i=0 ; i<DIM_M ; i++) {
						reg128 tm = LOADTHIS(n);
						reg128 accum = reg_mul_ps(reg_shuffle_ps(tm,tm, _REG_SHUFFLE(0,0,0,0)), LOADTHIS(0));
						// <Repeat by MUL_INNER2>
						for(int j=0 ; j<DIM_M ; j++)
							reg_add_ps(accum, reg_mul_ps(reg_shuffle_ps(tm,tm, _REG_SHUFFLE(j,j,j,j)), LOADTHIS(j));
						STORETHIS(j,accum);
					}
					return *this;
				}
			*/
			#define DEF_MULE0(n0,n1,align) \
				BOOST_PP_IF( \
					BOOST_PP_AND( \
						BOOST_PP_EQUAL(DIM_N, n0), \
						BOOST_PP_EQUAL(n1, DIM_N) \
					), \
					DEF_MULE, \
					NOTHING_ARG \
				)(n0,n1,align)
			#define DEF_MULE(n0,n1,align) \
				MT& MT::operator *= (const MatT<n0,n1,BOOLNIZE(align)>& m) { \
					if((intptr_t)&m == (intptr_t)this) return mul_self(); \
						BOOST_PP_REPEAT( \
							DIM_M, MUL_OUTER2, \
							BOOST_PP_IF(align, NOTHING, U) \
						) \
					return *this; }
			#define MUL_INNER2(z,n,AU) \
				accum = reg_add_ps( \
					accum, \
					reg_mul_ps(reg_shuffle_ps(tm,tm, _REG_SHUFFLE(n,n,n,n)), LOADPS##AU(m.ma[n])) \
				);
			#define MUL_OUTER2(z,n,AU) { \
				reg128 tm = LOADTHIS(n); \
				reg128 accum = reg_mul_ps(reg_shuffle_ps(tm, tm, _REG_SHUFFLE(0,0,0,0)), LOADPS##AU(m.ma[0])); \
				BOOST_PP_REPEAT_FROM_TO(1,DIM_N, MUL_INNER2, AU) \
				STORETHISPS(ma[n], accum); }
			BOOST_PP_REPEAT(LEN_SEQ, DEF_CONV_ITR, DEF_MULE0)
			#undef DEF_MULE0
			#undef DEF_MULE
			#undef DEF_INNER2
			#undef DEF_OUTER2

			// 他の行列やベクトルと計算するメソッドを定義
			/*	Pseudo-code:
				template <>
				VecT<DIM_N,ALIGNB> operator * (const VecT<DIM_N,align>& v) const {
					auto tmpM = transposition();
					return v * tmpM;
				}
			*/
			#define DEF_MULOP(z,align,dummy) \
				template <> VecT<DIM_N,ALIGNB> MT::operator * (const VecT<DIM_N,BOOLNIZE(align)>& v) const { \
					auto tmpM = BOOST_PP_CAT( \
									BOOST_PP_CAT( \
										BOOST_PP_CAT( \
											transposition().convert, \
											AFLAG(ALIGN) \
										), \
										DIM_N \
									), \
									DIM_N \
								)(); \
					return v * tmpM; }
			BOOST_PP_REPEAT(1, DEF_MULOP, NOTHING)
			#undef DEF_MULOP
			#if DIM_M==DIM_N
				VecT<DIM_N,MT::align> MT::mulV(const VecT<DIM_N,align>& v) const {
					auto tmpM = transposition();
					return v * tmpM;
				}
			#endif
		}
	#endif
	#include "local_unmacro.hpp"
	#undef TUP
	#undef DIM_M
	#undef DIM_N
	#undef DIM
	#undef ALIGN
	#undef ALIGNB
	#undef ALIGN16
	#undef MT
	#undef DMAX
	#undef DMIN
	#undef DMUL
	#undef DIMTUPLE
	#undef DEF_CONV_ITR
	#undef DEFINE_MATRIX
#endif