//Copyright (c) 2008-2016 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_2923BE84E16CD6AE529049F1F1BBE9EB
#define BOOST_QVM_2923BE84E16CD6AE529049F1F1BBE9EB

//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/enable_if.hpp>
#include <boost/qvm/error.hpp>
#include <boost/qvm/inline.hpp>
#include <boost/qvm/mat_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==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==2 && mat_traits<B>::cols==2,
            deduce_mat2<A,B,2,2> >::type
        operator+( A const & a, B const & b )
            {
            typedef typename deduce_mat2<A,B,2,2>::type R;
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows==2);
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols==2);
            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<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);
            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<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==1 && mat_traits<B>::cols==1,
            deduce_mat2<A,B,2,1> >::type
        operator+( A const & a, B const & b )
            {
            typedef typename deduce_mat2<A,B,2,1>::type R;
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows==2);
            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);
            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<2,1>
                {
                static bool const value=true;
                };
            }

        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==2 && mat_traits<B>::cols==2,
            deduce_mat2<A,B,1,2> >::type
        operator+( A const & a, B const & b )
            {
            typedef typename deduce_mat2<A,B,1,2>::type R;
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows==1);
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols==2);
            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);
            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,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==2 && mat_traits<B>::cols==2,
            deduce_mat2<A,B,2,2> >::type
        operator-( A const & a, B const & b )
            {
            typedef typename deduce_mat2<A,B,2,2>::type R;
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows==2);
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols==2);
            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<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);
            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<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==1 && mat_traits<B>::cols==1,
            deduce_mat2<A,B,2,1> >::type
        operator-( A const & a, B const & b )
            {
            typedef typename deduce_mat2<A,B,2,1>::type R;
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows==2);
            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);
            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<2,1>
                {
                static bool const value=true;
                };
            }

        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==2 && mat_traits<B>::cols==2,
            deduce_mat2<A,B,1,2> >::type
        operator-( A const & a, B const & b )
            {
            typedef typename deduce_mat2<A,B,1,2>::type R;
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows==1);
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols==2);
            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);
            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,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==2 && mat_traits<B>::cols==2,
            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<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);
            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<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            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);
            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<2,1>
                {
                static bool const value=true;
                };
            }

        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==2 && mat_traits<B>::cols==2,
            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);
            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,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==2 && mat_traits<B>::cols==2,
            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<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);
            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<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            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);
            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<2,1>
                {
                static bool const value=true;
                };
            }

        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==2 && mat_traits<B>::cols==2,
            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);
            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,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<A>::cols==2 && 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<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;
            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<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && 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;
            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<2,1>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==1 && mat_traits<A>::cols==2 && 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;
            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,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<A>::cols==2 && 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<1,0>(a)*=b;
            mat_traits<A>::template write_element<1,1>(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<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && 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;
            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<2,1>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==1 && mat_traits<A>::cols==2 && 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;
            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,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<A>::cols==2 && 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<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;
            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<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && 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;
            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<2,1>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==1 && mat_traits<A>::cols==2 && 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;
            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,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<A>::cols==2 && 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<1,0>(a)/=b;
            mat_traits<A>::template write_element<1,1>(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<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && 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;
            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<2,1>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==1 && mat_traits<A>::cols==2 && 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;
            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,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==2 && mat_traits<B>::cols==2,
            A &>::type
        assign( 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<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);
            return a;
            }

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

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

            template <>
            struct
            assign_mm_defined<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==1 && mat_traits<B>::cols==1,
            A &>::type
        assign( 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);
            return a;
            }

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

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

            template <>
            struct
            assign_mm_defined<2,1>
                {
                static bool const value=true;
                };
            }

        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==2 && mat_traits<B>::cols==2,
            A &>::type
        assign( 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);
            return a;
            }

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

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

            template <>
            struct
            assign_mm_defined<1,2>
                {
                static bool const value=true;
                };
            }

        template <class R,class A>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<R>::rows==2 && mat_traits<A>::rows==2 &&
            mat_traits<R>::cols==2 && mat_traits<A>::cols==2,
            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<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);
            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<2,2>
                {
                static bool const value=true;
                };
            }

        template <class R,class A>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<R>::rows==2 && mat_traits<A>::rows==2 &&
            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);
            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<2,1>
                {
                static bool const value=true;
                };
            }

        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==2 && mat_traits<A>::cols==2,
            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);
            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,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==2 && mat_traits<B>::cols==2,
            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<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);
            }

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

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

            template <>
            struct
            eq_mm_defined<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            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);
            }

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

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

            template <>
            struct
            eq_mm_defined<2,1>
                {
                static bool const value=true;
                };
            }

        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==2 && mat_traits<B>::cols==2,
            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);
            }

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

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

            template <>
            struct
            eq_mm_defined<1,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==2 && mat_traits<B>::cols==2,
            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<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));
            }

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

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

            template <>
            struct
            neq_mm_defined<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            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));
            }

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

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

            template <>
            struct
            neq_mm_defined<2,1>
                {
                static bool const value=true;
                };
            }

        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==2 && mat_traits<B>::cols==2,
            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));
            }

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

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

            template <>
            struct
            neq_mm_defined<1,2>
                {
                static bool const value=true;
                };
            }

        template <class A>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<A>::cols==2,
            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<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);
            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<2,2>
                {
                static bool const value=true;
                };
            }

        template <class A>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && 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);
            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<2,1>
                {
                static bool const value=true;
                };
            }

        template <class A>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==1 && mat_traits<A>::cols==2,
            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);
            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,2>
                {
                static bool const value=true;
                };
            }

        template <class A>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<A>::cols==2,
            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 a10=mat_traits<A>::template read_element<1,0>(a);
            T const a11=mat_traits<A>::template read_element<1,1>(a);
            T det=(a00*a11-a01*a10);
            return det;
            }

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

        namespace
        qvm_detail
            {
            template <int D>
            struct determinant_defined;

            template <>
            struct
            determinant_defined<2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<A>::cols==2 && 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 a10=mat_traits<A>::template read_element<1,0>(a);
            T const a11=mat_traits<A>::template read_element<1,1>(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;
            mat_traits<R>::template write_element<0,1>(r)=-f*a01;
            mat_traits<R>::template write_element<1,0>(r)=-f*a10;
            mat_traits<R>::template write_element<1,1>(r)= f*a00;
            return r;
            }

        template <class A>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<A>::cols==2,
            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<2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==2 && mat_traits<B>::cols==2,
            deduce_mat2<A,B,2,2> >::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 a10 = mat_traits<A>::template read_element<1,0>(a);
            Ta const a11 = mat_traits<A>::template read_element<1,1>(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 b10 = mat_traits<B>::template read_element<1,0>(b);
            Tb const b11 = mat_traits<B>::template read_element<1,1>(b);
            typedef typename deduce_mat2<A,B,2,2>::type R;
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows==2);
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols==2);
            R r;
            mat_traits<R>::template write_element<0,0>(r)=a00*b00+a01*b10;
            mat_traits<R>::template write_element<0,1>(r)=a00*b01+a01*b11;
            mat_traits<R>::template write_element<1,0>(r)=a10*b00+a11*b10;
            mat_traits<R>::template write_element<1,1>(r)=a10*b01+a11*b11;
            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<2,2,2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==2 && mat_traits<B>::cols==2,
            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 a10 = mat_traits<A>::template read_element<1,0>(a);
            Ta const a11 = mat_traits<A>::template read_element<1,1>(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 b10 = mat_traits<B>::template read_element<1,0>(b);
            Tb const b11 = mat_traits<B>::template read_element<1,1>(b);
            mat_traits<A>::template write_element<0,0>(a)=a00*b00+a01*b10;
            mat_traits<A>::template write_element<0,1>(a)=a00*b01+a01*b11;
            mat_traits<A>::template write_element<1,0>(a)=a10*b00+a11*b10;
            mat_traits<A>::template write_element<1,1>(a)=a10*b01+a11*b11;
            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<2>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==2 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==2 && mat_traits<B>::cols==1,
            deduce_mat2<A,B,2,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 a10 = mat_traits<A>::template read_element<1,0>(a);
            Ta const a11 = mat_traits<A>::template read_element<1,1>(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);
            typedef typename deduce_mat2<A,B,2,1>::type R;
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows==2);
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols==1);
            R r;
            mat_traits<R>::template write_element<0,0>(r)=a00*b00+a01*b10;
            mat_traits<R>::template write_element<1,0>(r)=a10*b00+a11*b10;
            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<2,2,1>
                {
                static bool const value=true;
                };
            }

        template <class A,class B>
        BOOST_QVM_INLINE_OPERATIONS
        typename lazy_enable_if_c<
            mat_traits<A>::rows==1 && mat_traits<B>::rows==2 &&
            mat_traits<A>::cols==2 && mat_traits<B>::cols==2,
            deduce_mat2<A,B,1,2> >::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);
            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 b10 = mat_traits<B>::template read_element<1,0>(b);
            Tb const b11 = mat_traits<B>::template read_element<1,1>(b);
            typedef typename deduce_mat2<A,B,1,2>::type R;
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows==1);
            BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols==2);
            R r;
            mat_traits<R>::template write_element<0,0>(r)=a00*b00+a01*b10;
            mat_traits<R>::template write_element<0,1>(r)=a00*b01+a01*b11;
            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,2,2>
                {
                static bool const value=true;
                };
            }

        }
    }

