//
// Copyright (c) 2020 Kenshi Abe
//

#include "Trainer.hpp"

#include <iostream>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/filesystem.hpp>
#include <boost/serialization/unordered_map.hpp>
#include "Node.hpp"

namespace Trainer {

/// @param mode variant of CFR algorithm
/// @param seed random seed
/// @param strategyPaths paths to the binary files that represent fixed strategies
template <typename T>
Trainer<T>::Trainer(const std::string &mode, const uint32_t seed, const std::vector<std::string> &strategyPaths) : mEngine(seed), mNodeTouchedCnt(0), mModeStr(mode) {
    mGame = new T(mEngine);
    mFolderPath = "../strategies/" + mGame->name();
    boost::filesystem::create_directories(mFolderPath);
    mFixedStrategies = new std::unordered_map<std::string, Node *>[mGame->playerNum()];
    mUpdate = new bool[mGame->playerNum()];
    for (int i = 0; i < mGame->playerNum(); ++i) {
        if (strategyPaths.size() >= i + 1 && !strategyPaths[i].empty()) {
            std::cout << "load strategy \"" << strategyPaths[i] << "\" as static player " << i << std::endl;
            std::ifstream ifs(strategyPaths[i]);
            boost::archive::binary_iarchive ia(ifs);
            ia >> mFixedStrategies[i];
            ifs.close();
            mUpdate[i] = false;
        } else {
            mUpdate[i] = true;
        }
    }
}

template <typename T>
Trainer<T>::~Trainer() {
    for (auto &itr : mNodeMap) {
        delete itr.second;
    }
    for (int i = 0; i < mGame->playerNum(); ++i) {
        if (mUpdate[i]) {
            continue;
        }
        for (auto &itr : mFixedStrategies[i]) {
            delete itr.second;
        }
    }
    delete[] mFixedStrategies;
    delete[] mUpdate;
    delete mGame;
}

/// @brief Calculate the expected payoff of each player
/// @param game game
/// @param strategies list of strategies for each player
/// @return list of expected payoffs
template <typename T>
std::vector<double> Trainer<T>::CalculatePayoff(const T &game, const std::vector<std::function<const double *(const T &)>> &strategies) {
    // return payoff for terminal states
    if (game.done()) {
        std::vector<double> payoffs(game.playerNum());
        for (int i = 0; i < game.playerNum(); ++i) {
            payoffs[i] = game.payoff(i);
        }
        return payoffs;
    }

    // chance node turn
    const int actionNum = game.actionNum();
    if (game.isChanceNode()) {
        std::vector<double> nodeUtils(game.playerNum());
        for (int i = 0; i < game.playerNum(); ++i) {
            nodeUtils[i] = 0.0;
        }
        for (int a = 0; a < actionNum; ++a) {
            auto game_cp(game);
            game_cp.step(a);
            const double chanceProbability = game_cp.chanceProbability();
            std::vector<double> utils = CalculatePayoff(game_cp, strategies);
            for (int i = 0; i < game.playerNum(); ++i) {
                nodeUtils[i] += chanceProbability * utils[i];
            }
        }
        return nodeUtils;
    }

    // for each action, recursively calculate payoff with additional history and probability
    const int player = game.currentPlayer();
    std::vector<double> nodeUtils(game.playerNum());
    for (int i = 0; i < game.playerNum(); ++i) {
        nodeUtils[i] = 0.0;
    }
    for (int a = 0; a < actionNum; ++a) {
        auto game_cp(game);
        game_cp.step(a);
        std::vector<double> utils = CalculatePayoff(game_cp, strategies);
        for (int i = 0; i < game.playerNum(); ++i) {
            nodeUtils[i] += strategies[player](game)[a] * utils[i];
        }
    }
    return nodeUtils;
}

/// @brief Calculate exploitability of a given strategy profile
/// @param game game
/// @param strategies list of strategies for each player
/// @return exploitability of a given strategy profile
template <typename T>
double Trainer<T>::CalculateExploitability(const T &game, const std::vector<std::function<const double *(const T &)>> &strategies) {
    InfoSets infoSets;
    for (int p = 0; p < game.playerNum(); ++p) {
        auto game_cp(game);
        game_cp.reset(false);
        CreateInfoSets(game_cp, p, strategies, 1.0, infoSets);
    }

    double exploitability = 0.0;
    for (int p = 0; p < game.playerNum(); ++p) {
        auto game_cp(game);
        game_cp.reset(false);
        std::unordered_map<std::string, std::vector<double>> bestResponseStrategies;
        exploitability += CalculateBestResponseValue(game_cp, p, strategies, bestResponseStrategies, 1.0, infoSets);
    }
    return exploitability;
}

/// @brief Fill the ordered map that maps information sets to game nodes and reach probabilities
/// @param game game
/// @param playerIndex player whose strategy is updated in the current iteration
/// @param strategies list of strategies for each player
/// @param po the probability of reaching the current game node if the acting player always chooses actions leading to the current game node
/// @return exploitability of a given strategy profile
template <typename T>
void Trainer<T>::CreateInfoSets(const T &game, const int playerIndex, const std::vector<std::function<const double *(const T &)>> &strategies, const double po, InfoSets &infoSets) {
    // return at terminal states
    if (game.done()) {
        return;
    }

    // chance node turn
    const int actionNum = game.actionNum();
    if (game.isChanceNode()) {
        for (int a = 0; a < actionNum; ++a) {
            auto game_cp(game);
            game_cp.step(a);
            const double chanceProbability = game_cp.chanceProbability();
            CreateInfoSets(game_cp, playerIndex, strategies, po * chanceProbability, infoSets);
        }
        return;
    }

    const int player = game.currentPlayer();
    if (player == playerIndex) {
        std::string infoSet = game.infoSetStr();
        if (infoSets.count(infoSet) == 0) {
            infoSets[infoSet] = std::vector<std::tuple<T, double>>();
        }
        infoSets[infoSet].push_back(std::make_tuple(game, po));
    }

    for (int a = 0; a < actionNum; ++a) {
        auto game_cp(game);
        game_cp.step(a);
        if (player == playerIndex) {
            CreateInfoSets(game_cp, playerIndex, strategies, po, infoSets);
        } else {
            const double actionProb = strategies[player](game)[a];
            CreateInfoSets(game_cp, playerIndex, strategies, po * actionProb, infoSets);
        }
    }
}

/// @brief Calculate best response value for a given player
/// @param game game
/// @param playerIndex player whose strategy is updated in the current iteration
/// @param strategies list of strategies for each player
/// @param bestResponseStrategies best response strategy for a given player
/// @param po the probability of reaching the current game node if the acting player always chooses actions leading to the current game node
/// @param infoSets ordered map that maps information sets to game nodes and reach probabilities
/// @return best response value for a given player
template <typename T>
double Trainer<T>::CalculateBestResponseValue(const T &game, const int playerIndex,
                                             const std::vector<std::function<const double *(const T &)>> &strategies,
                                             std::unordered_map<std::string, std::vector<double>> &bestResponseStrategies,
                                             const double po,
                                             const InfoSets &infoSets) {
    // return payoff for terminal states
    if (game.done()) {
        return game.payoff(playerIndex);
    }

    // chance node turn
    const int actionNum = game.actionNum();
    if (game.isChanceNode()) {
        double nodeUtil = 0.0;
        for (int a = 0; a < actionNum; ++a) {
            auto game_cp(game);
            game_cp.step(a);
            const double chanceProbability = game_cp.chanceProbability();
            nodeUtil += chanceProbability * CalculateBestResponseValue(game_cp, playerIndex, strategies, bestResponseStrategies, po * chanceProbability, infoSets);
        }
        return nodeUtil;
    }

    const int player = game.currentPlayer();
    if (player == playerIndex) {
        // get information set string representation
        std::string infoSet = game.infoSetStr();
        if (bestResponseStrategies.count(infoSet) == 0) {
            // calculate action values
            double actionValues[actionNum];
            for (int a = 0; a < actionNum; ++a) {
                actionValues[a] = 0.0;
            }
            for (int i = 0; i < infoSets.at(infoSet).size(); ++i) {
                auto game_ = std::get<0>(infoSets.at(infoSet)[i]);
                auto po_ = std::get<1>(infoSets.at(infoSet)[i]);
                double brValues[actionNum];
                for (int a = 0; a < actionNum; ++a) {
                    auto game_cp(game_);
                    game_cp.step(a);
                    brValues[a] = CalculateBestResponseValue(game_cp, playerIndex, strategies, bestResponseStrategies, po_, infoSets);
                    actionValues[a] += po_ * brValues[a];
                }
            }
            // calculate best response strategy
            int brAction = 0;
            for (int a = 0; a < actionNum; ++a) {
                if (actionValues[a] > actionValues[brAction]) {
                    brAction = a;
                }
            }
            bestResponseStrategies[infoSet] = std::vector<double>(actionNum, 0.0);
            bestResponseStrategies[infoSet][brAction] = 1.0;
        }
        // calculate best response value
        double utils[actionNum];
        for (int a = 0; a < actionNum; ++a) {
            auto game_cp(game);
            game_cp.step(a);
            utils[a] = CalculateBestResponseValue(game_cp, playerIndex, strategies, bestResponseStrategies, po, infoSets);
        }
        double bestResponseValue = 0.0;
        for (int a = 0; a < actionNum; ++a) {
            bestResponseValue += utils[a] * bestResponseStrategies.at(infoSet)[a];
        }
        return bestResponseValue;
    } else {
        // for each action, recursively calculate payoff with additional history and probability
        double nodeUtil = 0.0;
        for (int a = 0; a < actionNum; ++a) {
            auto game_cp(game);
            game_cp.step(a);
            const double actionProb = strategies[player](game)[a];
            nodeUtil += actionProb * CalculateBestResponseValue(game_cp, playerIndex, strategies, bestResponseStrategies, po * actionProb, infoSets);
        }
        return nodeUtil;
    }
}

/// @brief Execute the CFR algorithm to compute an approximate Nash equilibrium
/// @param iterations number of iterations of CFR
template <typename T>
void Trainer<T>::train(const int iterations) {
    double utils[mGame->playerNum()];

    for (int i = 0; i < iterations; ++i) {
        for (int p = 0; p < mGame->playerNum(); ++p) {
            if (!mUpdate[p]) {
                continue;
            }
            if (mModeStr == "vanilla") {
                mGame->reset(false);
                utils[p] = CFR(*mGame, p, 1.0, 1.0);
                for (auto & itr : mNodeMap) {
                    itr.second->updateStrategy();
                }
            } else {
                mGame->reset();
                if (mModeStr == "chance") {
                    utils[p] = chanceSamplingCFR(*mGame, p, 1.0, 1.0);
                    for (auto & itr : mNodeMap) {
                        itr.second->updateStrategy();
                    }
                } else if (mModeStr == "external") {
                    utils[p] = externalSamplingCFR(*mGame, p);
                } else if (mModeStr == "outcome") {
                    utils[p] = std::get<0>(outcomeSamplingCFR(*mGame, p, i, 1.0, 1.0, 1.0));
                } else {
                    assert(false);
                }
            }
        }
        if (i % 1000 == 0) {
            std::cout << "iteration:" << i << ", cumulative nodes touched: " << mNodeTouchedCnt << ", infosets num: " << mNodeMap.size() << ", expected payoffs: (";
            for (int p = 0; p < mGame->playerNum(); ++p) {
                std::cout << utils[p] << ",";
            }
            std::cout << ")" << std::endl;
        }
        if (i != 0 && i % 10000000 == 0) {
            writeStrategyToBin(i);
        }
    }

    writeStrategyToBin();
}

/// @brief Main procedure of vanilla CFR
/// @param game game
/// @param playerIndex player whose strategy is updated in the current iteration
/// @param pi the probability of reaching the current game node if all players other than the acting player always choose actions leading to the current game node
/// @param po the probability of reaching the current game node if the acting player always chooses actions leading to the current game node
/// @return expected payoff of the specified player at the current game node
template <typename T>
double Trainer<T>::CFR(const T &game, const int playerIndex, const double pi, const double po) {
    ++mNodeTouchedCnt;

    // return payoff for terminal states
    if (game.done()) {
        return game.payoff(playerIndex);
    }

    // chance node turn
    const int actionNum = game.actionNum();
    if (game.isChanceNode()) {
        double nodeUtil = 0.0;
        for (int a = 0; a < actionNum; ++a) {
            auto game_cp(game);
            game_cp.step(a);
            const double chanceProbability = game_cp.chanceProbability();
            nodeUtil += chanceProbability * CFR(game_cp, playerIndex, pi, po * chanceProbability);
        }
        return nodeUtil;
    }

    // get information set string representation
    std::string infoSet = game.infoSetStr();

    // treat static player as chance node
    const int player = game.currentPlayer();
    if (!mUpdate[player]) {
        double nodeUtil = 0.0;
        for (int a = 0; a < actionNum; ++a) {
            auto game_cp(game);
            game_cp.step(a);
            const auto chanceProbability = double(mFixedStrategies[player].at(infoSet)->averageStrategy()[a]);
            nodeUtil += chanceProbability * CFR(game_cp, playerIndex, pi, po * chanceProbability);
        }
        return nodeUtil;
    }

    // get information set node or create it if nonexistant
    Node *node = mNodeMap[infoSet];
    if (node == nullptr) {
        node = new Node(actionNum);
        mNodeMap[infoSet] = node;
    }

    // get current strategy through regret-matching
    const double *strategy = node->strategy();

    // for each action, recursively call CFR with additional history and probability
    double utils[actionNum];
    double nodeUtil = 0;
    for (int a = 0; a < actionNum; ++a) {
        auto game_cp(game);
        game_cp.step(a);
        if (player == playerIndex) {
            utils[a] = CFR(game_cp, playerIndex, pi * strategy[a], po);
        } else {
            utils[a] = CFR(game_cp, playerIndex, pi, po * strategy[a]);
        }
        nodeUtil += strategy[a] * utils[a];
    }

    if (player == playerIndex) {
        // for each action, compute and accumulate counterfactual regret
        for (int a = 0; a < actionNum; ++a) {
            const double regret = utils[a] - nodeUtil;
            const double regretSum = node->regretSum(a) + po * regret;
            node->regretSum(a, regretSum);
        }
        // update average strategy across all training iterations
        node->strategySum(strategy, pi);
    }

    return nodeUtil;
}

/// @brief Main procedure of chance-sampling MCCFR
/// @param game game
/// @param playerIndex player whose strategy is updated in the current iteration
/// @param pi the probability of reaching the current game node if all players other than the acting player always choose actions leading to the current game node
/// @param po the probability of reaching the current game node if the acting player and the chance player always choose actions leading to the current game node
/// @return estimated expected payoff of the specified player at the current game node
template <typename T>
double Trainer<T>::chanceSamplingCFR(const T &game, const int playerIndex, const double pi, const double po) {
    ++mNodeTouchedCnt;

    // return payoff for terminal states
    if (game.done()) {
        return game.payoff(playerIndex);
    }

    // get information set string representation
    std::string infoSet = game.infoSetStr();

    // treat static player as chance node
    const int actionNum = game.actionNum();
    const int player = game.currentPlayer();
    if (!mUpdate[player]) {
        auto game_cp(game);
        auto strategy = mFixedStrategies[player].at(infoSet)->averageStrategy();
        std::discrete_distribution<int> dist(strategy, strategy + actionNum);
        game_cp.step(dist(mEngine));
        return chanceSamplingCFR(game_cp, playerIndex, pi, po);
    }

    // get information set node or create it if nonexistant
    Node *node = mNodeMap[infoSet];
    if (node == nullptr) {
        node = new Node(actionNum);
        mNodeMap[infoSet] = node;
    }

    // get current strategy through regret-matching
    const double *strategy = node->strategy();

    // for each action, recursively call cfr with additional history and probability
    double utils[actionNum];
    double nodeUtil = 0;
    for (int a = 0; a < actionNum; ++a) {
        auto game_cp(game);
        game_cp.step(a);
        if (player == playerIndex) {
            utils[a] = chanceSamplingCFR(game_cp, playerIndex, pi * strategy[a], po);
        } else {
            utils[a] = chanceSamplingCFR(game_cp, playerIndex, pi, po * strategy[a]);
        }
        nodeUtil += strategy[a] * utils[a];
    }

    if (player == playerIndex) {
        // for each action, compute and accumulate counterfactual regret
        for (int a = 0; a < actionNum; ++a) {
            const double regret = utils[a] - nodeUtil;
            const double regretSum = node->regretSum(a) + po * regret;
            node->regretSum(a, regretSum);
        }
        // update average strategy across all training iterations
        node->strategySum(strategy, pi);
    }

    return nodeUtil;
}

/// @brief Main procedure of external-sampling MCCFR
/// @param game game
/// @param playerIndex player whose strategy is updated in the current iteration
/// @return estimated expected payoff of the specified player at the current game node
template <typename T>
double Trainer<T>::externalSamplingCFR(const T &game, const int playerIndex) {
    ++mNodeTouchedCnt;

    // return payoff for terminal states
    if (game.done()) {
        return game.payoff(playerIndex);
    }

    // get information set string representation
    std::string infoSet = game.infoSetStr();

    // external sampling with stochastically-weighted averaging cannot treat static player
    const int actionNum = game.actionNum();
    const int player = game.currentPlayer();
    assert(mUpdate[player] && "External sampling with stochastically-weighted averaging cannot treat static player.");

    // get information set node or create it if nonexistant
    Node *node = mNodeMap[infoSet];
    if (node == nullptr) {
        node = new Node(actionNum);
        mNodeMap[infoSet] = node;
    }

    // get current strategy through regret-matching
    node->updateStrategy();
    const double *strategy = node->strategy();

    // if current player is not the target player, sample a single action and recursively call cfr
    if (player != playerIndex) {
        auto game_cp(game);
        std::discrete_distribution<int> dist(strategy, strategy + actionNum);
        game_cp.step(dist(mEngine));
        const double util = externalSamplingCFR(game_cp, playerIndex);
        // update average strategy
        node->strategySum(strategy, 1.0);
        return util;
    }

    // for each action, recursively call cfr with additional history and probability
    double utils[actionNum];
    double nodeUtil = 0;
    for (int a = 0; a < actionNum; ++a) {
        auto game_cp(game);
        game_cp.step(a);
        utils[a] = externalSamplingCFR(game_cp, playerIndex);
        nodeUtil += strategy[a] * utils[a];
    }

    // for each action, compute and accumulate counterfactual regret
    for (int a = 0; a < actionNum; ++a) {
        const double regret = utils[a] - nodeUtil;
        const double regretSum = node->regretSum(a) + regret;
        node->regretSum(a, regretSum);
    }

    return nodeUtil;
}

/// @brief Main procedure of outcome-sampling MCCFR
/// @param game game
/// @param playerIndex player whose strategy is updated in the current iteration
/// @param pi the probability of reaching the current game node if all players other than the acting player always choose actions leading to the current game node
/// @param po the probability of reaching the current game node if the acting player and the chance player always choose actions leading to the current game node
/// @param s the probability of reaching the current game node if the chance player always chooses actions leading to the current game node and the other players act according to the sample profile
/// @return estimated expected payoff of the specified player at the current game node, and the probability of reaching the terminal game node if the chance player always chooses actions leading to the terminal game node
template <typename T>
std::tuple<double, double> Trainer<T>::outcomeSamplingCFR(const T &game, const int playerIndex, const int iteration , const double pi, const double po, const double s) {
    ++mNodeTouchedCnt;

    // return payoff for terminal states
    if (game.done()) {
        return std::make_tuple(game.payoff(playerIndex) / s, 1.0);
    }

    // get information set string representation
    std::string infoSet = game.infoSetStr();

    // outcome sampling with stochastically-weighted averaging cannot treat static player
    const int actionNum = game.actionNum();
    const int player = game.currentPlayer();
    assert(mUpdate[player] && "Outcome sampling with stochastically-weighted averaging cannot treat static player.");

    // get information set node or create it if nonexistant
    Node *node = mNodeMap[infoSet];
    if (node == nullptr) {
        node = new Node(actionNum);
        mNodeMap[infoSet] = node;
    }

    // get current strategy through regret-matching
    node->updateStrategy();
    const double *strategy = node->strategy();

    // if current player is the target player, sample a single action according to epsilon-on-policy
    // otherwise, sample a single action according to the player's strategy
    const double epsilon = 0.6;
    double probability[actionNum];
    if (player == playerIndex) {
        for (int a = 0; a < actionNum; ++a) {
            probability[a] = (epsilon / (double) actionNum) + (1.0 - epsilon) * strategy[a];
        }
    } else {
        for (int a = 0; a < actionNum; ++a) {
            probability[a] = strategy[a];
        }
    }
    std::discrete_distribution<int> dist(probability, probability + actionNum);
    const int action = dist(mEngine);

    // for sampled action, recursively call cfr with additional history and probability
    double util, pTail;
    auto game_cp(game);
    game_cp.step(action);
    const double newPi = pi * (player == playerIndex ? strategy[action] : 1.0);
    const double newPo = po * (player == playerIndex ? 1.0 : strategy[action]);
    std::tuple<double, double> ret = outcomeSamplingCFR(game_cp, playerIndex, iteration, newPi, newPo, s * probability[action]);
    util = std::get<0>(ret);
    pTail = std::get<1>(ret);
    if (player == playerIndex) {
        // for each action, compute and accumulate counterfactual regret
        const double W = util * po;
        for (int a = 0; a < actionNum; ++a) {
            const double regret = a == action ? W * (1.0 - strategy[action]) * pTail : -W * pTail * strategy[action];
            const double regretSum = node->regretSum(a) + regret;
            node->regretSum(a, regretSum);
        }
    } else {
        // update average strategy
        node->strategySum(strategy, po / s);
    }
    return std::make_tuple(util, pTail * strategy[action]);
}

/// @brief Save the current average strategy as a binary file
/// @param iteration current iteration
template <typename T>
void Trainer<T>::writeStrategyToBin(const int iteration) const {
    for (auto &itr : mNodeMap) {
        for (char c : itr.first) {
            std::cout << int(c);
        }
        std::cout << ":";
        for (int i = 0; i < itr.second->actionNum(); ++i) {
            std::cout << itr.second->averageStrategy()[i] << ",";
        }
        std::cout << std::endl;
    }
    std::string path = iteration > 0 ? "strategy_" + std::to_string(iteration)
                                     : "strategy";
    path += "_" + mModeStr + ".bin";
    std::ofstream ofs(mFolderPath + "/" + path);
    boost::archive::binary_oarchive oa(ofs);
    oa << mNodeMap;
    ofs.close();
}

} // namespace


