//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 UUID_47B1D1217B411E08424FA0ADFD72085

#define UUID_47B1D1217B411E08424FA0ADFD72085

#include <boost/qvm/mat_traits.hpp>

#include <boost/qvm/inline.hpp>

#include <boost/qvm/static_assert.hpp>

#include <boost/qvm/enable_if.hpp>

namespace

boost

    {

    namespace

    qvm

        {

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

        template <int R,int C,class M>

        BOOST_QVM_INLINE_TRIVIAL

        typename enable_if_c<

            is_mat<M>::value,

            typename mat_traits<M>::scalar_type>::type

        A( M const & a )

            {

            BOOST_STATIC_ASSERT(R>=0);

            BOOST_STATIC_ASSERT(R<mat_traits<M>::rows);

            BOOST_STATIC_ASSERT(C>=0);

            BOOST_STATIC_ASSERT(C<mat_traits<M>::cols);

            return mat_traits<M>::template read_element<R,C>(a);

            }

        template <int R,int C,class M>

        BOOST_QVM_INLINE_TRIVIAL

        typename enable_if_c<

            is_mat<M>::value,

            typename mat_traits<M>::scalar_type &>::type

        A( M & a )

            {

            BOOST_STATIC_ASSERT(R>=0);

            BOOST_STATIC_ASSERT(R<mat_traits<M>::rows);

            BOOST_STATIC_ASSERT(C>=0);

            BOOST_STATIC_ASSERT(C<mat_traits<M>::cols);

            return mat_traits<M>::template write_element<R,C>(a);

            }

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

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A00( M const & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template read_element<0,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A01( M const & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template read_element<0,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A02( M const & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template read_element<0,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A03( M const & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template read_element<0,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A04( M const & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template read_element<0,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A05( M const & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template read_element<0,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A06( M const & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template read_element<0,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A07( M const & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template read_element<0,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A08( M const & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template read_element<0,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A09( M const & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template read_element<0,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A10( M const & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template read_element<1,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A11( M const & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template read_element<1,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A12( M const & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template read_element<1,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A13( M const & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template read_element<1,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A14( M const & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template read_element<1,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A15( M const & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template read_element<1,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A16( M const & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template read_element<1,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A17( M const & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template read_element<1,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A18( M const & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template read_element<1,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A19( M const & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template read_element<1,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A20( M const & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template read_element<2,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A21( M const & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template read_element<2,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A22( M const & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template read_element<2,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A23( M const & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template read_element<2,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A24( M const & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template read_element<2,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A25( M const & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template read_element<2,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A26( M const & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template read_element<2,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A27( M const & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template read_element<2,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A28( M const & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template read_element<2,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A29( M const & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template read_element<2,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A30( M const & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template read_element<3,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A31( M const & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template read_element<3,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A32( M const & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template read_element<3,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A33( M const & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template read_element<3,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A34( M const & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template read_element<3,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A35( M const & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template read_element<3,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A36( M const & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template read_element<3,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A37( M const & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template read_element<3,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A38( M const & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template read_element<3,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A39( M const & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template read_element<3,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A40( M const & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template read_element<4,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A41( M const & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template read_element<4,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A42( M const & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template read_element<4,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A43( M const & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template read_element<4,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A44( M const & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template read_element<4,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A45( M const & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template read_element<4,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A46( M const & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template read_element<4,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A47( M const & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template read_element<4,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A48( M const & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template read_element<4,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A49( M const & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template read_element<4,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A50( M const & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template read_element<5,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A51( M const & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template read_element<5,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A52( M const & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template read_element<5,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A53( M const & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template read_element<5,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A54( M const & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template read_element<5,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A55( M const & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template read_element<5,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A56( M const & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template read_element<5,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A57( M const & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template read_element<5,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A58( M const & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template read_element<5,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A59( M const & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template read_element<5,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A60( M const & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template read_element<6,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A61( M const & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template read_element<6,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A62( M const & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template read_element<6,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A63( M const & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template read_element<6,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A64( M const & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template read_element<6,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A65( M const & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template read_element<6,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A66( M const & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template read_element<6,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A67( M const & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template read_element<6,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A68( M const & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template read_element<6,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A69( M const & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template read_element<6,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A70( M const & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template read_element<7,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A71( M const & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template read_element<7,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A72( M const & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template read_element<7,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A73( M const & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template read_element<7,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A74( M const & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template read_element<7,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A75( M const & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template read_element<7,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A76( M const & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template read_element<7,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A77( M const & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template read_element<7,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A78( M const & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template read_element<7,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A79( M const & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template read_element<7,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A80( M const & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template read_element<8,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A81( M const & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template read_element<8,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A82( M const & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template read_element<8,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A83( M const & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template read_element<8,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A84( M const & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template read_element<8,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A85( M const & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template read_element<8,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A86( M const & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template read_element<8,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A87( M const & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template read_element<8,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A88( M const & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template read_element<8,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A89( M const & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template read_element<8,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A90( M const & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template read_element<9,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A91( M const & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template read_element<9,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A92( M const & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template read_element<9,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A93( M const & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template read_element<9,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A94( M const & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template read_element<9,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A95( M const & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template read_element<9,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A96( M const & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template read_element<9,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A97( M const & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template read_element<9,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A98( M const & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template read_element<9,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type>::type A99( M const & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template read_element<9,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A00( M & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template write_element<0,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A01( M & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template write_element<0,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A02( M & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template write_element<0,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A03( M & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template write_element<0,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A04( M & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template write_element<0,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A05( M & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template write_element<0,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A06( M & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template write_element<0,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A07( M & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template write_element<0,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A08( M & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template write_element<0,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A09( M & a ) { BOOST_STATIC_ASSERT(0<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template write_element<0,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A10( M & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template write_element<1,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A11( M & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template write_element<1,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A12( M & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template write_element<1,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A13( M & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template write_element<1,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A14( M & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template write_element<1,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A15( M & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template write_element<1,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A16( M & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template write_element<1,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A17( M & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template write_element<1,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A18( M & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template write_element<1,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A19( M & a ) { BOOST_STATIC_ASSERT(1<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template write_element<1,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A20( M & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template write_element<2,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A21( M & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template write_element<2,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A22( M & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template write_element<2,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A23( M & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template write_element<2,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A24( M & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template write_element<2,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A25( M & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template write_element<2,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A26( M & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template write_element<2,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A27( M & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template write_element<2,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A28( M & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template write_element<2,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A29( M & a ) { BOOST_STATIC_ASSERT(2<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template write_element<2,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A30( M & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template write_element<3,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A31( M & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template write_element<3,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A32( M & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template write_element<3,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A33( M & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template write_element<3,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A34( M & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template write_element<3,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A35( M & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template write_element<3,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A36( M & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template write_element<3,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A37( M & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template write_element<3,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A38( M & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template write_element<3,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A39( M & a ) { BOOST_STATIC_ASSERT(3<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template write_element<3,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A40( M & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template write_element<4,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A41( M & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template write_element<4,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A42( M & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template write_element<4,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A43( M & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template write_element<4,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A44( M & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template write_element<4,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A45( M & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template write_element<4,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A46( M & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template write_element<4,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A47( M & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template write_element<4,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A48( M & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template write_element<4,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A49( M & a ) { BOOST_STATIC_ASSERT(4<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template write_element<4,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A50( M & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template write_element<5,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A51( M & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template write_element<5,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A52( M & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template write_element<5,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A53( M & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template write_element<5,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A54( M & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template write_element<5,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A55( M & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template write_element<5,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A56( M & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template write_element<5,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A57( M & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template write_element<5,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A58( M & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template write_element<5,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A59( M & a ) { BOOST_STATIC_ASSERT(5<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template write_element<5,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A60( M & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template write_element<6,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A61( M & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template write_element<6,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A62( M & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template write_element<6,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A63( M & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template write_element<6,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A64( M & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template write_element<6,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A65( M & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template write_element<6,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A66( M & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template write_element<6,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A67( M & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template write_element<6,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A68( M & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template write_element<6,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A69( M & a ) { BOOST_STATIC_ASSERT(6<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template write_element<6,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A70( M & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template write_element<7,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A71( M & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template write_element<7,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A72( M & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template write_element<7,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A73( M & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template write_element<7,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A74( M & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template write_element<7,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A75( M & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template write_element<7,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A76( M & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template write_element<7,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A77( M & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template write_element<7,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A78( M & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template write_element<7,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A79( M & a ) { BOOST_STATIC_ASSERT(7<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template write_element<7,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A80( M & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template write_element<8,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A81( M & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template write_element<8,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A82( M & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template write_element<8,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A83( M & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template write_element<8,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A84( M & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template write_element<8,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A85( M & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template write_element<8,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A86( M & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template write_element<8,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A87( M & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template write_element<8,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A88( M & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template write_element<8,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A89( M & a ) { BOOST_STATIC_ASSERT(8<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template write_element<8,9>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A90( M & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 0<mat_traits<M>::cols); return mat_traits<M>::template write_element<9,0>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A91( M & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 1<mat_traits<M>::cols); return mat_traits<M>::template write_element<9,1>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A92( M & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 2<mat_traits<M>::cols); return mat_traits<M>::template write_element<9,2>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A93( M & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 3<mat_traits<M>::cols); return mat_traits<M>::template write_element<9,3>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A94( M & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 4<mat_traits<M>::cols); return mat_traits<M>::template write_element<9,4>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A95( M & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 5<mat_traits<M>::cols); return mat_traits<M>::template write_element<9,5>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A96( M & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 6<mat_traits<M>::cols); return mat_traits<M>::template write_element<9,6>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A97( M & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 7<mat_traits<M>::cols); return mat_traits<M>::template write_element<9,7>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A98( M & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 8<mat_traits<M>::cols); return mat_traits<M>::template write_element<9,8>(a); }

        template <class M> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_mat<M>::value,typename mat_traits<M>::scalar_type &>::type A99( M & a ) { BOOST_STATIC_ASSERT(9<mat_traits<M>::rows && 9<mat_traits<M>::cols); return mat_traits<M>::template write_element<9,9>(a); }

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

        }

    }

#endif


/*
 * This file is part of the Sequoia MSO Solver.
 *
 * Copyright 2012 Alexander Langer, Theoretical Computer Science,
 *                                  RWTH Aachen University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @author Alexander Langer
 */
#include "cache_join.h"
#include "game_determined.h"
#include "game_q.h"
#include "game_q_introduce_iterator.h"
#include "game_q_forget_iterator.h"
#include "pair_iterator.h"

#include <boost/scoped_ptr.hpp>

namespace sequoia {

template <typename TFormula, typename TMove, MCGame::Player _player>
const MCGame_f* QUndetGame<TFormula, TMove, _player>::minimize() const {
    assert(outcome() == MCGame::UNDETERMINED);
    if (empty()) {
    	delete this;
    	return determined.get(MCGame::opponent<_player>::value);
    } else {
        return MCGameFlyFactory::make(this);
    }
}

template <typename TFormula, typename TMove, MCGame::Player _player>
void QUndetGame<TFormula, TMove, _player>::set_subgame(const TMove *move,
						       const MCGame_f* game) {
    bool res = _subgames.insert(move, game);
    if (!res) { // new entry
        // equivalent entry exists already, delete this game.
        delete game;
    }
}

template <typename TFormula, typename TMove, MCGame::Player _player>
const MCGame_f* QUndetGame<TFormula, TMove, _player>::add_subgame(const TMove* move,
								  const MCGame_f* game) {
    if (game->get()->outcome() == _player) {
	if (is_pointmove<TMove>::value && move == NULL) { // compiler will optimize this away for non-pointmoves
	    /*
	     * The current player must not use the NULL move for their advantage.
	     * An example where this may happen is testing whether the graph is
	     * a complete graph:  ALL x ALL y adj(x, y).
	     * If there is a non-vertex in the PAST, the game for adj(x,y)
	     * will return FALSE for the NULL move (due to the graph separator
	     * property).  Returning FALSE here would be wrong, when there is
	     * no vertex left to come.  We have to wait until this move is concrete.
	     */
	    DPRINTLN(" --> re-check this "
		    << formula()->variable()->identifier()
		    << " later (when concrete)!");
	    QUndetGame<TFormula, TMove, _player>::set_subgame(NULL, game);
	    return NULL;
	} else {
                DPRINTLN(" --> determines!");
                delete this;
		return game;
	}
    }
    if (game->get()->outcome() == MCGame::opponent<_player>::value) {
	    DPRINTLN(" --> ignore this "
		    << formula()->variable()->identifier()<< "!");
	    delete game;
	    return NULL;
    }
    DPRINTLN(" --> re-check this "
            << formula()->variable()->identifier() << " later!");
    QUndetGame<TFormula, TMove, _player>::set_subgame(move, game);
    return NULL;
}

template <typename TFormula, typename TMove, MCGame::Player _player>
const MCGame_f* QUndetGame<TFormula, TMove, _player>::convert() const {
    DEBUG({
        tab_prefix(level()) << "Building for game: "
                << toString() << std::endl;
    });
    QUndetGame<TFormula, TMove, _player>* returngame = new QUndetGame<TFormula, TMove, _player>(formula());
    typename QUndetGame<TFormula, TMove, _player>::GamesContainer::GameIterator
    	it(QUndetGame<TFormula, TMove, _player>::begin(),
	   QUndetGame<TFormula, TMove, _player>::end());
    while (it.has_next()) {
	std::pair<const TMove*, const MCGame_f*> res = it.next();
	const TMove* oldmove = res.first;
	const MCGame_f* oldgame = res.second;
        //DEBUG(tab_prefix(level()) << TOSTRING(res.first) << " --> " << res.second->get()->toString());
        DEBUG(tab_prefix(level()) << "Found Game for: " << TOSTRING(oldmove) << std::endl);
	DEBUG(tab_prefix(level()) << "Found Game " << oldgame->get()->toString() << std::endl);
	if (oldmove == NULL) continue; // this game is to be cut
	const MCGame_f* sub = oldgame->get()->convert();
	DEBUG(tab_prefix(level()) << TOSTRING(oldmove) << " --> " << sub->get()->toString());
        const MCGame_f* tmpgame = returngame->add_subgame(res.first, sub);
        if (tmpgame != NULL) return tmpgame;
    }
    return returngame->minimize();
}

template <typename TFormula, typename TMove, MCGame::Player _player>
const MCGame_f* QUndetGame<TFormula, TMove, _player>::forget(const ConstantSymbol* tsym,
							     int signature_depth,
							     const PointMove* replacement,
							     const Assignment_f* alpha) const {
    DEBUG({tab_prefix(level()) << "Building for game: " << TOSTRING(this) << std::endl;});

    QUndetGame<TFormula, TMove, _player>* returngame = new QUndetGame<TFormula, TMove, _player>(formula());
    DEBUG(returngame->level(level()));
    QUndetGameForgetIterator<TMove> it(tsym,
				       signature_depth,
				       replacement,
				       alpha,
				       formula()->variable(),
				       QUndetGame<TFormula, TMove, _player>::begin(),
				       QUndetGame<TFormula, TMove, _player>::end()
				       );
    DEBUG(it.level(level()));
    while (it.has_next()) {
	const std::pair<const TMove*, const MCGame_f*> res = it.next();
        DEBUG(tab_prefix(level()) << TOSTRING(res.first) << " --> " << res.second->get()->toString());
        const MCGame_f* tmpgame = returngame->add_subgame(res.first, res.second);
        if (tmpgame != NULL) return tmpgame;
    }
    return returngame->minimize();
}

template <typename TFormula, typename TMove, MCGame::Player _player>
const MCGame_f* QUndetGame<TFormula, TMove, _player>::introduce(const ConstantSymbol* tsym,
								int signature_depth,
								const Assignment_f* alpha) const {
    DEBUG({ tab_prefix(level()) << "Building for game: " << TOSTRING(this) << std::endl; });
    QUndetGame<TFormula, TMove, _player>* returngame = new QUndetGame<TFormula, TMove, _player>(formula());
    DEBUG(returngame->level(level()));
    QUndetGameIntroduceIterator<TMove> it(tsym,
			 signature_depth,
			 alpha,
			 formula()->variable(),
			 QUndetGame<TFormula, TMove, _player>::begin(),
			 QUndetGame<TFormula, TMove, _player>::end()
			 );
    DEBUG(it.level(level()));
    while (it.has_next()) {
	const std::pair<const TMove*, const MCGame_f*> res = it.next();
        DEBUG(tab_prefix(level()) << TOSTRING(res.first) << " --> " << res.second->get()->toString());
        const MCGame_f* tmpgame = returngame->add_subgame(res.first, res.second);
        if (tmpgame != NULL) return tmpgame;
    }
    return returngame->minimize();
}

template <typename TFormula, typename TMove, MCGame::Player _player>
const MCGame_f* QUndetGame<TFormula, TMove, _player>::join(const MCGame_f* other,
							   const Assignment_f* alpha) const {
    DEBUG({
        tab_prefix(level()) << "Building for game: "
                << toString() << std::endl;
    });
    if (other->get()->outcome() != MCGame::UNDETERMINED) {
        DEBUG(tab_prefix(level()) << "Found determined game, return." << std::endl);
        return other->clone();
    }
    return QUndetGame<TFormula, TMove, _player>::join_impl(other, alpha, tag<TMove>());
}

template <typename TFormula, typename TMove, MCGame::Player _player>
const MCGame_f* QUndetGame<TFormula, TMove, _player>::join_impl(const MCGame_f* other,
							   const Assignment_f* alpha,
							   tag<SetMove>) const {
    typedef QUndetGame<TFormula, TMove, _player> MyType;
    typedef PairIterator<subgames_iterator, subgames_iterator> GamePairIterator;
    typedef typename GamePairIterator::value_type GamePairIteratorValue;
    const MyType* gother = static_cast<const MyType*>(other->get());
    MyType* returngame = new MyType(formula());
    DEBUG(returngame->level(level()));

    const_iterator mit = begin();
    const_iterator mitend = end();
    for (; mit != mitend; mit++) {
	const SetMove* oldmove = mit->first;
        DEBUG(tab_prefix(level()) << "Found Games for: " << TOSTRING(oldmove) << std::endl);

	boost::scoped_ptr<const Assignment_f> my_alpha(AssignmentFlyFactory::make(
                new SetAssignment(alpha, formula()->variable(), oldmove)));

	const_iterator mit2 = gother->find(oldmove);
	if (mit2 == gother->end()) {
	    DEBUG(tab_prefix(level()) << TOSTRING(oldmove) << " --> ignored in other, skip" << std::endl);
            continue;
	}
	GamePairIterator git(mit->second->begin(), mit->second->end(),
	                     mit2->second->begin(), mit2->second->end());
	while (git.has_next()) {
	    GamePairIteratorValue res = git.next();
	    const MCGame_f* g1 = res.first;
	    const MCGame_f* g2 = res.second;
#if USE_CACHE_SUBGAMES
	    const MCGame_f *sub = cache_join_lookup(g1->get()->formula(), my_alpha.get(), g1, g2);
#else
	    const MCGame_f *sub = NULL;
#endif
	    if (sub == NULL) {
		sub = g1->get()->join(g2, my_alpha.get());
#if USE_CACHE_SUBGAMES
		cache_join_store(g1->get()->formula(), my_alpha.get(), g1, g2, sub);
#endif
	    }
	    DEBUG(tab_prefix(level()) << TOSTRING(oldmove) << " x "
		    << TOSTRING(oldmove) << " --> "
		    << sub->get()->toString() << std::endl);
	    const MCGame_f* tmpgame = returngame->add_subgame(oldmove, sub);
	    if (tmpgame != NULL) return tmpgame;
	}
    }
    return returngame->minimize();
}

template <typename TFormula, typename TMove, MCGame::Player _player>
const MCGame_f* QUndetGame<TFormula, TMove, _player>::join_impl(const MCGame_f* other,
							   const Assignment_f* alpha,
							   tag<PointMove>) const {
    typedef QUndetGame<TFormula, TMove, _player> MyType;
    typedef PairIterator<subgames_iterator, subgames_iterator> GamePairIterator;
    typedef typename GamePairIterator::value_type GamePairIteratorValue;
    const MyType* gother = static_cast<const MyType*>(other->get());
    MyType* returngame = new MyType(formula());
    DEBUG(returngame->level(level()));

    /*
     * We need to combine in a Cartesian manner:
     * - all TERMINAL moves with all TERMINAL moves on the other side (use the same move)
     * - all ANON moves with the NULL move on the other side, and vice versa;
     *	 this case includes the NULL x NULL case
     */
    const MyType* sides[] = { this, gother };
    for (int i = 0; i < 2; i++) {
        DEBUG(tab_prefix(level()) << "Join " << sides[i] << " with "
                << sides[1-i] << std::endl);
	const_iterator mit = sides[i]->begin();
	const_iterator mitend = sides[i]->end();
	for (; mit != mitend; mit++) {
            const PointMove* oldmove = mit->first;
            DEBUG(tab_prefix(level()) << "Found Games for: "
                    << TOSTRING(oldmove) << std::endl);
            // in the second run we do not need to re-check Terminal x Terminal
            // or NULL x *
            if (i == 1) {
                if (oldmove == NULL || oldmove->terminal()) {
                    DEBUG(tab_prefix(level()) << " --> checked before, skip" << std::endl);
                    continue;
                }
            }

            // set the lookup key that we expect for this move
	    // for terminals we require the same move on the other side,
	    // in all other cases the NULL move is the correct one.
            const PointMove* lookupmove = NULL;
            if (oldmove != NULL && oldmove->terminal())
                lookupmove = oldmove;

            // now lookup the corresponding SubGamesContainer container; continue if nonexistent
	    const_iterator mit2 = sides[1-i]->find(lookupmove);
	    if (mit2 == sides[1-i]->end()) {
                DEBUG(tab_prefix(level()) << TOSTRING(lookupmove)
                        << " nonexistent in other, "
                        << (i == 1 ? "break" : "skip")
                        << std::endl);
                // In the second visit of the for loop, we already
                // joined all terminals, so we can stop here early (saves a bit of time)
                // In the first run, we just continue with the next move.
                if (i == 1)
                    break;
                else
                    continue;
            }

	    boost::scoped_ptr<const Assignment_f> my_alpha(AssignmentFlyFactory::make(
                new ObjAssignment(alpha, formula()->variable(), oldmove)));

	    GamePairIterator git(mit->second->begin(), mit->second->end(),
	                         mit2->second->begin(), mit2->second->end());
	    while (git.has_next()) {
		GamePairIteratorValue res = git.next();
		const MCGame_f* g1 = res.first;
		const MCGame_f* g2 = res.second;
#if USE_CACHE_SUBGAMES
		const MCGame_f *sub = cache_join_lookup(g1->get()->formula(), my_alpha.get(), g1, g2);
#else
		const MCGame_f *sub = NULL;
#endif
		if (sub == NULL) {
		    sub = g1->get()->join(g2, my_alpha.get());
#if USE_CACHE_SUBGAMES
		    cache_join_store(g1->get()->formula(), my_alpha.get(), g1, g2, sub);
#endif
		}
		DEBUG(tab_prefix(level()) << TOSTRING(oldmove) << " x "
                            << TOSTRING(lookupmove) << " --> "
                            << sub->get()->toString() << std::endl);
		const MCGame_f* tmpgame = returngame->add_subgame(oldmove, sub);
		if (tmpgame != NULL) return tmpgame;
	    }
	}
    }
    return returngame->minimize();
}

#ifdef DODEBUG
template <typename TFormula, typename TMove, MCGame::Player _player>
void QUndetGame<TFormula, TMove, _player>::recursive_print_game(int level,
								bool skip_first) const {
    (skip_first ? std::cout : tab_prefix(level))
	    << "[" << toString()
	    << " with subgames" << std::endl;

    typename QUndetGame<TFormula, TMove, _player>::GamesContainer::GameIterator
    	it(QUndetGame<TFormula, TMove, _player>::begin(),
	   QUndetGame<TFormula, TMove, _player>::end());
    while (it.has_next()) {
	const std::pair<const TMove*, const MCGame_f*> res = it.next();
	tab_prefix(level+1) << TOSTRING(res.first) << ": " << std::endl;
	res.second->get()->recursive_print_game(level+2, false);
    }
    tab_prefix(level) << "]" << std::endl;
}
#endif

}; // namespace


// Copyright Nick Thompson, 2019
// Use, modification and distribution are subject to 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_MATH_INTERPOLATORS_WHITAKKER_SHANNON_HPP
#define BOOST_MATH_INTERPOLATORS_WHITAKKER_SHANNON_HPP
#include <memory>
#include <boost/math/interpolators/detail/whittaker_shannon_detail.hpp>

namespace boost { namespace math { namespace interpolators {

template<class RandomAccessContainer>
class whittaker_shannon {
public:

    using Real = typename RandomAccessContainer::value_type;
    whittaker_shannon(RandomAccessContainer&& y, Real const & t0, Real const & h)
     : m_impl(std::make_shared<detail::whittaker_shannon_detail<RandomAccessContainer>>(std::move(y), t0, h))
    {}

    inline Real operator()(Real t) const
    {
        return m_impl->operator()(t);
    }

    inline Real prime(Real t) const
    {
        return m_impl->prime(t);
    }

    inline Real operator[](size_t i) const
    {
        return m_impl->operator[](i);
    }

    RandomAccessContainer&& return_data()
    {
        return m_impl->return_data();
    }

private:
    std::shared_ptr<detail::whittaker_shannon_detail<RandomAccessContainer>> m_impl;
};
}}}
#endif


/*
    Residue.hpp
    ===========
        Class Residue implementation.
*/

#pragma once

#include <string>
#include <vector>
#include <unordered_map>
#include <utility>
#include <cctype>
#include <boost/format.hpp>
#include <Eigen/Dense>
#include "Residue.h"
#include "Chain.h"
#include "Atom.h"
#include "Math.hpp"
#include "Constants.hpp"

namespace PDBTools
{

////////////////////////////////////////////////////////////////////////////////
// Using
////////////////////////////////////////////////////////////////////////////////

using std::string;
using std::vector;
using std::unordered_map;
using std::pair;
using boost::format;
using Eigen::RowVector3d;
using Eigen::Matrix3d;

////////////////////////////////////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////////////////////////////////////

Residue::Residue(const string &name, int num, const string &ins, Chain *owner):
    __name (name),
    __num  (num),
    __ins  (ins),
    __owner(owner)
{
    if (owner)
    {
        owner->sub().push_back(this);
    }
}

////////////////////////////////////////////////////////////////////////////////
// Getter: __name
////////////////////////////////////////////////////////////////////////////////

string &Residue::name()
{
    return __name;
}

////////////////////////////////////////////////////////////////////////////////
// Getter: __num
////////////////////////////////////////////////////////////////////////////////

int Residue::num()
{
    return __num;
}

////////////////////////////////////////////////////////////////////////////////
// Getter: __ins
////////////////////////////////////////////////////////////////////////////////

string &Residue::ins()
{
    return __ins;
}

////////////////////////////////////////////////////////////////////////////////
// Getter: __owner
////////////////////////////////////////////////////////////////////////////////

Chain *Residue::owner()
{
    return __owner;
}

////////////////////////////////////////////////////////////////////////////////
// Getter: __sub
////////////////////////////////////////////////////////////////////////////////

vector<Atom *> &Residue::sub()
{
    return __sub;
}

////////////////////////////////////////////////////////////////////////////////
// Setter: __name
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::name(const string &val)
{
    __name = val;

    return this;
}

////////////////////////////////////////////////////////////////////////////////
// Setter: __num
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::num(int val)
{
    __num = val;

    return this;
}

////////////////////////////////////////////////////////////////////////////////
// Setter: __ins
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::ins(const string &val)
{
    __ins = val;

    return this;
}

////////////////////////////////////////////////////////////////////////////////
// Setter: __owner
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::owner(Chain *val)
{
    __owner = val;

    return this;
}

////////////////////////////////////////////////////////////////////////////////
// Setter: __sub
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::sub(const vector<Atom *> &val)
{
    __sub = val;

    return this;
}

////////////////////////////////////////////////////////////////////////////////
// Getter: compNum
////////////////////////////////////////////////////////////////////////////////

string Residue::compNum()
{
    return (format("%d%s") %
        __num              %
        __ins
    ).str();
}

////////////////////////////////////////////////////////////////////////////////
// Setter: compNum
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::compNum(int num, const string &ins)
{
    __num = num;
    __ins = ins;

    return this;
}

////////////////////////////////////////////////////////////////////////////////
// Setter: compNum (by compNumPair)
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::compNum(const pair<int, string> &compNumPair)
{
    __num = compNumPair.first;
    __ins = compNumPair.second;

    return this;
}

////////////////////////////////////////////////////////////////////////////////
// Copy
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::copy()
{
    auto copyResPtr = new Residue(__name, __num, __ins);

    for (auto atomPtr: __sub)
    {
        auto copyAtomPtr = atomPtr->copy();

        copyAtomPtr->owner(copyResPtr);

        copyResPtr->__sub.push_back(copyAtomPtr);
    }

    return copyResPtr;
}

////////////////////////////////////////////////////////////////////////////////
// GetResidues
////////////////////////////////////////////////////////////////////////////////

vector<Residue *> Residue::getResidues()
{
    return {this};
}

////////////////////////////////////////////////////////////////////////////////
// GetAtoms
////////////////////////////////////////////////////////////////////////////////

vector<Atom *> Residue::getAtoms()
{
    return __sub;
}

////////////////////////////////////////////////////////////////////////////////
// subMap
////////////////////////////////////////////////////////////////////////////////

unordered_map<string, Atom *> Residue::subMap()
{
    unordered_map<string, Atom *> atomPtrMap;

    for (auto atomPtr: __sub)
    {
        atomPtrMap.emplace(atomPtr->name(), atomPtr);
    }

    return atomPtrMap;
}

////////////////////////////////////////////////////////////////////////////////
// coordMap
////////////////////////////////////////////////////////////////////////////////

unordered_map<string, RowVector3d> Residue::coordMap()
{
    unordered_map<string, RowVector3d> coordPtrMap;

    for (auto atomPtr: __sub)
    {
        coordPtrMap.emplace(atomPtr->name(), atomPtr->coord());
    }

    return coordPtrMap;
}

////////////////////////////////////////////////////////////////////////////////
// Calc Backbone Dihedral Angle
////////////////////////////////////////////////////////////////////////////////

double Residue::calcBBDihedralAngle(DIH dihedralEnum)
{
    auto atomCoordMap = coordMap();

    if (dihedralEnum == DIH::L)
    {
        return calcDihedralAngle(
            prev()->coordMap().at("C"),
            atomCoordMap.at("N"),
            atomCoordMap.at("CA"),
            atomCoordMap.at("C")
        );
    }
    else
    {
        return calcDihedralAngle(
            atomCoordMap.at("N"),
            atomCoordMap.at("CA"),
            atomCoordMap.at("C"),
            next()->coordMap().at("N")
        );
    }
}

////////////////////////////////////////////////////////////////////////////////
// Calc Backbone Rotation Matrix By Delta Angle
////////////////////////////////////////////////////////////////////////////////

pair<RowVector3d, Matrix3d> Residue::calcBBRotationMatrixByDeltaAngle(
    DIH dihedralEnum, SIDE sideEnum, double deltaAngle)
{
    RowVector3d moveCoord;
    Matrix3d rotationMatrix;

    auto atomCoordMap = coordMap();

    if (sideEnum == SIDE::L)
    {
        deltaAngle = -deltaAngle;
    }

    if (dihedralEnum == DIH::L)
    {
        moveCoord = atomCoordMap.at("N");
        rotationMatrix = calcRotationMatrix(atomCoordMap.at("CA") - moveCoord, deltaAngle);
    }
    else
    {
        moveCoord = atomCoordMap.at("CA");
        rotationMatrix = calcRotationMatrix(atomCoordMap.at("C") - moveCoord, deltaAngle);
    }

    return {moveCoord, rotationMatrix};
}

////////////////////////////////////////////////////////////////////////////////
// Calc Backbone Rotation Matrix By Target Angle
////////////////////////////////////////////////////////////////////////////////

pair<RowVector3d, Matrix3d> Residue::calcBBRotationMatrixByTargetAngle(
    DIH dihedralEnum, SIDE sideEnum, double targetAngle)
{
    return calcBBRotationMatrixByDeltaAngle(dihedralEnum, sideEnum,
        targetAngle - calcBBDihedralAngle(dihedralEnum));
}

////////////////////////////////////////////////////////////////////////////////
// Get Backbone Rotation Atom Pointer
////////////////////////////////////////////////////////////////////////////////

vector<Atom *> Residue::getBBRotationAtomPtr(DIH dihedralEnum, SIDE sideEnum)
{
    vector<Atom *> rotationAtomObjList;
    auto iterInOwner = iter();

    if (sideEnum == SIDE::L)
    {
        for (auto resIter = __owner->sub().begin(); resIter != iterInOwner; resIter++)
        {
            for (auto atomPtr: **resIter)
            {
                rotationAtomObjList.push_back(atomPtr);
            }
        }

        if (dihedralEnum == DIH::R)
        {
            for (auto atomPtr: __sub)
            {
                if (atomPtr->name() != "CA" && atomPtr->name() != "C" &&
                    atomPtr->name() != "O" && atomPtr->name() != "OXT")
                {
                    rotationAtomObjList.push_back(atomPtr);
                }
            }
        }
    }
    else
    {
        if (dihedralEnum == DIH::L)
        {
            for (auto atomPtr: __sub)
            {
                if (atomPtr->name() != "N" && atomPtr->name() != "CA")
                {
                    rotationAtomObjList.push_back(atomPtr);
                }
            }
        }
        else
        {
            for (auto atomPtr: __sub)
            {
                if (atomPtr->name() == "O" || atomPtr->name() == "OXT")
                {
                    rotationAtomObjList.push_back(atomPtr);
                }
            }
        }

        for (auto resIter = iterInOwner + 1; resIter != __owner->sub().end(); resIter++)
        {
            for (auto atomPtr: **resIter)
            {
                rotationAtomObjList.push_back(atomPtr);
            }
        }
    }

    return rotationAtomObjList;
}

////////////////////////////////////////////////////////////////////////////////
// Rotate Backbone Dihedral Angle By Delta Angle
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::rotateBBDihedralAngleByDeltaAngle(DIH dihedralEnum,
    SIDE sideEnum, double deltaAngle)
{
    auto [moveCoord, rotationMatrix] = calcBBRotationMatrixByDeltaAngle(
        dihedralEnum, sideEnum, deltaAngle);

    for (auto atomPtr: getBBRotationAtomPtr(dihedralEnum, sideEnum))
    {
        atomPtr->coord((atomPtr->coord() - moveCoord) * rotationMatrix + moveCoord);
    }

    return this;
}

////////////////////////////////////////////////////////////////////////////////
// Rotate Backbone Dihedral Angle By Target Angle
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::rotateBBDihedralAngleByTargetAngle(DIH dihedralEnum,
    SIDE sideEnum, double targetAngle)
{
    return rotateBBDihedralAngleByDeltaAngle(dihedralEnum, sideEnum,
        targetAngle - calcBBDihedralAngle(dihedralEnum));
}

////////////////////////////////////////////////////////////////////////////////
// Calc Side Chain Dihedral Angle
////////////////////////////////////////////////////////////////////////////////

double Residue::calcSCDihedralAngle(int dihedralIdx)
{
    auto atomCoordMap = coordMap();

    return calcDihedralAngle(
        atomCoordMap[__RESIDUE_SIDE_CHAIN_ROTATION_ATOMS_NAME_MAP.at(__name).at(dihedralIdx)[0]],
        atomCoordMap[__RESIDUE_SIDE_CHAIN_ROTATION_ATOMS_NAME_MAP.at(__name).at(dihedralIdx)[1]],
        atomCoordMap[__RESIDUE_SIDE_CHAIN_ROTATION_ATOMS_NAME_MAP.at(__name).at(dihedralIdx)[2]],
        atomCoordMap[__RESIDUE_SIDE_CHAIN_ROTATION_ATOMS_NAME_MAP.at(__name).at(dihedralIdx)[3]]);
}

////////////////////////////////////////////////////////////////////////////////
// Calc Side Chain Rotation Matrix By Delta Angle
////////////////////////////////////////////////////////////////////////////////

pair<RowVector3d, Matrix3d> Residue::calcSCRotationMatrixByDeltaAngle(
    int dihedralIdx, double deltaAngle)
{
    auto atomCoordMap = coordMap();

    auto moveCoord = atomCoordMap[__RESIDUE_SIDE_CHAIN_ROTATION_ATOMS_NAME_MAP.at(
        __name).at(dihedralIdx)[1]];

    auto rotationMatrix = calcRotationMatrix(atomCoordMap[
        __RESIDUE_SIDE_CHAIN_ROTATION_ATOMS_NAME_MAP.at(__name).at(dihedralIdx)[2]] -
        moveCoord, deltaAngle);

    return {moveCoord, rotationMatrix};
}

////////////////////////////////////////////////////////////////////////////////
// Calc Side Chain Rotation Matrix By Target Angle
////////////////////////////////////////////////////////////////////////////////

pair<RowVector3d, Matrix3d> Residue::calcSCRotationMatrixByTargetAngle(
    int dihedralIdx, double targetAngle)
{
    return calcSCRotationMatrixByDeltaAngle(dihedralIdx,
        targetAngle - calcSCDihedralAngle(dihedralIdx));
}

////////////////////////////////////////////////////////////////////////////////
// Get Side Chain Rotation Atom Pointer
////////////////////////////////////////////////////////////////////////////////

vector<Atom *> Residue::getSCRotationAtomPtr(int dihedralIdx)
{
    vector<Atom *> rotationAtomObjList;

    unordered_set<string> rotationAtomNameSet(
        __RESIDUE_SIDE_CHAIN_ROTATION_ATOMS_NAME_MAP.at(__name).at(dihedralIdx).cbegin() + 3,
        __RESIDUE_SIDE_CHAIN_ROTATION_ATOMS_NAME_MAP.at(__name).at(dihedralIdx).cend());

    for (auto atomPtr: __sub)
    {
        if (rotationAtomNameSet.count(atomPtr->name()))
        {
            rotationAtomObjList.push_back(atomPtr);
        }
    }

    return rotationAtomObjList;
}

////////////////////////////////////////////////////////////////////////////////
// Rotate Side Chain Dihedral Angle By Delta Angle
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::rotateSCDihedralAngleByDeltaAngle(int dihedralIdx, double deltaAngle)
{
    auto [moveCoord, rotationMatrix] = calcSCRotationMatrixByDeltaAngle(
        dihedralIdx, deltaAngle);

    for (auto atomPtr: getSCRotationAtomPtr(dihedralIdx))
    {
        atomPtr->coord((atomPtr->coord() - moveCoord) * rotationMatrix + moveCoord);
    }

    return this;
}

////////////////////////////////////////////////////////////////////////////////
// Rotate Side Chain Dihedral Angle By Target Angle
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::rotateSCDihedralAngleByTargetAngle(int dihedralIdx, double targetAngle)
{
    return rotateSCDihedralAngleByDeltaAngle(dihedralIdx, targetAngle -
        calcSCDihedralAngle(dihedralIdx));
}

////////////////////////////////////////////////////////////////////////////////
// Dump
////////////////////////////////////////////////////////////////////////////////

Residue *Residue::dump(const string &dumpFilePath, const string &fileMode)
{
    string chainName;

    if (__owner)
    {
        chainName = __owner->name();
    }

    FILE *fo = fopen(dumpFilePath.c_str(), fileMode.c_str());

    for (auto atomPtr: __sub)
    {
        if (isdigit(atomPtr->name()[0]) || atomPtr->name().size() == 4)
        {
            fprintf(fo, "ATOM  %5d %-4s%1s%3s %1s%4d%1s   %8.3f%8.3f%8.3f%6s%6s          %2s%2s\n",
                atomPtr->num(),
                atomPtr->name().c_str(),
                atomPtr->alt().c_str(),
                __name.c_str(),
                chainName.c_str(),
                __num,
                __ins.c_str(),
                atomPtr->coord()[0],
                atomPtr->coord()[1],
                atomPtr->coord()[2],
                atomPtr->occ().c_str(),
                atomPtr->tempF().c_str(),
                atomPtr->ele().c_str(),
                atomPtr->chg().c_str()
            );
        }
        else
        {
            fprintf(fo, "ATOM  %5d  %-3s%1s%3s %1s%4d%1s   %8.3f%8.3f%8.3f%6s%6s          %2s%2s\n",
                atomPtr->num(),
                atomPtr->name().c_str(),
                atomPtr->alt().c_str(),
                __name.c_str(),
                chainName.c_str(),
                __num,
                __ins.c_str(),
                atomPtr->coord()[0],
                atomPtr->coord()[1],
                atomPtr->coord()[2],
                atomPtr->occ().c_str(),
                atomPtr->tempF().c_str(),
                atomPtr->ele().c_str(),
                atomPtr->chg().c_str()
            );
        }
    }

    fclose(fo);

    return this;
}

////////////////////////////////////////////////////////////////////////////////
// Destructor
////////////////////////////////////////////////////////////////////////////////

Residue::~Residue()
{
    for (auto subPtr: __sub)
    {
        delete subPtr;
    }
}

////////////////////////////////////////////////////////////////////////////////
// str
////////////////////////////////////////////////////////////////////////////////

string Residue::__str() const
{
    return (format("<Residue object: %d%s %s, at 0x%p>") %
        __num                                            %
        __ins                                            %
        __name                                           %
        this
    ).str();
}

}  // End namespace PDBTools


/**
 * Copyright (c) 2018, University Osnabrück
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the University Osnabrück nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL University Osnabrück BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

 /*
 * DualOctree.hpp
 *
 *  Created on: 18.01.2019
 *      Author: Benedikt Schumacher
 */

#ifndef DualOctree_HPP_
#define DualOctree_HPP_

#include <boost/thread.hpp>
#include "OctreeTables.hpp"

namespace lvr2
{

template<typename BaseVecT, typename BoxT>
class DualLeaf
{
public:

    /**
     * @brief Constructor.
     *
     * @param middle Center of the voxel
     */
    DualLeaf(BaseVecT vertices[]);

    /**
     * @brief Destructor (virtual)
     */
    virtual ~DualLeaf(){};

    /**
     * @brief Calculates and returns the bit-pattern respectively index from the edges of the represented voxel for the MC-Table.
     *
     * @param  distances Distances of the eight edges.
     * @return Index for the MC-Table.
     */
    int getIndex(float distances[]);

    BaseVecT getIntersectionPoint(float intersection, BaseVecT corner_one, BaseVecT corner_two);
    BaseVecT getIntersectionPoint(BaseVecT corner_one, float intersection, BaseVecT corner_two);
    BaseVecT getIntersectionPoint(BaseVecT corner_one, BaseVecT corner_two, float intersection);

    /**
     * @brief Calculates the twelve possible intersections between the cell and the surface to interpolate.
     *
     * @param corners   Eight corners of the current cell.
     * @param distance  Corresponding distance value.
     * @param positions Interpolated intersections.
     */
    void getIntersections(
            BaseVecT corners[],
            float distance[],
            BaseVecT positions[]);

    /**
     * @brief Returns edges of the voxel.
     *
     * @param corner Corners of the voxel.
     */
    void getVertices(
            BaseVecT corners[]);

    /**
     * @brief Returns the stored intersection between the cell and the surface at a given edge.
     *
     * @param i Index of the edge.
     */
    uint getIntersection(char i);

    /**
     * @brief Returns the middle of the represented voxel.
     *
     * @return Middle of the represented voxel.
     */
    BaseVecT& getMiddle();

    /**
     * @brief Sets the intersection between the cell and the surface at a given edge.
     *
     * @param i     Index of the edge.
     * @param value Value of the intersection.
     */
    void setIntersection(char i, uint value);

protected:

    /**
     * @brief Interpolates the intersection between x1 and x1.
     *
     * @param x1 First coordinate.
     * @param x2 Second coordinate.
     * @param d1 Distance value for the first coordinate.
     * @param d2 Distance value for the second coordinate.
     * @return Interpolated distance.
     */
    float calcIntersection(float x1, float x2, float d1, float d2);

    // Vertices of the represented voxel.
    BaseVecT m_vertices[8];

    // Twelve possible intersections.
    uint m_intersections[12];
};

}

#include "DualOctree.tcc"

#endif /* DualOctree_HPP_ */