#endif


//==========================================================================
//  This file is part of HMMlib.
//
//  HMMlib is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as
//  published by the Free Software Foundation, either version 3 of
//  the License, or (at your option) any later version.
//
//  HMMlib is distributed in the hope that it will be useful, but
//  WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with HMMlib. If not, see
//  <http://www.gnu.org/licenses/>.
//
//  Copyright (C) 2010  Bioinformatics Research Centre, Aarhus University.
//  Author: Andreas Sand (asand@birc.au.dk)
//==========================================================================

#ifndef HMM_HPP
#define HMM_HPP

#include <boost/shared_ptr.hpp>

#include <vector>
#include <cmath>
#include <assert.h>
using namespace std;

#include "HMMlib/hmm_matrix.hpp"
#include "HMMlib/hmm_vector.hpp"
#include "HMMlib/sse_operator_traits.hpp"

#include <pmmintrin.h>

#ifdef WITH_OMP
#include <omp.h>
#endif

typedef std::vector<unsigned int> sequence;

namespace hmmlib {

  /**
   * \class HMM
   *
   * \brief Encapsulates the representation of a Hidden Markov
   * Model.
   *
   * This class encapsulates the representation of a Hidden Markov
   * Model. A hidden Markov model is composed by a transition matrix,
   * an emission matrix and a vector of initial state probabilities.
   * Five classical HMM algorithms are supported: Viterbi, forward,
   * backward, Baum-Welch and posterior decoding.  The algorithms are
   * optimized using the SSE instruction set and parallelized using
   * OpenMP as described in <a
   * href="http://birc.au.dk/~asand/papers/HiBi10_hmmlib.pdf">HMMlib:
   * A C++ Library for General Hidden Markov Models Exploiting Modern
   * CPUs</a>.
   *
   * Possible values of \a float_type are: \a double or \a float.
   *
   * Possible values of sse_float_type are: \a double, \a float, \a
   * __m128d or __m128. If \a float_type is \a double, sse_float_type
   * must be either \a __m128d (default) or \a double. If \a
   * float_type is \a float, sse_float_type must be either \a __m128
   * (default) or \a float.
   *
   *
   * \author Andreas Sand (asand@birc.au.dk)
   * \see http://birc.au.dk/~asand/papers/HiBi10_hmmlib.pdf
   */
  template < typename float_type,
	     typename sse_float_type = typename FloatTraits<float_type>::sse_type>
  class HMM {

    typedef SSEOperatorTraits<float_type, sse_float_type> sse_operations_traits;

    boost::shared_ptr< HMMVector<float_type, sse_float_type> > initial_prob;
    boost::shared_ptr< HMMMatrix<float_type, sse_float_type> > trans_prob;
    boost::shared_ptr< HMMMatrix<float_type, sse_float_type> > emission_prob;

    const int no_states;
    const int alphabet_size;

  public :
    /**
     * \brief Constructs an HMM with the indicated initial state
     * probabilities, transition probabilities and emission
     * probabilities.
     *
     * Constructs an HMM with the indicated initial state
     * probabilities, transition probabilities and emission
     * probabilities.
     *
     * \param initial_prob   The vector of initial state probabilities, in which the \f$i\f$th entry must be the probability of the model initially being in state i. The entries in \a initial_prob must sum to 1.
     * \param trans_prob     The matrix of transition probabilities, in which the \f$i,j\f$th entry must be the probability of the transition from state i to state j. Each row in T must sum to 1.
     * \param emission_prob  The matrix of emission probabilities, in which the \f$i,j\f$th entry must be the probabilities of state \f$j\f$ emitting the \f$i\f$th alphabet symbol. Each column must sum to 1.
     *
     * Possible values of \a float_type are: \a double or \a float.
     *
     * Possible values of sse_float_type are: \a double, \a float, \a
     * __m128d or __m128. If \a float_type is \a double, sse_float_type
     * must be either \a __m128d (default) or \a double. If \a
     * float_type is \a float, sse_float_type must be either \a __m128
     * (default) or \a float.
     *
     * \pre The size of \a initial_prob must match the number of rows in \a trans_prob, the number of columns in trans_prob and the number columns in emission_prob.
     */
    HMM(boost::shared_ptr< HMMVector<float_type, sse_float_type> > initial_prob,
	boost::shared_ptr< HMMMatrix<float_type, sse_float_type> > trans_prob,
	boost::shared_ptr< HMMMatrix<float_type, sse_float_type> > emission_prob);

    // field accessors and mutators
    const HMMVector<float_type,sse_float_type> &get_initial_probs() { return *initial_prob; }
    const HMMMatrix<float_type,sse_float_type> &get_trans_probs() { return *trans_prob; }
    const HMMMatrix<float_type,sse_float_type> &get_emission_probs() { return *emission_prob; }
    const int get_no_states() const { return no_states; }
    const int get_alphabet_size() const { return alphabet_size; }

    // algorithms
    /**
     * \brief Runs the forward dynamic programming algorithm.
     *
     * Fills in the forward dynamic programming table, \a F.
     *
     * \param obsseq    The observed sequence of emissions.
     * \param F         The forward dynamic programming table (to be filled in).
     * \param scales    The scaling factors (to be filled in).
     *
     * After running this function, the \f$i,j\f$th entry in \a F will be:
     * \f[f_{ij} = c_i e_{s_i,j} \sum_l f_{i-1,l}t_{l, j},\f]
     * and the \f$i\f$th entry in \a scales, \f$c_i\f$, will be the factor normalizing
     * \f$ \sum_j e_{s_i,j}  \sum_l f_{i-1,l}t_{l, j}\f$.
     *
     * \pre The number of states in the model must match the number of columns in \a F.
     * \pre The number of observations in \a obsseq must match the number of rows in \a F and the size of \a scales.
     */
    void forward(const sequence &obsseq,
		 HMMVector<float_type, sse_float_type> &scales,
		 HMMMatrix<float_type, sse_float_type> &F);