const bool enable_expensive_tests = false;

#define BOOST_TEST_MODULE
#include <boost/test/unit_test.hpp>
#include <util/test_macros.hpp>
#include <vector>
#include <string>
#include <functional>

#include <random/random.hpp>

#include <unity/toolkits/recsys/models/factorization_models.hpp>
#include <unity/toolkits/util/data_generators.hpp>
#include <unity/toolkits/ml_data_2/ml_data.hpp>
#include <unity/toolkits/ml_data_2/ml_data_iterators.hpp>
#include <sframe/testing_utils.hpp>
#include <util/testing_utils.hpp>
#include <util/testing_utils.hpp>
#include <unity/lib/variant.hpp>

#include <cfenv>

#include "factorization_test_helpers.hpp"

using namespace turi;
using namespace turi::recsys;

struct factorization_tests  {
 public:

  ////////////////////////////////////////////////////////////////////////////////

  void test_factorization_se_really_bloody_basic_2d() {
    std::map<std::string, flexible_type> opts = {
      {"n_observations",      10 },
      {"num_factors",           1 } };

    test_convergence({2, 2}, opts, "fm");
  }

  ////////////////////////////////////////////////////////////////////////////////

  void test_factorization_se_really_bloody_basic_2d_8f() {
    std::map<std::string, flexible_type> opts = {
      {"n_observations",      10 },
      {"num_factors",           8 } };

    test_convergence({2, 2}, opts, "fm");
  }

  // // ////////////////////////////////////////////////////////////////////////////////

  void test_factorization_se_really_bloody_basic_3d() {
    if(!enable_expensive_tests)
      return;

    std::map<std::string, flexible_type> opts = {
      {"n_observations",      10 },
      {"num_factors",           1 } };

    test_convergence({2, 2, 2}, opts, "fm");
  }

  // // ////////////////////////////////////////////////////////////////////////////////

  void test_factorization_se_many_factors() {
    if(!enable_expensive_tests)
      return;

    std::map<std::string, flexible_type> opts = {
      {"n_observations",      100 },
      {"num_factors",           1000 }
    };

    test_convergence({4, 4, 4}, opts, "fm");
  }

  void test_factorization_se_8_factors() {
    std::map<std::string, flexible_type> opts = {
      {"n_observations",      100 },
      {"num_factors",           8}
    };

    test_convergence({8, 8, 8}, opts, "fm");
  }

  // // ////////////////////////////////////////////////////////////////////////////////

  void test_factorization_se_many_columns() {
    if(!enable_expensive_tests)
      return;

    std::map<std::string, flexible_type> opts = {
      {"n_observations",      100 },
      {"num_factors",           1 } };

    test_convergence({8, 8, 8, 8}, opts, "fm");
  }
};

struct log_factorization_tests  {
 public:

  ////////////////////////////////////////////////////////////////////////////////

  void test_factorization_log_really_bloody_basic_2d() {
    std::map<std::string, flexible_type> opts = {
      {"n_observations",      10 },
      {"num_factors",           1 } };

    test_convergence({1, 1}, opts, "logistic_fm");
  }

  // // ////////////////////////////////////////////////////////////////////////////////

  void test_factorization_log_many_factors_2d() {
    if(!enable_expensive_tests)
      return;

    std::map<std::string, flexible_type> opts = {
      {"n_observations",      100 },
      {"num_factors",          1000 } };

    test_convergence({8, 8}, opts, "logistic_fm");
  }

  // // ////////////////////////////////////////////////////////////////////////////////

  void test_factorization_log_8_factors_2d() {
    std::map<std::string, flexible_type> opts = {
      {"n_observations",      100 },
      {"num_factors",          8 } };

    test_convergence({8, 8}, opts, "logistic_fm");
  }

  // // ////////////////////////////////////////////////////////////////////////////////

  void test_factorization_log_many_factors_3d() {
    if(!enable_expensive_tests)
      return;

    std::map<std::string, flexible_type> opts = {
      {"n_observations",      100 },
      {"num_factors",          1000 } };

    test_convergence({8, 8, 8}, opts, "logistic_fm");
  }

  // // ////////////////////////////////////////////////////////////////////////////////

  void test_factorization_log_8_factors_3d() {
    std::map<std::string, flexible_type> opts = {
      {"n_observations",      100 },
      {"num_factors",         8 } };

    test_convergence({4, 4, 4}, opts, "logistic_fm");
  }

  // // ////////////////////////////////////////////////////////////////////////////////

  void test_factorization_log_many_categories() {
    if(!enable_expensive_tests)
      return;

    std::map<std::string, flexible_type> opts = {
      {"n_observations",      200 },
      {"num_factors",         1 } };

    test_convergence({2, 50}, opts, "logistic_fm");
  }

  void test_factorization_log_many_dimensions() {
    if(!enable_expensive_tests)
      return;

    std::map<std::string, flexible_type> opts = {
      {"n_observations",      100 },
      {"num_factors",         1 } };

    test_convergence({4, 4, 4}, opts, "logistic_fm");
  }

  void test_factorization_regression() {

    std::vector<std::vector<flexible_type> > Xv(300);

    for(size_t i = 0; i < 300; ++i) {
      Xv[i] = {i % 10, i % 30, 1};
    }

    sframe data = make_testing_sframe({"user_id", "item_id", "target"}, Xv);

    std::map<std::string, flexible_type> options =
        { {"solver", "auto"},
          {"binary_target", false},
          {"target", "target"},
          {"max_iterations", 5} };

    typedef std::shared_ptr<recsys::recsys_model_base> model_ptr;
    model_ptr model(new recsys_ranking_factorization_model);

    model->init_options(options);

    model->setup_and_train(data);
  }

};

BOOST_FIXTURE_TEST_SUITE(_factorization_tests, factorization_tests)
BOOST_AUTO_TEST_CASE(test_factorization_se_really_bloody_basic_2d) {
  factorization_tests::test_factorization_se_really_bloody_basic_2d();
}
BOOST_AUTO_TEST_CASE(test_factorization_se_really_bloody_basic_2d_8f) {
  factorization_tests::test_factorization_se_really_bloody_basic_2d_8f();
}
BOOST_AUTO_TEST_CASE(test_factorization_se_really_bloody_basic_3d) {
  factorization_tests::test_factorization_se_really_bloody_basic_3d();
}
BOOST_AUTO_TEST_CASE(test_factorization_se_many_factors) {
  factorization_tests::test_factorization_se_many_factors();
}
BOOST_AUTO_TEST_CASE(test_factorization_se_8_factors) {
  factorization_tests::test_factorization_se_8_factors();
}
BOOST_AUTO_TEST_CASE(test_factorization_se_many_columns) {
  factorization_tests::test_factorization_se_many_columns();
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_FIXTURE_TEST_SUITE(_log_factorization_tests, log_factorization_tests)
BOOST_AUTO_TEST_CASE(test_factorization_log_really_bloody_basic_2d) {
  log_factorization_tests::test_factorization_log_really_bloody_basic_2d();
}
BOOST_AUTO_TEST_CASE(test_factorization_log_many_factors_2d) {
  log_factorization_tests::test_factorization_log_many_factors_2d();
}
BOOST_AUTO_TEST_CASE(test_factorization_log_8_factors_2d) {
  log_factorization_tests::test_factorization_log_8_factors_2d();
}
BOOST_AUTO_TEST_CASE(test_factorization_log_many_factors_3d) {
  log_factorization_tests::test_factorization_log_many_factors_3d();
}
BOOST_AUTO_TEST_CASE(test_factorization_log_8_factors_3d) {
  log_factorization_tests::test_factorization_log_8_factors_3d();
}
BOOST_AUTO_TEST_CASE(test_factorization_log_many_categories) {
  log_factorization_tests::test_factorization_log_many_categories();
}
BOOST_AUTO_TEST_CASE(test_factorization_log_many_dimensions) {
  log_factorization_tests::test_factorization_log_many_dimensions();
}
BOOST_AUTO_TEST_CASE(test_factorization_regression) {
  log_factorization_tests::test_factorization_regression();
}
BOOST_AUTO_TEST_SUITE_END()


// Copyright (c) 2008-2017 Emil Dotchevski and Reverge Studios, Inc.

// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_QVM_B3A6DB3C87C3E99245ED1C6B747DE
#define BOOST_QVM_B3A6DB3C87C3E99245ED1C6B747DE

// This file was generated by a program. Do not edit manually.

#include <boost/qvm/assert.hpp>
#include <boost/qvm/deduce_mat.hpp>
#include <boost/qvm/deduce_vec.hpp>
#include <boost/qvm/error.hpp>
#include <boost/qvm/gen/mat_assign3.hpp>
#include <boost/qvm/quat_traits.hpp>
#include <boost/qvm/scalar_traits.hpp>
#include <boost/qvm/throw_exception.hpp>

namespace boost {
namespace qvm {
template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
        mat_traits<A>::cols == 3 && mat_traits<B>::cols == 3,
    deduce_mat2<A, B, 3, 3>>::type
operator+(A const &a, B const &b) {
  typedef typename deduce_mat2<A, B, 3, 3>::type R;
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows == 3);
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols == 3);
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) +
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<R>::template write_element<0, 1>(r) =
      mat_traits<A>::template read_element<0, 1>(a) +
      mat_traits<B>::template read_element<0, 1>(b);
  mat_traits<R>::template write_element<0, 2>(r) =
      mat_traits<A>::template read_element<0, 2>(a) +
      mat_traits<B>::template read_element<0, 2>(b);
  mat_traits<R>::template write_element<1, 0>(r) =
      mat_traits<A>::template read_element<1, 0>(a) +
      mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<R>::template write_element<1, 1>(r) =
      mat_traits<A>::template read_element<1, 1>(a) +
      mat_traits<B>::template read_element<1, 1>(b);
  mat_traits<R>::template write_element<1, 2>(r) =
      mat_traits<A>::template read_element<1, 2>(a) +
      mat_traits<B>::template read_element<1, 2>(b);
  mat_traits<R>::template write_element<2, 0>(r) =
      mat_traits<A>::template read_element<2, 0>(a) +
      mat_traits<B>::template read_element<2, 0>(b);
  mat_traits<R>::template write_element<2, 1>(r) =
      mat_traits<A>::template read_element<2, 1>(a) +
      mat_traits<B>::template read_element<2, 1>(b);
  mat_traits<R>::template write_element<2, 2>(r) =
      mat_traits<A>::template read_element<2, 2>(a) +
      mat_traits<B>::template read_element<2, 2>(b);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator+;
}