    /**
     * \brief Runs the backward dynamic programming algorithm.
     *
     * Fills in the backward dynamic programming table, \a B.
     *
     * \param obsseq    The observed sequence of emissions.
     * \param B         The backward dynamic programming table (to be filled in).
     * \param scales    The scaling factors from the forward algorithm.
     *
     * After running this function, the \f$i,j\f$th entry in \a B will be:
     * \f[b_{ij} = c_{i+1}\sum_l e_{s_{i+1},l} t_{jl} b_{i+1,l}.\f]
     *
     * \pre The number of states in the model must match the number of columns in \a B.
     * \pre The number of observations in \a obsseq must match the number of rows in \a B and the size of \a scales.
     * \pre \a scales must have been filled out using \a forward().
     */
    void backward(const sequence &obsseq,
		  const HMMVector<float_type, sse_float_type> &scales,
		  HMMMatrix<float_type, sse_float_type> &B);

    /**
     * \brief Computes the loglikelihood of the model generating an observed sequence.
     *
     * Computes the loglikelihood of the model generating an observed sequence, based on the vector of scaling factors computed by the \a forward() function.
     *
     * \param scales    The vector of scaling factors computed by the \a forward() function.
     * \returns         The loglikelihood.
     *
     * \pre \a scales must have been filled out using \a forward().
     */
    float_type likelihood(const HMMVector<float_type, sse_float_type> &scales);

    /**
     * \brief Computes \f$\Theta=(pi_{counts}, T_{counts}, E_{counts})\f$ such that
     * \f$P(obsseq \vert \Theta)\f$ is maximized.
     *
     * Computes \f$\Theta=(pi_{counts}, T_{counts}, E_{counts})\f$ such that
     * \f$P(obsseq \vert \Theta)\f$ is maximized.
     *
     * \param obsseq    The observed sequence of emissions.
     * \param F         The forward dynamic programming table computed by \a forward().
     * \param B         The backward dynamic programming table table computed by \a backward().
     * \param scales    The scalling factors from the forward algorithms computed by \a forward().
     * \param pi_counts The estimated initial state probability vector (to be filled in).
     * \param T_counts  The estimated transition matrix (to be filled in).
     * \param E_counts  The estimated emission matrix (to be filled in).
     *
     * \pre \a F, \a scales and \a B must have been filled in using the \a forward() and \a backward() functions.
     * \pre The number of states in the model must match the number of columns in \a F and \a B.
     * \pre The number of observations in \a obsseq must match the number of rows in \a F and \a B and the size of \a scales.
     * \pre The number of states in the model must match the size of \a pi_counts, the number of rows in \a T_counts, the number of columns in \a T_counts and the number of columns in \a E_counts.
     * \pre The alphabet size of the model must match the number of rows in \a E_counts.
     */
    void baum_welch(const sequence &obsseq,
		    const HMMMatrix<float_type, sse_float_type> &F,
		    const HMMMatrix<float_type, sse_float_type> &B,
		    const HMMVector<float_type, sse_float_type> &scales,
		    HMMVector<float_type, sse_float_type> &pi_counts,
		    HMMMatrix<float_type, sse_float_type> &T_counts,
		    HMMMatrix<float_type, sse_float_type> &E_counts);

    /**
     * \brief Computes the maximum likelihood sequence of hidden states.
     *
     * Computes the maximum likelihood sequence of hidden states.
     *
     * \param obsseq    The observed sequence of emissions.
     * \param hiddenseq The sequence of hidden states (to be filled in).
     *
     * \returns         The loglikelihood of the probability of \a hiddenseq, given that \a obsseq is emitted.
     *
     * \a hiddenseq is filled in using the Viterbi algorithm (computing in log space).
     *
     * \pre The size of hiddenseq must match the size of obsseq.
     */
    float_type viterbi(const sequence &obsseq,
		       sequence &hiddenseq);

    /**
     * \brief Computes the posterior decoding of \a obsseq.
     *
     * Computes the posterior decoding of \a obsseq.
     *
     * \param obsseq    The observed sequence of emissions.
     * \param F         The forward dynamic programming table computed by \a forward().
     * \param B         The backward dynamic programming table computed by \a backward().
     * \param scales    The vector of scaling factors computed by \a forward().
     * \param post      The matrix of posterior decoding probabilities (to be filled in).
     *
     * After running this function the \f$i,j\f$th entry in \a post
     * will be the probability with which the ith observed symbol in
     * obsseq was emitted by the the jth state:
     * \f[P(z_i = j \vert s) = \frac{f_{ij}b_{ij}}{c_i}.\f]
     *
     * \pre \a F, \a B and \a scales must have been fill in using the \a forward() and \a backward() functions.
     * \pre the number of rows in \a post must match the size of obsseq, and the number of columns in \a post must match the number of states in the model.
     */
    void posterior_decoding(const sequence &obsseq,
			    const HMMMatrix<float_type, sse_float_type> &F,
			    const HMMMatrix<float_type, sse_float_type> &B,
			    const HMMVector<float_type, sse_float_type> &scales,
			    HMMMatrix<float_type, sse_float_type> &post);

    friend class AllocatorTraits<float_type, sse_float_type>;
    friend class OperatorTraits<float_type, sse_float_type>;
  };

  // ###############################################
  // ########## Implementation comes here ##########
  // ###############################################
  template <typename float_type, typename sse_float_type>
  HMM<float_type,sse_float_type>::HMM(boost::shared_ptr< HMMVector<float_type, sse_float_type> > initial_prob,
				       boost::shared_ptr< HMMMatrix<float_type, sse_float_type> > trans_prob,
				       boost::shared_ptr< HMMMatrix<float_type, sse_float_type> > emission_prob)
    :
    initial_prob(initial_prob),
    trans_prob(trans_prob),
    emission_prob(emission_prob),
    no_states(initial_prob->get_size()),
    alphabet_size(emission_prob->get_no_rows()) {
    assert(trans_prob->get_no_columns() == no_states);
    assert(trans_prob->get_no_rows() == no_states);
    assert(emission_prob->get_no_columns() == no_states);
    assert(emission_prob->get_no_rows() == alphabet_size);
  }

  template <typename float_type, typename sse_float_type>
  void
  HMM<float_type, sse_float_type>::forward(const sequence &obsseq,
					    HMMVector<float_type, sse_float_type> &scales,
					    HMMMatrix<float_type, sse_float_type> &F) {
    // just making it a bit easier on ourselves...
    const HMMVector<float_type, sse_float_type> &pi = *initial_prob;
    const HMMMatrix<float_type, sse_float_type> &T = *trans_prob;
    const HMMMatrix<float_type, sse_float_type> &E = *emission_prob;

    HMMMatrix<float_type, sse_float_type> &T_t = *(new HMMMatrix<float_type, sse_float_type>(T.get_no_columns(), T.get_no_rows()));
    T.transpose(T_t);

    const int length = obsseq.size();
    const int no_chunks = F.get_no_chunks_per_row();

    // sanity check
    assert(F.get_no_columns() == no_states);
    assert(F.get_no_rows() == length);
    assert(scales.get_size() == length);

    // nice to have
    sse_float_type ones;
    sse_operations_traits::set_all(ones, (float_type) 1.0);

    // initialise
    unsigned int x = obsseq[0];
    sse_float_type scale;
    sse_operations_traits::set_all(scale, (float_type) 0.0);
    int sc;
    for(sc = 0; sc < no_chunks; ++sc) {
      sse_float_type temp = pi.get_chunk(sc) * E.get_chunk(x,sc);
      F.get_chunk(0, sc) = temp;
      scale += temp;
    }

    float_type scale_float = 0;
    sse_operations_traits::sum(scale);
    scale_float = 1 / *((float_type *) &scale);
    scales(0) = scale_float;

    // scale first column of F
    sse_operations_traits::set_all(scale, scale_float);
    int chunk;
    for(chunk = 0; chunk < no_chunks; ++chunk)
      F.get_chunk(0,chunk) *= scale;

    for(int i = 1; i < length; ++i) { // filling in i'th row of F: F(i, -)
      x = obsseq[i];
      #ifdef WITH_OMP
      #pragma omp parallel for
      #endif
      for(int j = 0; j < no_states; ++j) { // filling in F(i,j)
	sse_float_type prob_sum;
	sse_operations_traits::set_all(prob_sum, (float_type) 0.0);
	for(int c = 0; c < no_chunks; ++c)
	  prob_sum += F.get_chunk(i-1, c) * T_t.get_chunk(j, c);
	sse_operations_traits::sum(prob_sum);
	sse_operations_traits::store(F(i,j), prob_sum);
      }

      sse_operations_traits::set_all(scale, (float_type) 0.0);
      for(int c = 0; c < no_chunks; ++c) {
	F.get_chunk(i, c) *= E.get_chunk(x,c);
	scale += F.get_chunk(i, c);
      }
      sse_operations_traits::sum(scale);
      sse_operations_traits::store(scales(i), scale);

      // normalize i'th column
      scales(i) = 1.0 / scales(i);
      sse_operations_traits::set_all(scale, scales(i));
      for(int chunk = 0; chunk < no_chunks; ++chunk)
	F.get_chunk(i,chunk) *= scale;
    }
  }

  template <typename float_type, typename sse_float_type>
  void
  HMM<float_type, sse_float_type>::backward(const sequence &obsseq,
					     const HMMVector<float_type, sse_float_type> &scales,
					     HMMMatrix<float_type, sse_float_type> &B) {
    // just making it a little easier on ourselves...
    const HMMMatrix<float_type, sse_float_type> &T = *trans_prob;
    const HMMMatrix<float_type, sse_float_type> &E = *emission_prob;

    // boost::shared_ptr<HMMMatrix<float_type, sse_float_type> > T_t_ptr(new HMMMatrix<float_type, sse_float_type>(T.get_no_columns(), T.get_no_rows()));
    // HMMMatrix<float_type, sse_float_type> &T_t = *T_t_ptr;
    // T.transpose(T_t);

    const int length = obsseq.size();
    const int no_chunks = B.get_no_chunks_per_row();

    // sanity check
    assert(B.get_no_columns() == no_states);
    assert(B.get_no_rows() == length);
    assert(scales.get_size() == length);

    // Fill in the last column of B
    sse_float_type scale;
    sse_operations_traits::set_all(scale, scales(length - 1));
    int sc;
    for(sc = 0; sc < no_chunks; ++sc)
      B.get_chunk(length-1,sc) = scale;

    // Recursion
    for(int i = length - 1; i > 0; --i) { // fill in the (i-1)'th row of B
      int x = obsseq[i];
      #ifdef WITH_OMP
      #pragma omp parallel for
      #endif
      for(int s = 0; s < no_states; ++s) { // fill in B(i-1,s)
	sse_float_type prob_sum;
	sse_operations_traits::set_all(prob_sum, 0.0);

	for(int chunk = 0; chunk < no_chunks; ++chunk)
	  prob_sum += T.get_chunk(s, chunk) * E.get_chunk(x, chunk) * B.get_chunk(i, chunk);

	sse_operations_traits::sum(prob_sum);
	float_type sum;
	sse_operations_traits::store(sum, prob_sum);
	B(i-1,s) = sum * scales(i-1);
      }
    }
  }

  template <typename float_type, typename sse_float_type>
  float_type
  HMM<float_type, sse_float_type>::likelihood(const HMMVector<float_type, sse_float_type> &scales) {
    float_type likelihood = 0.0;
    for (int s = 0; s < scales.get_size(); ++s) {
      likelihood -= std::log(scales(s));
    }
    return likelihood;
  }

  template <typename float_type, typename sse_float_type>
  void
  HMM<float_type, sse_float_type>::baum_welch(const sequence &obsseq,
					       const HMMMatrix<float_type, sse_float_type> &F,
					       const HMMMatrix<float_type, sse_float_type> &B,
					       const HMMVector<float_type, sse_float_type> &scales,
					       HMMVector<float_type, sse_float_type> &new_pi,
					       HMMMatrix<float_type, sse_float_type> &new_T,
					       HMMMatrix<float_type, sse_float_type> &new_E) {

    // easier reference
    HMMMatrix<float_type, sse_float_type> &T = *trans_prob;
    HMMMatrix<float_type, sse_float_type> &E = *emission_prob;

    const int length = obsseq.size();
    const int no_chunks = B.get_no_chunks_per_row();

    // sanity check
    assert(new_T.get_no_columns() == no_states);
    assert(new_T.get_no_rows() ==  no_states);
    assert(new_E.get_no_columns() == no_states);
    assert(new_E.get_no_rows() == alphabet_size);
    assert(new_pi.get_size() == no_states);
    assert(scales.get_size() == length);
    assert(F.get_no_rows() == length);
    assert(F.get_no_columns() == no_states);
    assert(B.get_no_rows() == length);
    assert(B.get_no_columns() == no_states);

    // initialise
    new_pi.reset();
    new_T.reset();
    new_E.reset();

    // compute counts
    HMMVector<float_type, sse_float_type> pi_counts(no_states);
    HMMMatrix<float_type, sse_float_type> T_counts(no_states, no_states);
    HMMMatrix<float_type, sse_float_type> E_counts(alphabet_size, no_states);

    // compute new_pi and initialize E_counts
    unsigned x = obsseq[0];
    sse_float_type scale;
    sse_operations_traits::set_all(scale, (float_type) (1.0/scales(0)));
    for (int sc = 0; sc < no_chunks; ++sc) {
      sse_float_type temp = (F.get_chunk(0,sc) * B.get_chunk(0,sc)) * scale;
      new_pi.get_chunk(sc) = temp;
      E_counts.get_chunk(x, sc) += temp;
    }

    // compute transition and emission counts
    for (int i = 1; i < length; ++i) {
      x = obsseq[i];
      #ifdef WITH_OMP
      #pragma omp parallel for
      #endif
      for(int j = 0; j < no_states; ++j) {
	// transition counts
	sse_float_type prev_forward;
	sse_operations_traits::set_all(prev_forward, F(i-1, j));
	for(int chunk = 0; chunk < no_chunks; ++chunk) {
	  T_counts.get_chunk(j, chunk) += prev_forward *
	    T.get_chunk(j,chunk) *
	    E.get_chunk(x,chunk) *
	    B.get_chunk(i,chunk);
	}
      }
      // emission counts
      sse_operations_traits::set_all(scale, (float_type) (1.0/scales(i)));
      for(int chunk = 0; chunk < no_chunks; ++chunk)
	E_counts.get_chunk(x,chunk) += F.get_chunk(i, chunk) *
	  B.get_chunk(i, chunk) * scale;
    }

    // compute new_T and new_E by normalizing counts
    for (int s = 0; s < no_states; ++s) {
      // transition probabilities
      float_type sum = 0.0;
      for (int dst_state = 0; dst_state < no_states; ++dst_state)
        sum += T_counts(s, dst_state);
      for (int dst_state = 0; dst_state < no_states; ++dst_state)
        new_T(s, dst_state) = T_counts(s, dst_state) / sum;

      // emission probabilities
      sum = 0.0;
      for (int sym = 0; sym < alphabet_size; ++sym)
        sum += E_counts(sym, s);
      for (int sym = 0; sym < alphabet_size; ++sym)
        new_E(sym, s) = E_counts(sym, s) / sum;
    }
  }