namespace qvm_detail {
template <int R, int C> struct plus_mm_defined;

template <> struct plus_mm_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
        mat_traits<A>::cols == 1 && mat_traits<B>::cols == 1,
    deduce_mat2<A, B, 3, 1>>::type
operator+(A const &a, B const &b) {
  typedef typename deduce_mat2<A, B, 3, 1>::type R;
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows == 3);
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols == 1);
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) +
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<R>::template write_element<1, 0>(r) =
      mat_traits<A>::template read_element<1, 0>(a) +
      mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<R>::template write_element<2, 0>(r) =
      mat_traits<A>::template read_element<2, 0>(a) +
      mat_traits<B>::template read_element<2, 0>(b);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator+;
}

namespace qvm_detail {
template <int R, int C> struct plus_mm_defined;

template <> struct plus_mm_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 1 && mat_traits<B>::rows == 1 &&
        mat_traits<A>::cols == 3 && mat_traits<B>::cols == 3,
    deduce_mat2<A, B, 1, 3>>::type
operator+(A const &a, B const &b) {
  typedef typename deduce_mat2<A, B, 1, 3>::type R;
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows == 1);
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols == 3);
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) +
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<R>::template write_element<0, 1>(r) =
      mat_traits<A>::template read_element<0, 1>(a) +
      mat_traits<B>::template read_element<0, 1>(b);
  mat_traits<R>::template write_element<0, 2>(r) =
      mat_traits<A>::template read_element<0, 2>(a) +
      mat_traits<B>::template read_element<0, 2>(b);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator+;
}

namespace qvm_detail {
template <int R, int C> struct plus_mm_defined;

template <> struct plus_mm_defined<1, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
        mat_traits<A>::cols == 3 && mat_traits<B>::cols == 3,
    deduce_mat2<A, B, 3, 3>>::type
operator-(A const &a, B const &b) {
  typedef typename deduce_mat2<A, B, 3, 3>::type R;
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows == 3);
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols == 3);
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) -
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<R>::template write_element<0, 1>(r) =
      mat_traits<A>::template read_element<0, 1>(a) -
      mat_traits<B>::template read_element<0, 1>(b);
  mat_traits<R>::template write_element<0, 2>(r) =
      mat_traits<A>::template read_element<0, 2>(a) -
      mat_traits<B>::template read_element<0, 2>(b);
  mat_traits<R>::template write_element<1, 0>(r) =
      mat_traits<A>::template read_element<1, 0>(a) -
      mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<R>::template write_element<1, 1>(r) =
      mat_traits<A>::template read_element<1, 1>(a) -
      mat_traits<B>::template read_element<1, 1>(b);
  mat_traits<R>::template write_element<1, 2>(r) =
      mat_traits<A>::template read_element<1, 2>(a) -
      mat_traits<B>::template read_element<1, 2>(b);
  mat_traits<R>::template write_element<2, 0>(r) =
      mat_traits<A>::template read_element<2, 0>(a) -
      mat_traits<B>::template read_element<2, 0>(b);
  mat_traits<R>::template write_element<2, 1>(r) =
      mat_traits<A>::template read_element<2, 1>(a) -
      mat_traits<B>::template read_element<2, 1>(b);
  mat_traits<R>::template write_element<2, 2>(r) =
      mat_traits<A>::template read_element<2, 2>(a) -
      mat_traits<B>::template read_element<2, 2>(b);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator-;
}

namespace qvm_detail {
template <int R, int C> struct minus_mm_defined;

template <> struct minus_mm_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
        mat_traits<A>::cols == 1 && mat_traits<B>::cols == 1,
    deduce_mat2<A, B, 3, 1>>::type
operator-(A const &a, B const &b) {
  typedef typename deduce_mat2<A, B, 3, 1>::type R;
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows == 3);
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols == 1);
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) -
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<R>::template write_element<1, 0>(r) =
      mat_traits<A>::template read_element<1, 0>(a) -
      mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<R>::template write_element<2, 0>(r) =
      mat_traits<A>::template read_element<2, 0>(a) -
      mat_traits<B>::template read_element<2, 0>(b);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator-;
}

namespace qvm_detail {
template <int R, int C> struct minus_mm_defined;

template <> struct minus_mm_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 1 && mat_traits<B>::rows == 1 &&
        mat_traits<A>::cols == 3 && mat_traits<B>::cols == 3,
    deduce_mat2<A, B, 1, 3>>::type
operator-(A const &a, B const &b) {
  typedef typename deduce_mat2<A, B, 1, 3>::type R;
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows == 1);
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols == 3);
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) -
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<R>::template write_element<0, 1>(r) =
      mat_traits<A>::template read_element<0, 1>(a) -
      mat_traits<B>::template read_element<0, 1>(b);
  mat_traits<R>::template write_element<0, 2>(r) =
      mat_traits<A>::template read_element<0, 2>(a) -
      mat_traits<B>::template read_element<0, 2>(b);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator-;
}