  template <typename float_type, typename sse_float_type>
  float_type
  HMM<float_type, sse_float_type>::viterbi(const sequence &obsseq, sequence &hiddenseq) {
    // just making it a bit easier on ourselves...
    const HMMVector<float_type, sse_float_type> &pi = *initial_prob;
    const HMMMatrix<float_type, sse_float_type> &T = *trans_prob;
    const HMMMatrix<float_type, sse_float_type> &E = *emission_prob;

    // sanity check
    assert(obsseq.size() == hiddenseq.size());

    // Set up internal data structures
    const int length = obsseq.size();

    HMMMatrix<float_type, sse_float_type> path_probs(length, no_states);

    const int no_chunks = path_probs.get_no_chunks_per_row();

    boost::shared_ptr<HMMMatrix<float_type, sse_float_type> > T_t_ptr(new HMMMatrix<float_type, sse_float_type>(T.get_no_columns(), T.get_no_rows()));
    boost::shared_ptr<HMMMatrix<float_type, sse_float_type> > T_t_log_ptr(new HMMMatrix<float_type, sse_float_type>(T.get_no_rows(), T.get_no_columns()));
    HMMMatrix<float_type, sse_float_type> &T_t_log = *T_t_log_ptr;

    boost::shared_ptr<HMMMatrix<float_type, sse_float_type> > E_log_ptr(new HMMMatrix<float_type, sse_float_type>(E.get_no_rows(), E.get_no_columns()));
    HMMMatrix<float_type, sse_float_type> &E_log = *E_log_ptr;

    // construct T_t_log and E_log
    T.transpose(*T_t_ptr);
    (*T_t_ptr).log(T_t_log);
    E.log(E_log);

    // Fill first column of path_probs
    int x = obsseq[0];
    for (int i = 0; i < no_states; ++i) {
      path_probs(0, i) = std::log(pi(i)) + E_log(x, i);
    }

    // Recursion
    for(int i = 1; i < length; ++i) { // fill ith row of path_probs
      x = obsseq[i];
      #ifdef WITH_OMP
      #pragma omp parallel for
      #endif
      for(int s = 0; s < no_states; ++s) { // fill path_probs(i,s)
	sse_float_type max, tmp;
	sse_operations_traits::set_all(max, -INFINITY);
	for(int c = 0; c < no_chunks - 1; ++c) { // no_chunks-1 since we don't want to take the max of some garbage.
	  tmp = path_probs.get_chunk(i-1, c) + T_t_log.get_chunk(s, c);
	  sse_operations_traits::max(max, tmp);
	}
	float_type float_max = sse_operations_traits::hmax(max);
	int floats_per_chunk = sizeof(sse_float_type) / sizeof(float_type);
	for(int c = (no_chunks - 1) * floats_per_chunk; c < path_probs.get_no_columns(); ++c)
	  float_max = std::max(float_max, path_probs(i-1,c) + T_t_log(s,c));
	path_probs(i, s) = float_max;
      }

      for(int c = 0; c < no_chunks; ++c) {
	path_probs.get_chunk(i,c) += E_log.get_chunk(x,c);
      }
    }

    // Backtracking - final row
    float_type loglikelihood = -INFINITY;
    int hidden_state = 0;
    for(int i = 0; i < no_states; ++i) {
      if (path_probs(length - 1, i) > loglikelihood) {
	loglikelihood = path_probs(length - 1, i);
	hidden_state = i;
      }
    }
    hiddenseq[length - 1] = hidden_state;

    // Backtracking - recursion
    for (unsigned i = length - 1; i > 0; --i) {
      float_type max = -INFINITY;
      float_type tmp;
      int maxidx = 0;
      for (int s = 0; s < no_states; ++s) {
	tmp = path_probs(i-1, s) + T_t_log(hidden_state, s);
	if(tmp > max) {
	  max = tmp;
	  maxidx = s;
	}
      }
      hidden_state = maxidx;
      hiddenseq[i-1] = hidden_state;
    }

    return loglikelihood;
  }

  template <typename float_type, typename sse_float_type>
  void
  HMM<float_type, sse_float_type>::posterior_decoding(const sequence &obsseq,
						       const HMMMatrix<float_type, sse_float_type> &F,
						       const HMMMatrix<float_type, sse_float_type> &B,
						       const HMMVector<float_type, sse_float_type> &scales,
						       HMMMatrix<float_type, sse_float_type> &post) {
    const int length = obsseq.size();
    const int no_chunks = F.get_no_chunks_per_row();
    #ifdef WITH_OMP
    #pragma omp parallel for
    #endif
    for (int i = 0; i < length; ++i) {
      sse_float_type scale;
      sse_operations_traits::set_all(scale, (float_type) (1.0/scales(i)));
      for (int chunk = 0; chunk < no_chunks; ++chunk) {
	post.get_chunk(i, chunk) = F.get_chunk(i, chunk) * B.get_chunk(i, chunk) * scale;
      }
    }
  }

} // end of namespace

#endif


#ifndef STAN_MATH_REV_MAT_FUN_MULTIPLY_HPP
#define STAN_MATH_REV_MAT_FUN_MULTIPLY_HPP

#include <stan/math/prim/mat/fun/Eigen.hpp>
#include <stan/math/prim/mat/fun/typedefs.hpp>
#include <stan/math/rev/scal/fun/value_of_rec.hpp>
#include <stan/math/rev/scal/fun/value_of.hpp>
#include <stan/math/rev/mat/fun/to_var.hpp>
#include <stan/math/rev/core.hpp>
#include <stan/math/prim/mat/fun/value_of_rec.hpp>
#include <stan/math/prim/mat/err/check_multiplicable.hpp>
#include <stan/math/prim/scal/err/check_not_nan.hpp>
#include <boost/math/tools/promotion.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>

namespace stan {
  namespace math {

    /**
     * This is a subclass of the vari class for matrix
     * multiplication A * B where A is N by M and B
     * is M by K.
     *
     * The class stores the structure of each matrix,
     * the double values of A and B, and pointers to
     * the varis for A and B if A or B is a var. It
     * also instantiates and stores pointers to
     * varis for all elements of A * B.
     *
     * @tparam TA Scalar type for matrix A
     * @tparam RA Rows for matrix A
     * @tparam CA Columns for matrix A, Rows for matrix B
     * @tparam TB Scalar type for matrix B
     * @tparam CB Columns for matrix B
     */
    template <typename TA, int RA, int CA, typename TB, int CB>
    class multiply_mat_vari : public vari {
    public:
      int A_rows_, A_cols_, B_cols_, A_size_, B_size_;
      double* Ad_;
      double* Bd_;
      vari** variRefA_;
      vari** variRefB_;
      vari** variRefAB_;