namespace qvm_detail {
template <int R, int C> struct minus_mm_defined;

template <> struct minus_mm_defined<1, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
                             mat_traits<A>::cols == 3 &&
                             mat_traits<B>::cols == 3,
                         A &>::type
    operator+=(A &a, B const &b) {
  mat_traits<A>::template write_element<0, 0>(a) +=
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<A>::template write_element<0, 1>(a) +=
      mat_traits<B>::template read_element<0, 1>(b);
  mat_traits<A>::template write_element<0, 2>(a) +=
      mat_traits<B>::template read_element<0, 2>(b);
  mat_traits<A>::template write_element<1, 0>(a) +=
      mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<A>::template write_element<1, 1>(a) +=
      mat_traits<B>::template read_element<1, 1>(b);
  mat_traits<A>::template write_element<1, 2>(a) +=
      mat_traits<B>::template read_element<1, 2>(b);
  mat_traits<A>::template write_element<2, 0>(a) +=
      mat_traits<B>::template read_element<2, 0>(b);
  mat_traits<A>::template write_element<2, 1>(a) +=
      mat_traits<B>::template read_element<2, 1>(b);
  mat_traits<A>::template write_element<2, 2>(a) +=
      mat_traits<B>::template read_element<2, 2>(b);
  return a;
}

namespace sfinae {
using ::boost::qvm::operator+=;
}

namespace qvm_detail {
template <int R, int C> struct plus_eq_mm_defined;

template <> struct plus_eq_mm_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
                             mat_traits<A>::cols == 1 &&
                             mat_traits<B>::cols == 1,
                         A &>::type
    operator+=(A &a, B const &b) {
  mat_traits<A>::template write_element<0, 0>(a) +=
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<A>::template write_element<1, 0>(a) +=
      mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<A>::template write_element<2, 0>(a) +=
      mat_traits<B>::template read_element<2, 0>(b);
  return a;
}

namespace sfinae {
using ::boost::qvm::operator+=;
}

namespace qvm_detail {
template <int R, int C> struct plus_eq_mm_defined;

template <> struct plus_eq_mm_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 1 && mat_traits<B>::rows == 1 &&
                             mat_traits<A>::cols == 3 &&
                             mat_traits<B>::cols == 3,
                         A &>::type
    operator+=(A &a, B const &b) {
  mat_traits<A>::template write_element<0, 0>(a) +=
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<A>::template write_element<0, 1>(a) +=
      mat_traits<B>::template read_element<0, 1>(b);
  mat_traits<A>::template write_element<0, 2>(a) +=
      mat_traits<B>::template read_element<0, 2>(b);
  return a;
}

namespace sfinae {
using ::boost::qvm::operator+=;
}

namespace qvm_detail {
template <int R, int C> struct plus_eq_mm_defined;

template <> struct plus_eq_mm_defined<1, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
                             mat_traits<A>::cols == 3 &&
                             mat_traits<B>::cols == 3,
                         A &>::type
    operator-=(A &a, B const &b) {
  mat_traits<A>::template write_element<0, 0>(a) -=
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<A>::template write_element<0, 1>(a) -=
      mat_traits<B>::template read_element<0, 1>(b);
  mat_traits<A>::template write_element<0, 2>(a) -=
      mat_traits<B>::template read_element<0, 2>(b);
  mat_traits<A>::template write_element<1, 0>(a) -=
      mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<A>::template write_element<1, 1>(a) -=
      mat_traits<B>::template read_element<1, 1>(b);
  mat_traits<A>::template write_element<1, 2>(a) -=
      mat_traits<B>::template read_element<1, 2>(b);
  mat_traits<A>::template write_element<2, 0>(a) -=
      mat_traits<B>::template read_element<2, 0>(b);
  mat_traits<A>::template write_element<2, 1>(a) -=
      mat_traits<B>::template read_element<2, 1>(b);
  mat_traits<A>::template write_element<2, 2>(a) -=
      mat_traits<B>::template read_element<2, 2>(b);
  return a;
}

namespace sfinae {
using ::boost::qvm::operator-=;
}

namespace qvm_detail {
template <int R, int C> struct minus_eq_mm_defined;

template <> struct minus_eq_mm_defined<3, 3> {
  static bool const value = true;
};
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
                             mat_traits<A>::cols == 1 &&
                             mat_traits<B>::cols == 1,
                         A &>::type
    operator-=(A &a, B const &b) {
  mat_traits<A>::template write_element<0, 0>(a) -=
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<A>::template write_element<1, 0>(a) -=
      mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<A>::template write_element<2, 0>(a) -=
      mat_traits<B>::template read_element<2, 0>(b);
  return a;
}

namespace sfinae {
using ::boost::qvm::operator-=;
}

namespace qvm_detail {
template <int R, int C> struct minus_eq_mm_defined;

template <> struct minus_eq_mm_defined<3, 1> {
  static bool const value = true;
};
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 1 && mat_traits<B>::rows == 1 &&
                             mat_traits<A>::cols == 3 &&
                             mat_traits<B>::cols == 3,
                         A &>::type
    operator-=(A &a, B const &b) {
  mat_traits<A>::template write_element<0, 0>(a) -=
      mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<A>::template write_element<0, 1>(a) -=
      mat_traits<B>::template read_element<0, 1>(b);
  mat_traits<A>::template write_element<0, 2>(a) -=
      mat_traits<B>::template read_element<0, 2>(b);
  return a;
}

namespace sfinae {
using ::boost::qvm::operator-=;
}

namespace qvm_detail {
template <int R, int C> struct minus_eq_mm_defined;

template <> struct minus_eq_mm_defined<1, 3> {
  static bool const value = true;
};
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<A>::cols == 3 && is_scalar<B>::value,
    deduce_mat<A>>::type
operator*(A const &a, B b) {
  typedef typename deduce_mat<A>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) * b;
  mat_traits<R>::template write_element<0, 1>(r) =
      mat_traits<A>::template read_element<0, 1>(a) * b;
  mat_traits<R>::template write_element<0, 2>(r) =
      mat_traits<A>::template read_element<0, 2>(a) * b;
  mat_traits<R>::template write_element<1, 0>(r) =
      mat_traits<A>::template read_element<1, 0>(a) * b;
  mat_traits<R>::template write_element<1, 1>(r) =
      mat_traits<A>::template read_element<1, 1>(a) * b;
  mat_traits<R>::template write_element<1, 2>(r) =
      mat_traits<A>::template read_element<1, 2>(a) * b;
  mat_traits<R>::template write_element<2, 0>(r) =
      mat_traits<A>::template read_element<2, 0>(a) * b;
  mat_traits<R>::template write_element<2, 1>(r) =
      mat_traits<A>::template read_element<2, 1>(a) * b;
  mat_traits<R>::template write_element<2, 2>(r) =
      mat_traits<A>::template read_element<2, 2>(a) * b;
  return r;
}

namespace sfinae {
using ::boost::qvm::operator*;
}

namespace qvm_detail {
template <int R, int C> struct mul_ms_defined;

template <> struct mul_ms_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename lazy_enable_if_c<is_scalar<A>::value && mat_traits<B>::rows == 3 &&
                                  mat_traits<B>::cols == 3,
                              deduce_mat<B>>::type
    operator*(A a, B const &b) {
  typedef typename deduce_mat<B>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      a * mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<R>::template write_element<0, 1>(r) =
      a * mat_traits<B>::template read_element<0, 1>(b);
  mat_traits<R>::template write_element<0, 2>(r) =
      a * mat_traits<B>::template read_element<0, 2>(b);
  mat_traits<R>::template write_element<1, 0>(r) =
      a * mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<R>::template write_element<1, 1>(r) =
      a * mat_traits<B>::template read_element<1, 1>(b);
  mat_traits<R>::template write_element<1, 2>(r) =
      a * mat_traits<B>::template read_element<1, 2>(b);
  mat_traits<R>::template write_element<2, 0>(r) =
      a * mat_traits<B>::template read_element<2, 0>(b);
  mat_traits<R>::template write_element<2, 1>(r) =
      a * mat_traits<B>::template read_element<2, 1>(b);
  mat_traits<R>::template write_element<2, 2>(r) =
      a * mat_traits<B>::template read_element<2, 2>(b);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator*;
}

namespace qvm_detail {
template <int R, int C> struct mul_sm_defined;

template <> struct mul_sm_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<A>::cols == 1 && is_scalar<B>::value,
    deduce_mat<A>>::type
operator*(A const &a, B b) {
  typedef typename deduce_mat<A>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) * b;
  mat_traits<R>::template write_element<1, 0>(r) =
      mat_traits<A>::template read_element<1, 0>(a) * b;
  mat_traits<R>::template write_element<2, 0>(r) =
      mat_traits<A>::template read_element<2, 0>(a) * b;
  return r;
}

namespace sfinae {
using ::boost::qvm::operator*;
}

namespace qvm_detail {
template <int R, int C> struct mul_ms_defined;

template <> struct mul_ms_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename lazy_enable_if_c<is_scalar<A>::value && mat_traits<B>::rows == 3 &&
                                  mat_traits<B>::cols == 1,
                              deduce_mat<B>>::type
    operator*(A a, B const &b) {
  typedef typename deduce_mat<B>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      a * mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<R>::template write_element<1, 0>(r) =
      a * mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<R>::template write_element<2, 0>(r) =
      a * mat_traits<B>::template read_element<2, 0>(b);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator*;
}

namespace qvm_detail {
template <int R, int C> struct mul_sm_defined;

template <> struct mul_sm_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 1 && mat_traits<A>::cols == 3 && is_scalar<B>::value,
    deduce_mat<A>>::type
operator*(A const &a, B b) {
  typedef typename deduce_mat<A>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) * b;
  mat_traits<R>::template write_element<0, 1>(r) =
      mat_traits<A>::template read_element<0, 1>(a) * b;
  mat_traits<R>::template write_element<0, 2>(r) =
      mat_traits<A>::template read_element<0, 2>(a) * b;
  return r;
}

namespace sfinae {
using ::boost::qvm::operator*;
}

namespace qvm_detail {
template <int R, int C> struct mul_ms_defined;

template <> struct mul_ms_defined<1, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename lazy_enable_if_c<is_scalar<A>::value && mat_traits<B>::rows == 1 &&
                                  mat_traits<B>::cols == 3,
                              deduce_mat<B>>::type
    operator*(A a, B const &b) {
  typedef typename deduce_mat<B>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      a * mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<R>::template write_element<0, 1>(r) =
      a * mat_traits<B>::template read_element<0, 1>(b);
  mat_traits<R>::template write_element<0, 2>(r) =
      a * mat_traits<B>::template read_element<0, 2>(b);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator*;
}

namespace qvm_detail {
template <int R, int C> struct mul_sm_defined;

template <> struct mul_sm_defined<1, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<A>::cols == 3 &&
                             is_scalar<B>::value,
                         A &>::type
    operator*=(A &a, B b) {
  mat_traits<A>::template write_element<0, 0>(a) *= b;
  mat_traits<A>::template write_element<0, 1>(a) *= b;
  mat_traits<A>::template write_element<0, 2>(a) *= b;
  mat_traits<A>::template write_element<1, 0>(a) *= b;
  mat_traits<A>::template write_element<1, 1>(a) *= b;
  mat_traits<A>::template write_element<1, 2>(a) *= b;
  mat_traits<A>::template write_element<2, 0>(a) *= b;
  mat_traits<A>::template write_element<2, 1>(a) *= b;
  mat_traits<A>::template write_element<2, 2>(a) *= b;
  return a;
}

namespace sfinae {
using ::boost::qvm::operator*=;
}

namespace qvm_detail {
template <int R, int C> struct mul_eq_ms_defined;

template <> struct mul_eq_ms_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<A>::cols == 1 &&
                             is_scalar<B>::value,
                         A &>::type
    operator*=(A &a, B b) {
  mat_traits<A>::template write_element<0, 0>(a) *= b;
  mat_traits<A>::template write_element<1, 0>(a) *= b;
  mat_traits<A>::template write_element<2, 0>(a) *= b;
  return a;
}

namespace sfinae {
using ::boost::qvm::operator*=;
}

namespace qvm_detail {
template <int R, int C> struct mul_eq_ms_defined;

template <> struct mul_eq_ms_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 1 && mat_traits<A>::cols == 3 &&
                             is_scalar<B>::value,
                         A &>::type
    operator*=(A &a, B b) {
  mat_traits<A>::template write_element<0, 0>(a) *= b;
  mat_traits<A>::template write_element<0, 1>(a) *= b;
  mat_traits<A>::template write_element<0, 2>(a) *= b;
  return a;
}

namespace sfinae {
using ::boost::qvm::operator*=;
}

namespace qvm_detail {
template <int R, int C> struct mul_eq_ms_defined;

template <> struct mul_eq_ms_defined<1, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<A>::cols == 3 && is_scalar<B>::value,
    deduce_mat<A>>::type
operator/(A const &a, B b) {
  typedef typename deduce_mat<A>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) / b;
  mat_traits<R>::template write_element<0, 1>(r) =
      mat_traits<A>::template read_element<0, 1>(a) / b;
  mat_traits<R>::template write_element<0, 2>(r) =
      mat_traits<A>::template read_element<0, 2>(a) / b;
  mat_traits<R>::template write_element<1, 0>(r) =
      mat_traits<A>::template read_element<1, 0>(a) / b;
  mat_traits<R>::template write_element<1, 1>(r) =
      mat_traits<A>::template read_element<1, 1>(a) / b;
  mat_traits<R>::template write_element<1, 2>(r) =
      mat_traits<A>::template read_element<1, 2>(a) / b;
  mat_traits<R>::template write_element<2, 0>(r) =
      mat_traits<A>::template read_element<2, 0>(a) / b;
  mat_traits<R>::template write_element<2, 1>(r) =
      mat_traits<A>::template read_element<2, 1>(a) / b;
  mat_traits<R>::template write_element<2, 2>(r) =
      mat_traits<A>::template read_element<2, 2>(a) / b;
  return r;
}

namespace sfinae {
using ::boost::qvm::operator/;
}

namespace qvm_detail {
template <int R, int C> struct div_ms_defined;

template <> struct div_ms_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename lazy_enable_if_c<is_scalar<A>::value && mat_traits<B>::rows == 3 &&
                                  mat_traits<B>::cols == 3,
                              deduce_mat<B>>::type
    operator/(A a, B const &b) {
  typedef typename deduce_mat<B>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      a / mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<R>::template write_element<0, 1>(r) =
      a / mat_traits<B>::template read_element<0, 1>(b);
  mat_traits<R>::template write_element<0, 2>(r) =
      a / mat_traits<B>::template read_element<0, 2>(b);
  mat_traits<R>::template write_element<1, 0>(r) =
      a / mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<R>::template write_element<1, 1>(r) =
      a / mat_traits<B>::template read_element<1, 1>(b);
  mat_traits<R>::template write_element<1, 2>(r) =
      a / mat_traits<B>::template read_element<1, 2>(b);
  mat_traits<R>::template write_element<2, 0>(r) =
      a / mat_traits<B>::template read_element<2, 0>(b);
  mat_traits<R>::template write_element<2, 1>(r) =
      a / mat_traits<B>::template read_element<2, 1>(b);
  mat_traits<R>::template write_element<2, 2>(r) =
      a / mat_traits<B>::template read_element<2, 2>(b);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator/;
}

namespace qvm_detail {
template <int R, int C> struct div_sm_defined;

template <> struct div_sm_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<A>::cols == 1 && is_scalar<B>::value,
    deduce_mat<A>>::type
operator/(A const &a, B b) {
  typedef typename deduce_mat<A>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) / b;
  mat_traits<R>::template write_element<1, 0>(r) =
      mat_traits<A>::template read_element<1, 0>(a) / b;
  mat_traits<R>::template write_element<2, 0>(r) =
      mat_traits<A>::template read_element<2, 0>(a) / b;
  return r;
}

namespace sfinae {
using ::boost::qvm::operator/;
}

namespace qvm_detail {
template <int R, int C> struct div_ms_defined;

template <> struct div_ms_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename lazy_enable_if_c<is_scalar<A>::value && mat_traits<B>::rows == 3 &&
                                  mat_traits<B>::cols == 1,
                              deduce_mat<B>>::type
    operator/(A a, B const &b) {
  typedef typename deduce_mat<B>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      a / mat_traits<B>::template read_element<0, 0>(b);
  mat_traits<R>::template write_element<1, 0>(r) =
      a / mat_traits<B>::template read_element<1, 0>(b);
  mat_traits<R>::template write_element<2, 0>(r) =
      a / mat_traits<B>::template read_element<2, 0>(b);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator/;
}

namespace qvm_detail {
template <int R, int C> struct div_sm_defined;

template <> struct div_sm_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 1 && mat_traits<A>::cols == 3 && is_scalar<B>::value,
    deduce_mat<A>>::type
operator/(A const &a, B b) {
  typedef typename deduce_mat<A>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a) / b;
  mat_traits<R>::template write_element<0, 1>(r) =
      mat_traits<A>::template read_element<0, 1>(a) / b;
  mat_traits<R>::template write_element<0, 2>(r) =
      mat_traits<A>::template read_element<0, 2>(a) / b;
  return r;
}

namespace sfinae {
using ::boost::qvm::operator/;
}

namespace qvm_detail {
template <int R, int C> struct div_ms_defined;

template <> struct div_ms_defined<1, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<A>::cols == 3 &&
                             is_scalar<B>::value,
                         A &>::type
    operator/=(A &a, B b) {
  mat_traits<A>::template write_element<0, 0>(a) /= b;
  mat_traits<A>::template write_element<0, 1>(a) /= b;
  mat_traits<A>::template write_element<0, 2>(a) /= b;
  mat_traits<A>::template write_element<1, 0>(a) /= b;
  mat_traits<A>::template write_element<1, 1>(a) /= b;
  mat_traits<A>::template write_element<1, 2>(a) /= b;
  mat_traits<A>::template write_element<2, 0>(a) /= b;
  mat_traits<A>::template write_element<2, 1>(a) /= b;
  mat_traits<A>::template write_element<2, 2>(a) /= b;
  return a;
}

namespace sfinae {
using ::boost::qvm::operator/=;
}

namespace qvm_detail {
template <int R, int C> struct div_eq_ms_defined;

template <> struct div_eq_ms_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<A>::cols == 1 &&
                             is_scalar<B>::value,
                         A &>::type
    operator/=(A &a, B b) {
  mat_traits<A>::template write_element<0, 0>(a) /= b;
  mat_traits<A>::template write_element<1, 0>(a) /= b;
  mat_traits<A>::template write_element<2, 0>(a) /= b;
  return a;
}

namespace sfinae {
using ::boost::qvm::operator/=;
}

namespace qvm_detail {
template <int R, int C> struct div_eq_ms_defined;

template <> struct div_eq_ms_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 1 && mat_traits<A>::cols == 3 &&
                             is_scalar<B>::value,
                         A &>::type
    operator/=(A &a, B b) {
  mat_traits<A>::template write_element<0, 0>(a) /= b;
  mat_traits<A>::template write_element<0, 1>(a) /= b;
  mat_traits<A>::template write_element<0, 2>(a) /= b;
  return a;
}

namespace sfinae {
using ::boost::qvm::operator/=;
}

namespace qvm_detail {
template <int R, int C> struct div_eq_ms_defined;

template <> struct div_eq_ms_defined<1, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class R, class A>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<R>::rows == 3 && mat_traits<A>::rows == 3 &&
                             mat_traits<R>::cols == 3 &&
                             mat_traits<A>::cols == 3,
                         R>::type
    convert_to(A const &a) {
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a);
  mat_traits<R>::template write_element<0, 1>(r) =
      mat_traits<A>::template read_element<0, 1>(a);
  mat_traits<R>::template write_element<0, 2>(r) =
      mat_traits<A>::template read_element<0, 2>(a);
  mat_traits<R>::template write_element<1, 0>(r) =
      mat_traits<A>::template read_element<1, 0>(a);
  mat_traits<R>::template write_element<1, 1>(r) =
      mat_traits<A>::template read_element<1, 1>(a);
  mat_traits<R>::template write_element<1, 2>(r) =
      mat_traits<A>::template read_element<1, 2>(a);
  mat_traits<R>::template write_element<2, 0>(r) =
      mat_traits<A>::template read_element<2, 0>(a);
  mat_traits<R>::template write_element<2, 1>(r) =
      mat_traits<A>::template read_element<2, 1>(a);
  mat_traits<R>::template write_element<2, 2>(r) =
      mat_traits<A>::template read_element<2, 2>(a);
  return r;
}

template <class R, class A>
BOOST_QVM_INLINE typename enable_if_c<is_mat<R>::value && is_quat<A>::value &&
                                          mat_traits<R>::rows == 3 &&
                                          mat_traits<R>::cols == 3,
                                      R>::type
convert_to(A const &q) {
  typedef typename mat_traits<R>::scalar_type T;
  T const a = quat_traits<A>::template read_element<0>(q);
  T const b = quat_traits<A>::template read_element<1>(q);
  T const c = quat_traits<A>::template read_element<2>(q);
  T const d = quat_traits<A>::template read_element<3>(q);
  T const bb = b * b;
  T const cc = c * c;
  T const dd = d * d;
  T const bc = b * c;
  T const bd = b * d;
  T const cd = c * d;
  T const ab = a * b;
  T const ac = a * c;
  T const ad = a * d;
  T const one = scalar_traits<T>::value(1);
  T const two = one + one;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) = one - two * (cc + dd);
  mat_traits<R>::template write_element<0, 1>(r) = two * (bc - ad);
  mat_traits<R>::template write_element<0, 2>(r) = two * (bd + ac);
  mat_traits<R>::template write_element<1, 0>(r) = two * (bc + ad);
  mat_traits<R>::template write_element<1, 1>(r) = one - two * (bb + dd);
  mat_traits<R>::template write_element<1, 2>(r) = two * (cd - ab);
  mat_traits<R>::template write_element<2, 0>(r) = two * (bd - ac);
  mat_traits<R>::template write_element<2, 1>(r) = two * (cd + ab);
  mat_traits<R>::template write_element<2, 2>(r) = one - two * (bb + cc);
  return r;
}

namespace sfinae {
using ::boost::qvm::convert_to;
}

namespace qvm_detail {
template <int R, int C> struct convert_to_m_defined;

template <> struct convert_to_m_defined<3, 3> {
  static bool const value = true;
};
} // namespace qvm_detail

template <class R, class A>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<R>::rows == 3 && mat_traits<A>::rows == 3 &&
                             mat_traits<R>::cols == 1 &&
                             mat_traits<A>::cols == 1,
                         R>::type
    convert_to(A const &a) {
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a);
  mat_traits<R>::template write_element<1, 0>(r) =
      mat_traits<A>::template read_element<1, 0>(a);
  mat_traits<R>::template write_element<2, 0>(r) =
      mat_traits<A>::template read_element<2, 0>(a);
  return r;
}

namespace sfinae {
using ::boost::qvm::convert_to;
}

namespace qvm_detail {
template <int R, int C> struct convert_to_m_defined;

template <> struct convert_to_m_defined<3, 1> {
  static bool const value = true;
};
} // namespace qvm_detail

template <class R, class A>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<R>::rows == 1 && mat_traits<A>::rows == 1 &&
                             mat_traits<R>::cols == 3 &&
                             mat_traits<A>::cols == 3,
                         R>::type
    convert_to(A const &a) {
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      mat_traits<A>::template read_element<0, 0>(a);
  mat_traits<R>::template write_element<0, 1>(r) =
      mat_traits<A>::template read_element<0, 1>(a);
  mat_traits<R>::template write_element<0, 2>(r) =
      mat_traits<A>::template read_element<0, 2>(a);
  return r;
}

namespace sfinae {
using ::boost::qvm::convert_to;
}

namespace qvm_detail {
template <int R, int C> struct convert_to_m_defined;

template <> struct convert_to_m_defined<1, 3> {
  static bool const value = true;
};
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
                             mat_traits<A>::cols == 3 &&
                             mat_traits<B>::cols == 3,
                         bool>::type
    operator==(A const &a, B const &b) {
  return mat_traits<A>::template read_element<0, 0>(a) ==
             mat_traits<B>::template read_element<0, 0>(b) &&
         mat_traits<A>::template read_element<0, 1>(a) ==
             mat_traits<B>::template read_element<0, 1>(b) &&
         mat_traits<A>::template read_element<0, 2>(a) ==
             mat_traits<B>::template read_element<0, 2>(b) &&
         mat_traits<A>::template read_element<1, 0>(a) ==
             mat_traits<B>::template read_element<1, 0>(b) &&
         mat_traits<A>::template read_element<1, 1>(a) ==
             mat_traits<B>::template read_element<1, 1>(b) &&
         mat_traits<A>::template read_element<1, 2>(a) ==
             mat_traits<B>::template read_element<1, 2>(b) &&
         mat_traits<A>::template read_element<2, 0>(a) ==
             mat_traits<B>::template read_element<2, 0>(b) &&
         mat_traits<A>::template read_element<2, 1>(a) ==
             mat_traits<B>::template read_element<2, 1>(b) &&
         mat_traits<A>::template read_element<2, 2>(a) ==
             mat_traits<B>::template read_element<2, 2>(b);
}

namespace sfinae {
using ::boost::qvm::operator==;
}

namespace qvm_detail {
template <int R, int C> struct eq_mm_defined;

template <> struct eq_mm_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
                             mat_traits<A>::cols == 1 &&
                             mat_traits<B>::cols == 1,
                         bool>::type
    operator==(A const &a, B const &b) {
  return mat_traits<A>::template read_element<0, 0>(a) ==
             mat_traits<B>::template read_element<0, 0>(b) &&
         mat_traits<A>::template read_element<1, 0>(a) ==
             mat_traits<B>::template read_element<1, 0>(b) &&
         mat_traits<A>::template read_element<2, 0>(a) ==
             mat_traits<B>::template read_element<2, 0>(b);
}

namespace sfinae {
using ::boost::qvm::operator==;
}

namespace qvm_detail {
template <int R, int C> struct eq_mm_defined;

template <> struct eq_mm_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 1 && mat_traits<B>::rows == 1 &&
                             mat_traits<A>::cols == 3 &&
                             mat_traits<B>::cols == 3,
                         bool>::type
    operator==(A const &a, B const &b) {
  return mat_traits<A>::template read_element<0, 0>(a) ==
             mat_traits<B>::template read_element<0, 0>(b) &&
         mat_traits<A>::template read_element<0, 1>(a) ==
             mat_traits<B>::template read_element<0, 1>(b) &&
         mat_traits<A>::template read_element<0, 2>(a) ==
             mat_traits<B>::template read_element<0, 2>(b);
}

namespace sfinae {
using ::boost::qvm::operator==;
}

namespace qvm_detail {
template <int R, int C> struct eq_mm_defined;

template <> struct eq_mm_defined<1, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
                             mat_traits<A>::cols == 3 &&
                             mat_traits<B>::cols == 3,
                         bool>::type
    operator!=(A const &a, B const &b) {
  return !(mat_traits<A>::template read_element<0, 0>(a) ==
           mat_traits<B>::template read_element<0, 0>(b)) ||
         !(mat_traits<A>::template read_element<0, 1>(a) ==
           mat_traits<B>::template read_element<0, 1>(b)) ||
         !(mat_traits<A>::template read_element<0, 2>(a) ==
           mat_traits<B>::template read_element<0, 2>(b)) ||
         !(mat_traits<A>::template read_element<1, 0>(a) ==
           mat_traits<B>::template read_element<1, 0>(b)) ||
         !(mat_traits<A>::template read_element<1, 1>(a) ==
           mat_traits<B>::template read_element<1, 1>(b)) ||
         !(mat_traits<A>::template read_element<1, 2>(a) ==
           mat_traits<B>::template read_element<1, 2>(b)) ||
         !(mat_traits<A>::template read_element<2, 0>(a) ==
           mat_traits<B>::template read_element<2, 0>(b)) ||
         !(mat_traits<A>::template read_element<2, 1>(a) ==
           mat_traits<B>::template read_element<2, 1>(b)) ||
         !(mat_traits<A>::template read_element<2, 2>(a) ==
           mat_traits<B>::template read_element<2, 2>(b));
}

namespace sfinae {
using ::boost::qvm::operator!=;
}

namespace qvm_detail {
template <int R, int C> struct neq_mm_defined;

template <> struct neq_mm_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
                             mat_traits<A>::cols == 1 &&
                             mat_traits<B>::cols == 1,
                         bool>::type
    operator!=(A const &a, B const &b) {
  return !(mat_traits<A>::template read_element<0, 0>(a) ==
           mat_traits<B>::template read_element<0, 0>(b)) ||
         !(mat_traits<A>::template read_element<1, 0>(a) ==
           mat_traits<B>::template read_element<1, 0>(b)) ||
         !(mat_traits<A>::template read_element<2, 0>(a) ==
           mat_traits<B>::template read_element<2, 0>(b));
}

namespace sfinae {
using ::boost::qvm::operator!=;
}

namespace qvm_detail {
template <int R, int C> struct neq_mm_defined;

template <> struct neq_mm_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 1 && mat_traits<B>::rows == 1 &&
                             mat_traits<A>::cols == 3 &&
                             mat_traits<B>::cols == 3,
                         bool>::type
    operator!=(A const &a, B const &b) {
  return !(mat_traits<A>::template read_element<0, 0>(a) ==
           mat_traits<B>::template read_element<0, 0>(b)) ||
         !(mat_traits<A>::template read_element<0, 1>(a) ==
           mat_traits<B>::template read_element<0, 1>(b)) ||
         !(mat_traits<A>::template read_element<0, 2>(a) ==
           mat_traits<B>::template read_element<0, 2>(b));
}

namespace sfinae {
using ::boost::qvm::operator!=;
}

namespace qvm_detail {
template <int R, int C> struct neq_mm_defined;

template <> struct neq_mm_defined<1, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<A>::cols == 3, deduce_mat<A>>::type
operator-(A const &a) {
  typedef typename deduce_mat<A>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      -mat_traits<A>::template read_element<0, 0>(a);
  mat_traits<R>::template write_element<0, 1>(r) =
      -mat_traits<A>::template read_element<0, 1>(a);
  mat_traits<R>::template write_element<0, 2>(r) =
      -mat_traits<A>::template read_element<0, 2>(a);
  mat_traits<R>::template write_element<1, 0>(r) =
      -mat_traits<A>::template read_element<1, 0>(a);
  mat_traits<R>::template write_element<1, 1>(r) =
      -mat_traits<A>::template read_element<1, 1>(a);
  mat_traits<R>::template write_element<1, 2>(r) =
      -mat_traits<A>::template read_element<1, 2>(a);
  mat_traits<R>::template write_element<2, 0>(r) =
      -mat_traits<A>::template read_element<2, 0>(a);
  mat_traits<R>::template write_element<2, 1>(r) =
      -mat_traits<A>::template read_element<2, 1>(a);
  mat_traits<R>::template write_element<2, 2>(r) =
      -mat_traits<A>::template read_element<2, 2>(a);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator-;
}

namespace qvm_detail {
template <int R, int C> struct minus_m_defined;

template <> struct minus_m_defined<3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<A>::cols == 1, deduce_mat<A>>::type
operator-(A const &a) {
  typedef typename deduce_mat<A>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      -mat_traits<A>::template read_element<0, 0>(a);
  mat_traits<R>::template write_element<1, 0>(r) =
      -mat_traits<A>::template read_element<1, 0>(a);
  mat_traits<R>::template write_element<2, 0>(r) =
      -mat_traits<A>::template read_element<2, 0>(a);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator-;
}

namespace qvm_detail {
template <int R, int C> struct minus_m_defined;

template <> struct minus_m_defined<3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 1 && mat_traits<A>::cols == 3, deduce_mat<A>>::type
operator-(A const &a) {
  typedef typename deduce_mat<A>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      -mat_traits<A>::template read_element<0, 0>(a);
  mat_traits<R>::template write_element<0, 1>(r) =
      -mat_traits<A>::template read_element<0, 1>(a);
  mat_traits<R>::template write_element<0, 2>(r) =
      -mat_traits<A>::template read_element<0, 2>(a);
  return r;
}

namespace sfinae {
using ::boost::qvm::operator-;
}

namespace qvm_detail {
template <int R, int C> struct minus_m_defined;

template <> struct minus_m_defined<1, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<A>::cols == 3,
                         typename mat_traits<A>::scalar_type>::type
    determinant(A const &a) {
  typedef typename mat_traits<A>::scalar_type T;
  T const a00 = mat_traits<A>::template read_element<0, 0>(a);
  T const a01 = mat_traits<A>::template read_element<0, 1>(a);
  T const a02 = mat_traits<A>::template read_element<0, 2>(a);
  T const a10 = mat_traits<A>::template read_element<1, 0>(a);
  T const a11 = mat_traits<A>::template read_element<1, 1>(a);
  T const a12 = mat_traits<A>::template read_element<1, 2>(a);
  T const a20 = mat_traits<A>::template read_element<2, 0>(a);
  T const a21 = mat_traits<A>::template read_element<2, 1>(a);
  T const a22 = mat_traits<A>::template read_element<2, 2>(a);
  T det = (a00 * (a11 * a22 - a12 * a21) - a01 * (a10 * a22 - a12 * a20) +
           a02 * (a10 * a21 - a11 * a20));
  return det;
}

namespace sfinae {
using ::boost::qvm::determinant;
}

namespace qvm_detail {
template <int D> struct determinant_defined;

template <> struct determinant_defined<3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<A>::cols == 3 && is_scalar<B>::value,
    deduce_mat<A>>::type
inverse(A const &a, B det) {
  typedef typename mat_traits<A>::scalar_type T;
  BOOST_QVM_ASSERT(det != scalar_traits<B>::value(0));
  T const a00 = mat_traits<A>::template read_element<0, 0>(a);
  T const a01 = mat_traits<A>::template read_element<0, 1>(a);
  T const a02 = mat_traits<A>::template read_element<0, 2>(a);
  T const a10 = mat_traits<A>::template read_element<1, 0>(a);
  T const a11 = mat_traits<A>::template read_element<1, 1>(a);
  T const a12 = mat_traits<A>::template read_element<1, 2>(a);
  T const a20 = mat_traits<A>::template read_element<2, 0>(a);
  T const a21 = mat_traits<A>::template read_element<2, 1>(a);
  T const a22 = mat_traits<A>::template read_element<2, 2>(a);
  T const f = scalar_traits<T>::value(1) / det;
  typedef typename deduce_mat<A>::type R;
  R r;
  mat_traits<R>::template write_element<0, 0>(r) = f * (a11 * a22 - a12 * a21);
  mat_traits<R>::template write_element<0, 1>(r) = -f * (a01 * a22 - a02 * a21);
  mat_traits<R>::template write_element<0, 2>(r) = f * (a01 * a12 - a02 * a11);
  mat_traits<R>::template write_element<1, 0>(r) = -f * (a10 * a22 - a12 * a20);
  mat_traits<R>::template write_element<1, 1>(r) = f * (a00 * a22 - a02 * a20);
  mat_traits<R>::template write_element<1, 2>(r) = -f * (a00 * a12 - a02 * a10);
  mat_traits<R>::template write_element<2, 0>(r) = f * (a10 * a21 - a11 * a20);
  mat_traits<R>::template write_element<2, 1>(r) = -f * (a00 * a21 - a01 * a20);
  mat_traits<R>::template write_element<2, 2>(r) = f * (a00 * a11 - a01 * a10);
  return r;
}

template <class A>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<A>::cols == 3, deduce_mat<A>>::type
inverse(A const &a) {
  typedef typename mat_traits<A>::scalar_type T;
  T det = determinant(a);
  if (det == scalar_traits<T>::value(0))
    BOOST_QVM_THROW_EXCEPTION(zero_determinant_error());
  return inverse(a, det);
}

namespace sfinae {
using ::boost::qvm::inverse;
}

namespace qvm_detail {
template <int D> struct inverse_m_defined;

template <> struct inverse_m_defined<3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
        mat_traits<A>::cols == 3 && mat_traits<B>::cols == 3,
    deduce_mat2<A, B, 3, 3>>::type
operator*(A const &a, B const &b) {
  typedef typename mat_traits<A>::scalar_type Ta;
  typedef typename mat_traits<B>::scalar_type Tb;
  Ta const a00 = mat_traits<A>::template read_element<0, 0>(a);
  Ta const a01 = mat_traits<A>::template read_element<0, 1>(a);
  Ta const a02 = mat_traits<A>::template read_element<0, 2>(a);
  Ta const a10 = mat_traits<A>::template read_element<1, 0>(a);
  Ta const a11 = mat_traits<A>::template read_element<1, 1>(a);
  Ta const a12 = mat_traits<A>::template read_element<1, 2>(a);
  Ta const a20 = mat_traits<A>::template read_element<2, 0>(a);
  Ta const a21 = mat_traits<A>::template read_element<2, 1>(a);
  Ta const a22 = mat_traits<A>::template read_element<2, 2>(a);
  Tb const b00 = mat_traits<B>::template read_element<0, 0>(b);
  Tb const b01 = mat_traits<B>::template read_element<0, 1>(b);
  Tb const b02 = mat_traits<B>::template read_element<0, 2>(b);
  Tb const b10 = mat_traits<B>::template read_element<1, 0>(b);
  Tb const b11 = mat_traits<B>::template read_element<1, 1>(b);
  Tb const b12 = mat_traits<B>::template read_element<1, 2>(b);
  Tb const b20 = mat_traits<B>::template read_element<2, 0>(b);
  Tb const b21 = mat_traits<B>::template read_element<2, 1>(b);
  Tb const b22 = mat_traits<B>::template read_element<2, 2>(b);
  typedef typename deduce_mat2<A, B, 3, 3>::type R;
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows == 3);
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols == 3);
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      a00 * b00 + a01 * b10 + a02 * b20;
  mat_traits<R>::template write_element<0, 1>(r) =
      a00 * b01 + a01 * b11 + a02 * b21;
  mat_traits<R>::template write_element<0, 2>(r) =
      a00 * b02 + a01 * b12 + a02 * b22;
  mat_traits<R>::template write_element<1, 0>(r) =
      a10 * b00 + a11 * b10 + a12 * b20;
  mat_traits<R>::template write_element<1, 1>(r) =
      a10 * b01 + a11 * b11 + a12 * b21;
  mat_traits<R>::template write_element<1, 2>(r) =
      a10 * b02 + a11 * b12 + a12 * b22;
  mat_traits<R>::template write_element<2, 0>(r) =
      a20 * b00 + a21 * b10 + a22 * b20;
  mat_traits<R>::template write_element<2, 1>(r) =
      a20 * b01 + a21 * b11 + a22 * b21;
  mat_traits<R>::template write_element<2, 2>(r) =
      a20 * b02 + a21 * b12 + a22 * b22;
  return r;
}

namespace sfinae {
using ::boost::qvm::operator*;
}

namespace qvm_detail {
template <int R, int CR, int C> struct mul_mm_defined;

template <> struct mul_mm_defined<3, 3, 3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS
    typename enable_if_c<mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
                             mat_traits<A>::cols == 3 &&
                             mat_traits<B>::cols == 3,
                         A &>::type
    operator*=(A &a, B const &b) {
  typedef typename mat_traits<A>::scalar_type Ta;
  typedef typename mat_traits<B>::scalar_type Tb;
  Ta const a00 = mat_traits<A>::template read_element<0, 0>(a);
  Ta const a01 = mat_traits<A>::template read_element<0, 1>(a);
  Ta const a02 = mat_traits<A>::template read_element<0, 2>(a);
  Ta const a10 = mat_traits<A>::template read_element<1, 0>(a);
  Ta const a11 = mat_traits<A>::template read_element<1, 1>(a);
  Ta const a12 = mat_traits<A>::template read_element<1, 2>(a);
  Ta const a20 = mat_traits<A>::template read_element<2, 0>(a);
  Ta const a21 = mat_traits<A>::template read_element<2, 1>(a);
  Ta const a22 = mat_traits<A>::template read_element<2, 2>(a);
  Tb const b00 = mat_traits<B>::template read_element<0, 0>(b);
  Tb const b01 = mat_traits<B>::template read_element<0, 1>(b);
  Tb const b02 = mat_traits<B>::template read_element<0, 2>(b);
  Tb const b10 = mat_traits<B>::template read_element<1, 0>(b);
  Tb const b11 = mat_traits<B>::template read_element<1, 1>(b);
  Tb const b12 = mat_traits<B>::template read_element<1, 2>(b);
  Tb const b20 = mat_traits<B>::template read_element<2, 0>(b);
  Tb const b21 = mat_traits<B>::template read_element<2, 1>(b);
  Tb const b22 = mat_traits<B>::template read_element<2, 2>(b);
  mat_traits<A>::template write_element<0, 0>(a) =
      a00 * b00 + a01 * b10 + a02 * b20;
  mat_traits<A>::template write_element<0, 1>(a) =
      a00 * b01 + a01 * b11 + a02 * b21;
  mat_traits<A>::template write_element<0, 2>(a) =
      a00 * b02 + a01 * b12 + a02 * b22;
  mat_traits<A>::template write_element<1, 0>(a) =
      a10 * b00 + a11 * b10 + a12 * b20;
  mat_traits<A>::template write_element<1, 1>(a) =
      a10 * b01 + a11 * b11 + a12 * b21;
  mat_traits<A>::template write_element<1, 2>(a) =
      a10 * b02 + a11 * b12 + a12 * b22;
  mat_traits<A>::template write_element<2, 0>(a) =
      a20 * b00 + a21 * b10 + a22 * b20;
  mat_traits<A>::template write_element<2, 1>(a) =
      a20 * b01 + a21 * b11 + a22 * b21;
  mat_traits<A>::template write_element<2, 2>(a) =
      a20 * b02 + a21 * b12 + a22 * b22;
  return a;
}

namespace sfinae {
using ::boost::qvm::operator*=;
}

namespace qvm_detail {
template <int D> struct mul_eq_mm_defined;

template <> struct mul_eq_mm_defined<3> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 3 && mat_traits<B>::rows == 3 &&
        mat_traits<A>::cols == 3 && mat_traits<B>::cols == 1,
    deduce_mat2<A, B, 3, 1>>::type
operator*(A const &a, B const &b) {
  typedef typename mat_traits<A>::scalar_type Ta;
  typedef typename mat_traits<B>::scalar_type Tb;
  Ta const a00 = mat_traits<A>::template read_element<0, 0>(a);
  Ta const a01 = mat_traits<A>::template read_element<0, 1>(a);
  Ta const a02 = mat_traits<A>::template read_element<0, 2>(a);
  Ta const a10 = mat_traits<A>::template read_element<1, 0>(a);
  Ta const a11 = mat_traits<A>::template read_element<1, 1>(a);
  Ta const a12 = mat_traits<A>::template read_element<1, 2>(a);
  Ta const a20 = mat_traits<A>::template read_element<2, 0>(a);
  Ta const a21 = mat_traits<A>::template read_element<2, 1>(a);
  Ta const a22 = mat_traits<A>::template read_element<2, 2>(a);
  Tb const b00 = mat_traits<B>::template read_element<0, 0>(b);
  Tb const b10 = mat_traits<B>::template read_element<1, 0>(b);
  Tb const b20 = mat_traits<B>::template read_element<2, 0>(b);
  typedef typename deduce_mat2<A, B, 3, 1>::type R;
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows == 3);
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols == 1);
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      a00 * b00 + a01 * b10 + a02 * b20;
  mat_traits<R>::template write_element<1, 0>(r) =
      a10 * b00 + a11 * b10 + a12 * b20;
  mat_traits<R>::template write_element<2, 0>(r) =
      a20 * b00 + a21 * b10 + a22 * b20;
  return r;
}

namespace sfinae {
using ::boost::qvm::operator*;
}

namespace qvm_detail {
template <int R, int CR, int C> struct mul_mm_defined;

template <> struct mul_mm_defined<3, 3, 1> { static bool const value = true; };
} // namespace qvm_detail

template <class A, class B>
BOOST_QVM_INLINE_OPERATIONS typename lazy_enable_if_c<
    mat_traits<A>::rows == 1 && mat_traits<B>::rows == 3 &&
        mat_traits<A>::cols == 3 && mat_traits<B>::cols == 3,
    deduce_mat2<A, B, 1, 3>>::type
operator*(A const &a, B const &b) {
  typedef typename mat_traits<A>::scalar_type Ta;
  typedef typename mat_traits<B>::scalar_type Tb;
  Ta const a00 = mat_traits<A>::template read_element<0, 0>(a);
  Ta const a01 = mat_traits<A>::template read_element<0, 1>(a);
  Ta const a02 = mat_traits<A>::template read_element<0, 2>(a);
  Tb const b00 = mat_traits<B>::template read_element<0, 0>(b);
  Tb const b01 = mat_traits<B>::template read_element<0, 1>(b);
  Tb const b02 = mat_traits<B>::template read_element<0, 2>(b);
  Tb const b10 = mat_traits<B>::template read_element<1, 0>(b);
  Tb const b11 = mat_traits<B>::template read_element<1, 1>(b);
  Tb const b12 = mat_traits<B>::template read_element<1, 2>(b);
  Tb const b20 = mat_traits<B>::template read_element<2, 0>(b);
  Tb const b21 = mat_traits<B>::template read_element<2, 1>(b);
  Tb const b22 = mat_traits<B>::template read_element<2, 2>(b);
  typedef typename deduce_mat2<A, B, 1, 3>::type R;
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows == 1);
  BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols == 3);
  R r;
  mat_traits<R>::template write_element<0, 0>(r) =
      a00 * b00 + a01 * b10 + a02 * b20;
  mat_traits<R>::template write_element<0, 1>(r) =
      a00 * b01 + a01 * b11 + a02 * b21;
  mat_traits<R>::template write_element<0, 2>(r) =
      a00 * b02 + a01 * b12 + a02 * b22;
  return r;
}

namespace sfinae {
using ::boost::qvm::operator*;
}

namespace qvm_detail {
template <int R, int CR, int C> struct mul_mm_defined;

template <> struct mul_mm_defined<1, 3, 3> { static bool const value = true; };
} // namespace qvm_detail

} // namespace qvm
} // namespace boost

#endif