      /**
       * Constructor for multiply_mat_vari.
       *
       * All memory allocated in
       * ChainableStack's stack_alloc arena.
       *
       * It is critical for the efficiency of this object
       * that the constructor create new varis that aren't
       * popped onto the var_stack_, but rather are
       * popped onto the var_nochain_stack_. This is
       * controlled to the second argument to
       * vari's constructor.
       *
       * @param A matrix
       * @param B matrix
       */
      multiply_mat_vari(const Eigen::Matrix<TA, RA, CA>& A,
                        const Eigen::Matrix<TB, CA, CB>& B)
        : vari(0.0),
          A_rows_(A.rows()), A_cols_(A.cols()),
          B_cols_(B.cols()), A_size_(A.size()), B_size_(B.size()),
          Ad_(ChainableStack::memalloc_.alloc_array<double>(A_size_)),
          Bd_(ChainableStack::memalloc_.alloc_array<double>(B_size_)),
          variRefA_(ChainableStack::memalloc_.alloc_array<vari*>(A_size_)),
          variRefB_(ChainableStack::memalloc_.alloc_array<vari*>(B_size_)),
          variRefAB_(ChainableStack::memalloc_.alloc_array<vari*>(A_rows_
                                                                  * B_cols_)) {
        using Eigen::Map;
        using Eigen::MatrixXd;
        for (size_type i = 0; i < A.size(); ++i) {
          variRefA_[i] = A.coeffRef(i).vi_;
          Ad_[i] = A.coeffRef(i).val();
        }
        for (size_type i = 0; i < B.size(); ++i) {
          variRefB_[i] = B.coeffRef(i).vi_;
          Bd_[i] = B.coeffRef(i).val();
        }
        MatrixXd AB
          = Map<MatrixXd>(Ad_, A_rows_, A_cols_)
          * Map<MatrixXd>(Bd_, A_cols_, B_cols_);
        for (size_type i = 0; i < AB.size(); ++i)
          variRefAB_[i] = new vari(AB.coeffRef(i), false);
      }

      virtual void chain() {
        using Eigen::MatrixXd;
        using Eigen::Map;
        MatrixXd adjAB(A_rows_, B_cols_);
        MatrixXd adjA(A_rows_, A_cols_);
        MatrixXd adjB(A_cols_, B_cols_);

        for (size_type i = 0; i < adjAB.size(); ++i)
          adjAB(i) = variRefAB_[i]->adj_;
        adjA = adjAB
          * Map<MatrixXd>(Bd_, A_cols_, B_cols_).transpose();
        adjB = Map<MatrixXd>(Ad_, A_rows_, A_cols_).transpose()
          * adjAB;
        for (size_type i = 0; i < A_size_; ++i)
          variRefA_[i]->adj_ += adjA(i);
        for (size_type i = 0; i < B_size_; ++i)
          variRefB_[i]->adj_ += adjB(i);
      }
    };

    /**
     * This is a subclass of the vari class for matrix
     * multiplication A * B where A is 1 by M and B
     * is M by 1.
     *
     * The class stores the structure of each matrix,
     * the double values of A and B, and pointers to
     * the varis for A and B if A or B is a var. It
     * also instantiates and stores pointers to
     * varis for all elements of A * B.
     *
     * @tparam TA Scalar type for matrix A
     * @tparam CA Columns for matrix A, Rows for matrix B
     * @tparam TB Scalar type for matrix B
     */
    template <typename TA, int CA, typename TB>
    class multiply_mat_vari<TA, 1, CA, TB, 1> : public vari {
    public:
      int size_;
      double* Ad_;
      double* Bd_;
      vari** variRefA_;
      vari** variRefB_;
      vari* variRefAB_;

      /**
       * Constructor for multiply_mat_vari.
       *
       * All memory allocated in
       * ChainableStack's stack_alloc arena.
       *
       * It is critical for the efficiency of this object
       * that the constructor create new varis that aren't
       * popped onto the var_stack_, but rather are
       * popped onto the var_nochain_stack_. This is
       * controlled to the second argument to
       * vari's constructor.
       *
       * @param A row vector
       * @param B vector
       */
      multiply_mat_vari(const Eigen::Matrix<TA, 1, CA>& A,
                        const Eigen::Matrix<TB, CA, 1>& B)
        : vari(0.0),
          size_(A.cols()),
          Ad_(ChainableStack::memalloc_.alloc_array<double>(size_)),
          Bd_(ChainableStack::memalloc_.alloc_array<double>(size_)),
          variRefA_(ChainableStack::memalloc_.alloc_array<vari*>(size_)),
          variRefB_(ChainableStack::memalloc_.alloc_array<vari*>(size_)) {
        using Eigen::Map;
        using Eigen::VectorXd;
        using Eigen::RowVectorXd;
        for (size_type i = 0; i < size_; ++i) {
          variRefA_[i] = A.coeffRef(i).vi_;
          Ad_[i] = A.coeffRef(i).val();
        }
        for (size_type i = 0; i < size_; ++i) {
          variRefB_[i] = B.coeffRef(i).vi_;
          Bd_[i] = B.coeffRef(i).val();
        }
        double AB = Map<RowVectorXd>(Ad_, 1, size_)
          * Map<VectorXd>(Bd_, size_, 1);
        variRefAB_ = new vari(AB, false);
      }

      virtual void chain() {
        using Eigen::VectorXd;
        using Eigen::RowVectorXd;
        using Eigen::Map;
        double adjAB;
        RowVectorXd adjA(size_);
        VectorXd adjB(size_);

        adjAB = variRefAB_->adj_;
        adjA = adjAB
          * Map<VectorXd>(Bd_, size_, 1).transpose();
        adjB = Map<RowVectorXd>(Ad_, 1, size_).transpose()
          * adjAB;
        for (size_type i = 0; i < size_; ++i)
          variRefA_[i]->adj_ += adjA(i);
        for (size_type i = 0; i < size_; ++i)
          variRefB_[i]->adj_ += adjB(i);
      }
    };

    /**
     * This is a subclass of the vari class for matrix
     * multiplication A * B where A is an N by M
     * matrix of double and B is M by K.
     *
     * The class stores the structure of each matrix,
     * the double values of A and B, and pointers to
     * the varis for A and B if A or B is a var. It
     * also instantiates and stores pointers to
     * varis for all elements of A * B.
     *
     * @tparam RA Rows for matrix A
     * @tparam CA Columns for matrix A, Rows for matrix B
     * @tparam TB Scalar type for matrix B
     * @tparam CB Columns for matrix B
     */
    template <int RA, int CA, typename TB, int CB>
    class multiply_mat_vari<double, RA, CA, TB, CB> : public vari {
    public:
      int A_rows_, A_cols_, B_cols_, A_size_, B_size_;
      double* Ad_;
      double* Bd_;
      vari** variRefB_;
      vari** variRefAB_;

      /**
       * Constructor for multiply_mat_vari.
       *
       * All memory allocated in
       * ChainableStack's stack_alloc arena.
       *
       * It is critical for the efficiency of this object
       * that the constructor create new varis that aren't
       * popped onto the var_stack_, but rather are
       * popped onto the var_nochain_stack_. This is
       * controlled to the second argument to
       * vari's constructor.
       *
       * @param A row vector
       * @param B vector
       */
      multiply_mat_vari(const Eigen::Matrix<double, RA, CA>& A,
                        const Eigen::Matrix<TB, CA, CB>& B)
        : vari(0.0),
          A_rows_(A.rows()), A_cols_(A.cols()),
          B_cols_(B.cols()), A_size_(A.size()), B_size_(B.size()),
          Ad_(ChainableStack::memalloc_.alloc_array<double>(A_size_)),
          Bd_(ChainableStack::memalloc_.alloc_array<double>(B_size_)),
          variRefB_(ChainableStack::memalloc_.alloc_array<vari*>(B_size_)),
          variRefAB_(ChainableStack::memalloc_.alloc_array<vari*>(A_rows_
                                                                  * B_cols_)) {
        using Eigen::MatrixXd;
        using Eigen::Map;
        for (size_type i = 0; i < A.size(); ++i)
          Ad_[i] = A.coeffRef(i);
        for (size_type i = 0; i < B.size(); ++i) {
          variRefB_[i] = B.coeffRef(i).vi_;
          Bd_[i] = B.coeffRef(i).val();
        }
        MatrixXd AB
          = Map<MatrixXd>(Ad_, A_rows_, A_cols_)
          * Map<MatrixXd>(Bd_, A_cols_, B_cols_);
        for (size_type i = 0; i < AB.size(); ++i)
          variRefAB_[i] = new vari(AB.coeffRef(i), false);
      }

      virtual void chain() {
        using Eigen::MatrixXd;
        using Eigen::Map;
        MatrixXd adjAB(A_rows_, B_cols_);
        MatrixXd adjB(A_cols_, B_cols_);

        for (size_type i = 0; i < adjAB.size(); ++i)
          adjAB(i) = variRefAB_[i]->adj_;
        adjB = Map<MatrixXd>(Ad_, A_rows_, A_cols_).transpose()
          * adjAB;
        for (size_type i = 0; i < B_size_; ++i)
          variRefB_[i]->adj_ += adjB(i);
      }
    };

    /**
     * This is a subclass of the vari class for matrix
     * multiplication A * B where A is a double
     * row vector of length M and B is a vector of
     * length M.
     *
     * The class stores the structure of each matrix,
     * the double values of A and B, and pointers to
     * the varis for A and B if A or B is a var. It
     * also instantiates and stores pointers to
     * varis for all elements of A * B.
     *
     * @tparam CA Columns for matrix A, Rows for matrix B
     * @tparam TB Scalar type for matrix B
     */
    template <int CA, typename TB>
    class multiply_mat_vari<double, 1, CA, TB, 1> : public vari {
    public:
      int size_;
      double* Ad_;
      double* Bd_;
      vari** variRefB_;
      vari* variRefAB_;

      /**
       * Constructor for multiply_mat_vari.
       *
       * All memory allocated in
       * ChainableStack's stack_alloc arena.
       *
       * It is critical for the efficiency of this object
       * that the constructor create new varis that aren't
       * popped onto the var_stack_, but rather are
       * popped onto the var_nochain_stack_. This is
       * controlled to the second argument to
       * vari's constructor.
       *
       * @param A row vector
       * @param B vector
       */
      multiply_mat_vari(const Eigen::Matrix<double, 1, CA>& A,
                        const Eigen::Matrix<TB, CA, 1>& B)
        : vari(0.0),
          size_(A.cols()),
          Ad_(ChainableStack::memalloc_.alloc_array<double>(size_)),
          Bd_(ChainableStack::memalloc_.alloc_array<double>(size_)),
          variRefB_(ChainableStack::memalloc_.alloc_array<vari*>(size_)) {
        using Eigen::Map;
        using Eigen::VectorXd;
        using Eigen::RowVectorXd;
        for (size_type i = 0; i < size_; ++i)
          Ad_[i] = A.coeffRef(i);
        for (size_type i = 0; i < size_; ++i) {
          variRefB_[i] = B.coeffRef(i).vi_;
          Bd_[i] = B.coeffRef(i).val();
        }
        double AB
          = Eigen::Map<RowVectorXd>(Ad_, 1, size_)
          * Eigen::Map<VectorXd>(Bd_, size_, 1);
        variRefAB_ = new vari(AB, false);
      }

      virtual void chain() {
        using Eigen::RowVectorXd;
        using Eigen::VectorXd;
        using Eigen::Map;
        double adjAB;
        VectorXd adjB(size_);

        adjAB = variRefAB_->adj_;
        adjB = Map<RowVectorXd>(Ad_, 1, size_).transpose()
          * adjAB;
        for (size_type i = 0; i < size_; ++i)
          variRefB_[i]->adj_ += adjB(i);
      }
    };

    /**
     * This is a subclass of the vari class for matrix
     * multiplication A * B where A is N by M and B
     * is an M by K matrix of doubles.
     *
     * The class stores the structure of each matrix,
     * the double values of A and B, and pointers to
     * the varis for A and B if A or B is a var. It
     * also instantiates and stores pointers to
     * varis for all elements of A * B.
     *
     * @tparam TA Scalar type for matrix A
     * @tparam RA Rows for matrix A
     * @tparam CA Columns for matrix A, Rows for matrix B
     * @tparam CB Columns for matrix B
     */
    template <typename TA, int RA, int CA, int CB>
    class multiply_mat_vari<TA, RA, CA, double, CB> : public vari {
    public:
      int A_rows_, A_cols_, B_cols_, A_size_, B_size_;
      double* Ad_;
      double* Bd_;
      vari** variRefA_;
      vari** variRefAB_;

      /**
       * Constructor for multiply_mat_vari.
       *
       * All memory allocated in
       * ChainableStack's stack_alloc arena.
       *
       * It is critical for the efficiency of this object
       * that the constructor create new varis that aren't
       * popped onto the var_stack_, but rather are
       * popped onto the var_nochain_stack_. This is
       * controlled to the second argument to
       * vari's constructor.
       *
       * @param A row vector
       * @param B vector
       */
      multiply_mat_vari(const Eigen::Matrix<TA, RA, CA>& A,
                        const Eigen::Matrix<double, CA, CB>& B)
        : vari(0.0),
          A_rows_(A.rows()), A_cols_(A.cols()),
          B_cols_(B.cols()), A_size_(A.size()), B_size_(B.size()),
          Ad_(ChainableStack::memalloc_.alloc_array<double>(A_size_)),
          Bd_(ChainableStack::memalloc_.alloc_array<double>(B_size_)),
          variRefA_(ChainableStack::memalloc_.alloc_array<vari*>(A_size_)),
          variRefAB_(ChainableStack::memalloc_.alloc_array<vari*>(A_rows_
                                                                  * B_cols_)) {
        using Eigen::Map;
        using Eigen::MatrixXd;
        for (size_type i = 0; i < A_size_; ++i) {
          variRefA_[i] = A.coeffRef(i).vi_;
          Ad_[i] = A.coeffRef(i).val();
        }
        for (size_type i = 0; i < B_size_; ++i) {
          Bd_[i] = B.coeffRef(i);
        }
        MatrixXd AB
          = Map<MatrixXd>(Ad_, A_rows_, A_cols_)
          * Map<MatrixXd>(Bd_, A_cols_, B_cols_);
        for (size_type i = 0; i < AB.size(); ++i)
          variRefAB_[i] = new vari(AB.coeffRef(i), false);
      }

      virtual void chain() {
        using Eigen::MatrixXd;
        using Eigen::Map;
        MatrixXd adjAB(A_rows_, B_cols_);
        MatrixXd adjA(A_rows_, A_cols_);

        for (size_type i = 0; i < adjAB.size(); ++i)
          adjAB(i) = variRefAB_[i]->adj_;
        adjA = adjAB * Map<MatrixXd>(Bd_, A_cols_, B_cols_).transpose();
        for (size_type i = 0; i < A_size_; ++i)
          variRefA_[i]->adj_ += adjA(i);
      }
    };

    /**
     * This is a subclass of the vari class for matrix
     * multiplication A * B where A is a row
     * vector of length M and B is a vector of length M
     * of doubles.
     *
     * The class stores the structure of each matrix,
     * the double values of A and B, and pointers to
     * the varis for A and B if A or B is a var. It
     * also instantiates and stores pointers to
     * varis for all elements of A * B.
     *
     * @tparam TA Scalar type for matrix A
     * @tparam RA Rows for matrix A
     * @tparam CA Columns for matrix A, Rows for matrix B
     * @tparam TB Scalar type for matrix B
     * @tparam CB Columns for matrix B
     */
    template <typename TA, int CA>
    class multiply_mat_vari<TA, 1, CA, double, 1> : public vari {
    public:
      int size_;
      double* Ad_;
      double* Bd_;
      vari** variRefA_;
      vari* variRefAB_;

      /**
       * Constructor for multiply_mat_vari.
       *
       * All memory allocated in
       * ChainableStack's stack_alloc arena.
       *
       * It is critical for the efficiency of this object
       * that the constructor create new varis that aren't
       * popped onto the var_stack_, but rather are
       * popped onto the var_nochain_stack_. This is
       * controlled to the second argument to
       * vari's constructor.
       *
       * @param A row vector
       * @param B vector
       */
      multiply_mat_vari(const Eigen::Matrix<TA, 1, CA>& A,
                        const Eigen::Matrix<double, CA, 1>& B)
        : vari(0.0),
          size_(A.cols()),
          Ad_(ChainableStack::memalloc_.alloc_array<double>(size_)),
          Bd_(ChainableStack::memalloc_.alloc_array<double>(size_)),
          variRefA_(ChainableStack::memalloc_.alloc_array<vari*>(size_)) {
        using Eigen::Map;
        using Eigen::VectorXd;
        using Eigen::RowVectorXd;
        for (size_type i = 0; i < size_; ++i) {
          variRefA_[i] = A.coeffRef(i).vi_;
          Ad_[i] = A.coeffRef(i).val();
        }
        for (size_type i = 0; i < size_; ++i)
          Bd_[i] = B.coeffRef(i);
        double AB
          = Map<RowVectorXd>(Ad_, 1, size_)
          * Map<VectorXd>(Bd_, size_, 1);
        variRefAB_ = new vari(AB, false);
      }

      virtual void chain() {
        using Eigen::Map;
        using Eigen::VectorXd;
        using Eigen::RowVectorXd;
        double adjAB;
        RowVectorXd adjA(size_);

        adjAB = variRefAB_->adj_;
        adjA = adjAB
          * Map<VectorXd>(Bd_, size_, 1).transpose();
        for (size_type i = 0; i < size_; ++i)
          variRefA_[i]->adj_ += adjA(i);
      }
    };

    /**
     * Return the product of two scalars.
     * @tparam T1 scalar type of v
     * @tparam T2 scalar type of c
     * @param[in] v First scalar
     * @param[in] c Specified scalar
     * @return Product of scalars
     */
    template <typename T1, typename T2>
    inline typename
    boost::enable_if_c<
      (boost::is_scalar<T1>::value || boost::is_same<T1, var>::value)
      && (boost::is_scalar<T2>::value || boost::is_same<T2, var>::value),
      typename boost::math::tools::promote_args<T1, T2>::type>::type
    multiply(const T1& v, const T2& c) {
      return v * c;
    }

    /**
     * Return the product of scalar and matrix.
     * @tparam T1 scalar type v
     * @tparam T2 scalar type matrix m
     * @tparam R2 Rows matrix m
     * @tparam C2 Columns matrix m
     * @param[in] c Specified scalar
     * @param[in] m Matrix
     * @return Product of scalar and matrix
     */
    template<typename T1, typename T2, int R2, int C2>
    inline Eigen::Matrix<var, R2, C2>
    multiply(const T1& c, const Eigen::Matrix<T2, R2, C2>& m) {
      // TODO(trangucci) pull out to eliminate overpromotion of one side
      // move to matrix.hpp w. promotion?
      return to_var(m) * to_var(c);
    }

    /**
     * Return the product of scalar and matrix.
     * @tparam T1 scalar type matrix m
     * @tparam T2 scalar type v
     * @tparam R1 Rows matrix m
     * @tparam C1 Columns matrix m
     * @param[in] c Specified scalar
     * @param[in] m Matrix
     * @return Product of scalar and matrix
     */
    template<typename T1, int R1, int C1, typename T2>
    inline Eigen::Matrix<var, R1, C1>
    multiply(const Eigen::Matrix<T1, R1, C1>& m, const T2& c) {
      // TODO(trangucci) pull out to eliminate overpromotion of one side
      // move to matrix.hpp w. promotion?
      return to_var(m) * to_var(c);
    }

    /**
     * Return the product of two matrices.
     * @tparam TA scalar type matrix A
     * @tparam RA Rows matrix A
     * @tparam CA Columns matrix A
     * @tparam TB scalar type matrix B
     * @tparam RB Rows matrix B
     * @tparam CB Columns matrix B
     * @param[in] A Matrix
     * @param[in] B Matrix
     * @return Product of scalar and matrix.
     */
    template <typename TA, int RA, int CA, typename TB, int CB>
    inline typename
    boost::enable_if_c<boost::is_same<TA, var>::value
                       || boost::is_same<TB, var>::value,
                       Eigen::Matrix<var, RA, CB> >::type
    multiply(const Eigen::Matrix<TA, RA, CA> &A,
             const Eigen::Matrix<TB, CA, CB> &B) {
      check_multiplicable("multiply", "A", A, "B", B);
      check_not_nan("multiply", "A", A);
      check_not_nan("multiply", "B", B);

      // Memory managed with the arena allocator.
      multiply_mat_vari<TA, RA, CA, TB, CB> *baseVari
        = new multiply_mat_vari<TA, RA, CA, TB, CB>(A, B);
      Eigen::Matrix<var, RA, CB> AB_v(A.rows(), B.cols());
      for (size_type i = 0; i < AB_v.size(); ++i) {
        AB_v.coeffRef(i).vi_ = baseVari->variRefAB_[i];
      }
      return AB_v;
    }

    /**
     * Return the scalar product of a row vector and
     * a vector.
     * @tparam TA scalar type row vector A
     * @tparam CA Columns matrix A
     * @tparam TB scalar type vector B
     * @param[in] A Row vector
     * @param[in] B Column vector
     * @return Scalar product of row vector and vector
     */
    template <typename TA, int CA, typename TB>
    inline typename
    boost::enable_if_c<boost::is_same<TA, var>::value
                       || boost::is_same<TB, var>::value, var>::type
    multiply(const Eigen::Matrix<TA, 1, CA> &A,
             const Eigen::Matrix<TB, CA, 1> &B) {
      check_multiplicable("multiply", "A", A, "B", B);
      check_not_nan("multiply", "A", A);
      check_not_nan("multiply", "B", B);

      // Memory managed with the arena allocator.
      multiply_mat_vari<TA, 1, CA, TB, 1> *baseVari
        = new multiply_mat_vari<TA, 1, CA, TB, 1>(A, B);
      var AB_v;
      AB_v.vi_ = baseVari->variRefAB_;
      return AB_v;
    }
  }
}
#endif