// Copyright 2008-2022 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)

#include "boost/throw_exception.hpp"
#include "boost/exception/info.hpp"
#include "boost/exception/diagnostic_information.hpp"
#include "boost/bind.hpp"
#include <string>
#include <map>
#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
#include <set>
#include <algorithm>
#include <limits>
#include <assert.h>

#define NL "\n"
#define TAB "    "
#define TAB1 TAB
#define TAB2 TAB TAB
#define INCLUDE_MAT_ASSIGN "boost/qvm/gen/mat_assign%d.hpp"
#define INCLUDE_VEC_ASSIGN "boost/qvm/gen/vec_assign%d.hpp"
#define INCLUDE_STATIC_ASSERT "boost/qvm/static_assert.hpp"
#define INCLUDE_MATH "boost/qvm/math.hpp"
#define INCLUDE_THROW_EXCEPTION "boost/qvm/throw_exception.hpp"
#define INCLUDE_ERROR "boost/qvm/error.hpp"
#define INCLUDE_CONFIG "boost/qvm/config.hpp"
#define INCLUDE_M_TRAITS "boost/qvm/mat_traits.hpp"
#define INCLUDE_V_TRAITS "boost/qvm/vec_traits.hpp"
#define INCLUDE_Q_TRAITS "boost/qvm/quat_traits.hpp"
#define INCLUDE_S_TRAITS "boost/qvm/scalar_traits.hpp"
#define INCLUDE_DEDUCE_M "boost/qvm/deduce_mat.hpp"
#define INCLUDE_DEDUCE_V "boost/qvm/deduce_vec.hpp"
#define INCLUDE_DEDUCE_Q "boost/qvm/deduce_quat.hpp"
#define INCLUDE_DEDUCE_S "boost/qvm/deduce_scalar.hpp"
#define INCLUDE_SWIZZLE_TRAITS "boost/qvm/detail/swizzle_traits.hpp"
#define INCLUDE_ENABLE_IF "boost/qvm/enable_if.hpp"
#define INCLUDE_ASSERT "boost/qvm/assert.hpp"

namespace
    {
    struct exception_base: virtual std::exception, virtual boost::exception { };
    struct bad_command_line: virtual exception_base { };
    typedef boost::error_info<struct cmd_arg_,std::string> cmd_arg;

    struct
    null_deleter
        {
        template <class T>
        void
        operator()( T * ) const
            {
            }
        };

    std::string
    get_include_guard( std::string file_name )
        {
        std::transform(file_name.begin(), file_name.end(),file_name.begin(), ::toupper);
        std::replace(file_name.begin(), file_name.end(), '.', '_');
        return "BOOST_QVM_GEN_" + file_name + "_INCLUDED";
        }

    template <class T>
    std::string
    to_string( T const & x )
        {
        std::ostringstream s;
        s<<x;
        return s.str();
        }

    struct
    command_line_options
        {
        bool con;
        std::string output_directory;

        command_line_options():
            con(false)
            {
            }
        };

    class
    output_file
        {
        output_file( output_file const & );
        output_file & operator=( output_file const & );

        std::string const output_directory;
        bool const con;
        std::ostringstream out_;
        std::set<std::string> includes_;

        public:

        explicit
        output_file( command_line_options const & opt ):
            output_directory(opt.output_directory),
            con(opt.con)
            {
            }

        void
        require_include( std::string const & fn )
            {
            assert(!strchr(fn.c_str(),'%'));
            includes_.insert(fn);
            };

        std::ostream &
        stream()
            {
            return out_;
            }

        void
        dump( std::string const & name ) const
            {
            std::ostream * out = &std::cout;
            boost::shared_ptr<std::ofstream> f;
            if( !con )
                {
                std::string path;
                if( !output_directory.empty() )
                    {
                    path+=output_directory;
                    path+='/';
                    path+=name;
                    }
                boost::shared_ptr<std::ofstream>(new std::ofstream(path.c_str())).swap(f);
                out = f.get();
                std::cout << "Writing " << path << "..." << std::endl;
                }
            out->exceptions(std::ofstream::eofbit|std::ofstream::failbit|std::ofstream::badbit);
            std::string include_guard=get_include_guard(name);
            *out <<
                "#ifndef " << include_guard << NL
                "#define " << include_guard << NL
                NL
                "// Copyright 2008-2022 Emil Dotchevski and Reverge Studios, Inc." NL
                NL
                "// Distributed under the Boost Software License, Version 1.0. (See accompanying" NL
                "// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)" NL
                NL
                "// This file was generated by a program. Do not edit manually." NL
                NL
                ;
            for( std::set<std::string>::const_iterator i=includes_.begin(),e=includes_.end(); i!=e; ++i )
                *out << "#include <" << *i << ">" NL;
            *out <<
                NL
                "namespace boost { namespace qvm {" NL
                NL
                <<out_.str() <<
                "} }" NL
                NL
                "#endif" NL
                ;
            }
        };

    void
    replace( std::string & s, char const * substr, char const * newstr )
        {
        assert(substr && *substr);
        assert(newstr && *newstr);
        std::string::size_type f=s.find(substr);
        if( s.npos!=f )
            s.replace(f,f+strlen(substr),newstr);
        }

    std::string
    deduce_name( std::string const & fn, char const * suffix )
        {
        std::string s=fn;
        replace(s,"operator==","eq");
        replace(s,"operator!=","neq");
        replace(s,"operator+=","plus_eq");
        replace(s,"operator-=","minus_eq");
        replace(s,"operator*=","mul_eq");
        replace(s,"operator/=","div_eq");
        replace(s,"operator+","plus");
        replace(s,"operator-","minus");
        replace(s,"operator*","mul");
        replace(s,"operator/","div");
        if( suffix )
            {
            s += '_';
            s += suffix;
            }
        return s;
        }

    void
    header_mr_ma_mb_same_size( output_file & out, int r, int c, std::string const & name )
        {
        assert(r>0);
        assert(c>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_M);
        out.stream() <<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "mat_traits<A>::rows=="<<r<<" && mat_traits<B>::rows=="<<r<<" &&" NL
            TAB1 "mat_traits<A>::cols=="<<c<<" && mat_traits<B>::cols=="<<c<<"," NL
            TAB1 "deduce_mat2<A,B,"<<r<<','<<c<<"> >::type" NL
            <<name<<"( A const & a, B const & b )" NL
            ;
        }

    void
    header_mr_ma_mb_mult( output_file & out, int m, int n, int p, std::string const & name )
        {
        assert(m>0);
        assert(n>0);
        assert(p>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_M);
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "mat_traits<A>::rows=="<<m<<" && mat_traits<B>::rows=="<<n<<" &&" NL
            TAB1 "mat_traits<A>::cols=="<<n<<" && mat_traits<B>::cols=="<<p<<"," NL
            TAB1 "deduce_mat2<A,B,"<<m<<','<<p<<"> >::type" NL
            <<name<<"( A const & a, B const & b )" NL
            ;
        }

    void
    header_vr_ma_vb_mult( output_file & out, int r, int c, std::string const & name )
        {
        assert(r>0);
        assert(c>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_V);
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "mat_traits<A>::rows=="<<r<<" && mat_traits<A>::cols=="<<c<<" &&" NL
            TAB1 "vec_traits<B>::dim=="<<c<<"," NL
            TAB1 "deduce_vec2<A,B,"<<c<<"> >::type" NL
            <<name<<"( A const & a, B const & b )" NL
            ;
        }

    void
    header_vr_va_mb_mult( output_file & out, int r, int c, std::string const & name )
        {
        assert(r>0);
        assert(c>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_V);
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "mat_traits<B>::rows=="<<r<<" && mat_traits<B>::cols=="<<c<<" &&" NL
            TAB1 "vec_traits<A>::dim=="<<c<<"," NL
            TAB1 "deduce_vec2<A,B,"<<r<<"> >::type" NL
            <<name<<"( A const & a, B const & b )" NL
            ;
        }

    void
    header_vr_va_vb_same_size( output_file & out, int d, std::string const & name )
        {
        assert(d>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_V);
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "vec_traits<A>::dim=="<<d<<" && vec_traits<B>::dim=="<<d<<"," NL
            TAB1 "deduce_vec2<A,B,"<<d<<"> >::type" NL
            <<name<<"( A const & a, B const & b )" NL
            ;
        }

    void
    header_bool_ma_mb_same_size( output_file & out, int r, int c, std::string const & name )
        {
        assert(r>0);
        assert(c>0);
        assert(!name.empty());
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename enable_if_c<" NL
            TAB1 "mat_traits<A>::rows=="<<r<<" && mat_traits<B>::rows=="<<r<<" &&" NL
            TAB1 "mat_traits<A>::cols=="<<c<<" && mat_traits<B>::cols=="<<c<<"," NL
            TAB1 "bool>::type" NL
            <<name<<"( A const & a, B const & b )" NL
            ;
        }

    void
    header_bool_va_vb_same_size( output_file & out, int d, std::string const & name )
        {
        assert(d>0);
        assert(!name.empty());
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename enable_if_c<" NL
            TAB1 "vec_traits<A>::dim=="<<d<<" && vec_traits<B>::dim=="<<d<<"," NL
            "bool>::type" NL
            <<name<<"( A const & a, B const & b )" NL
            ;
        }

    void
    header_ma_mb_same_size( output_file & out, int r, int c, std::string const & name )
        {
        assert(r>0);
        assert(c>0);
        assert(!name.empty());
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename enable_if_c<" NL
            TAB1 "mat_traits<A>::rows=="<<r<<" && mat_traits<B>::rows=="<<r<<" &&" NL
            TAB1 "mat_traits<A>::cols=="<<c<<" && mat_traits<B>::cols=="<<c<<"," NL
            TAB1 "A &>::type" NL
            <<name<<"( A & a, B const & b )" NL
            ;
        }

    void
    header_va_vb_same_size( output_file & out, int d, std::string const & name )
        {
        assert(d>0);
        assert(!name.empty());
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename enable_if_c<" NL
            TAB1 "vec_traits<A>::dim=="<<d<<" && vec_traits<B>::dim=="<<d<<"," NL
            TAB1 "A &>::type" NL
            <<name<<"( A & a, B const & b )" NL
            ;
        }

    void
    header_sr_ma( output_file & out, int r, int c, std::string const & name )
        {
        assert(r>0);
        assert(c>0);
        assert(!name.empty());
        out.stream()<<
            "template <class A>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename enable_if_c<" NL
            TAB1 "mat_traits<A>::rows=="<<r<<" && mat_traits<A>::cols=="<<c<<"," NL
            TAB1 "typename mat_traits<A>::scalar_type>::type" NL
            <<name<<"( A const & a )" NL
            ;
        }

    void
    header_sr_va_vb( output_file & out, int d, std::string const & name )
        {
        assert(d>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_S);
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "vec_traits<A>::dim=="<<d<<" && vec_traits<B>::dim=="<<d<<"," NL
            TAB1 "deduce_scalar<typename vec_traits<A>::scalar_type,typename vec_traits<B>::scalar_type> >::type" NL
            <<name<<"( A const & a, B const & b )" NL
            ;
        }

    void
    header_sr_va( output_file & out, int d, std::string const & name )
        {
        assert(d>0);
        assert(!name.empty());
        out.stream()<<
            "template <class A>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename enable_if_c<" NL
            TAB1 "is_vec<A>::value && vec_traits<A>::dim=="<<d<<"," NL
            TAB1 "typename vec_traits<A>::scalar_type>::type" NL
            <<name<<"( A const & a )" NL
            ;
        }

    void
    header_mr_ma( output_file & out, int r, int c, std::string const & name )
        {
        assert(r>0);
        assert(c>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_M);
        out.stream()<<
            "template <class A>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "mat_traits<A>::rows=="<<r<<" && mat_traits<A>::cols=="<<c<<"," NL
            TAB1 "deduce_mat<A> >::type" NL
            <<name<<"( A const & a )" NL
            ;
        }

    void
    header_vr_va( output_file & out, int d, std::string const & name )
        {
        assert(d>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_V);
        out.stream()<<
            "template <class A>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "vec_traits<A>::dim=="<<d<<"," NL
            TAB1 "deduce_vec<A> >::type" NL
            <<name<<"( A const & a )" NL
            ;
        }

    void
    header_vr_va_same_size( output_file & out, int d, std::string const & name )
        {
        assert(d>0);
        assert(!name.empty());
        out.stream()<<
            "template <class R,class A>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename enable_if_c<" NL
            TAB1 "is_vec<A>::value &&" NL
            TAB1 "vec_traits<R>::dim=="<<d<<" && vec_traits<A>::dim=="<<d<<"," NL
            TAB1 "R>::type" NL
            <<name<<"( A const & a )" NL
            ;
        }

    void
    header_mr_ma_sb( output_file & out, int r, int c, std::string const & name )
        {
        assert(r>0);
        assert(c>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_M);
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "mat_traits<A>::rows=="<<r<<" && mat_traits<A>::cols=="<<c<<" && is_scalar<B>::value," NL
            TAB1 "deduce_mat2<A,B,mat_traits<A>::rows,mat_traits<A>::cols> >::type" NL
            <<name<<"( A const & a, B b )" NL
            ;
        }

    void
    header_mr_sa_mb( output_file & out, int r, int c, std::string const & name )
        {
        assert(r>0);
        assert(c>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_M);
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "is_scalar<A>::value && mat_traits<B>::rows=="<<r<<" && mat_traits<B>::cols=="<<c<<"," NL
            TAB1 "deduce_mat2<A,B,mat_traits<B>::rows,mat_traits<B>::cols> >::type" NL
            <<name<<"( A a, B const & b )" NL
            ;
        }

    void
    header_vr_va_sb( output_file & out, int d, std::string const & name )
        {
        assert(d>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_V);
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "vec_traits<A>::dim=="<<d<<" && is_scalar<B>::value," NL
            TAB1 "deduce_vec2<A,B,vec_traits<A>::dim> >::type" NL
            <<name<<"( A const & a, B b )" NL
            ;
        }

    void
    header_vr_sa_vb( output_file & out, int d, std::string const & name )
        {
        assert(d>0);
        assert(!name.empty());
        out.require_include(INCLUDE_DEDUCE_V);
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "is_scalar<A>::value && vec_traits<B>::dim=="<<d<<"," NL
            TAB1 "deduce_vec2<A,B,vec_traits<B>::dim> >::type" NL
            <<name<<"( A a, B const & b )" NL
            ;
        }

    void
    header_ma_sb( output_file & out, int r, int c, std::string const & name )
        {
        assert(r>0);
        assert(c>0);
        assert(!name.empty());
        out.stream()<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename enable_if_c<" NL
            TAB1 "mat_traits<A>::rows=="<<r<<" && mat_traits<A>::cols=="<<c<<" && is_scalar<B>::value," NL
            TAB1 "A &>::type" NL
            <<name<<"( A & a, B b )" NL
            ;
        }

    void
    header_va_sb( output_file & out, int d, std::string const & name )
        {
        assert(d>0);
        assert(!name.empty());
        out.stream()<<
            "template <class A,class  B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename enable_if_c<" NL
            TAB1 "vec_traits<A>::dim=="<<d<<" && is_scalar<B>::value," NL
            TAB1 "A &>::type" NL
            <<name<<"( A & a, B b )" NL
            ;
        }

    void
    defined( std::ostream & g, int r, int cr, int c, std::string fn, char const * suffix )
        {
        assert(r>0);
        assert(cr>0);
        assert(c>0);
        assert(!fn.empty());
        std::string dn=deduce_name(fn,suffix);
        std::string name=dn+"_defined";
        g<<
            NL
            "namespace" NL
            "sfinae" NL
            TAB1 "{" NL
            TAB1 "using ::boost::qvm::"<<fn<<";" NL
            TAB1 "}" NL
            NL
            "namespace" NL
            "qvm_detail" NL
            TAB1 "{" NL
            TAB1 "template <int R,int /*CR*/,int C>" NL
            TAB1 "struct "<<name<<";" NL
            NL
            TAB1 "template <>" NL
            TAB1 "struct" NL
            TAB1<<name<<'<'<<r<<','<<cr<<','<<c<<">" NL
            TAB2"{" NL
            TAB2"static bool const value=true;" NL
            TAB2"};" NL
            TAB1 "}" NL
            NL
            ;
        }

    void
    defined( std::ostream & g, int r, int c, std::string const & fn, char const * suffix )
        {
        assert(r>0);
        assert(c>0);
        assert(!fn.empty());
        std::string dn=deduce_name(fn,suffix);
        std::string name=dn+"_defined";
        g<<
            NL
            "namespace" NL
            "sfinae" NL
            TAB1 "{" NL
            TAB1 "using ::boost::qvm::"<<fn<<";" NL
            TAB1 "}" NL
            NL
            "namespace" NL
            "qvm_detail" NL
            TAB1 "{" NL
            TAB1 "template <int R,int C>" NL
            TAB1 "struct "<<name<<";" NL
            NL
            TAB1 "template <>" NL
            TAB1 "struct" NL
            TAB1<<name<<"<"<<r<<","<<c<<">" NL
            TAB2"{" NL
            TAB2"static bool const value=true;" NL
            TAB2"};" NL
            TAB1 "}" NL
            NL
            ;
        }

    void
    defined( std::ostream & g, int d, std::string const & fn, char const * suffix )
        {
        assert(d>0);
        assert(!fn.empty());
        std::string dn=deduce_name(fn,suffix);
        std::string name=dn+"_defined";
        g<<
            NL
            "namespace" NL
            "sfinae" NL
            TAB1 "{" NL
            TAB1 "using ::boost::qvm::"<<fn<<";" NL
            TAB1 "}" NL
            NL
            "namespace" NL
            "qvm_detail" NL
            TAB1 "{" NL
            TAB1 "template <int D>" NL
            TAB1 "struct "<<name<<";" NL
            NL
            TAB1 "template <>" NL
            TAB1 "struct" NL
            TAB1<<name<<"<"<<d<<">" NL
            TAB2"{" NL
            TAB2"static bool const value=true;" NL
            TAB2"};" NL
            TAB1 "}" NL
            NL
            ;
        }

    void
    mr_mult_ma_mb( output_file & out, int m, int n, int p, char const * suffix )
        {
        assert(m>0);
        assert(n>0);
        assert(p>0);
        header_mr_ma_mb_mult(out,m,n,p,"operator*");
        out.require_include(INCLUDE_DEDUCE_M);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename mat_traits<A>::scalar_type Ta;" NL
            TAB1 "typedef typename mat_traits<B>::scalar_type Tb;" NL
            ;
        for( int i=0; i!=m; ++i )
            for( int j=0; j!=n; ++j )
                g<<TAB1 "Ta const a"<<i<<j<<" = mat_traits<A>::template read_element<"<<i<<','<<j<<">(a);" NL;
        for( int i=0; i!=n; ++i )
            for( int j=0; j!=p; ++j )
                g<<TAB1 "Tb const b"<<i<<j<<" = mat_traits<B>::template read_element<"<<i<<','<<j<<">(b);" NL;
        g<<
            TAB1 "typedef typename deduce_mat2<A,B,"<<m<<','<<p<<">::type R;" NL
            TAB1 "BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows=="<<m<<");" NL
            TAB1 "BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols=="<<p<<");" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=m; ++i )
            for( int j=0; j!=p; ++j )
                {
                g<<TAB1 "write_mat_element<"<<i<<","<<j<<">(r,";
                for( int k=0; k!=n; ++k )
                    {
                    if( k )
                        g<<'+';
                    g<<'a'<<i<<k<<"*b"<<k<<j;
                    }
                g<<");" NL;
                }
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,m,n,p,"operator*",suffix);
        }

    void
    ma_mult_ma_mb( output_file & out, int d, char const * suffix )
        {
        assert(d>0);
        header_ma_mb_same_size(out,d,d,"operator*=");
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename mat_traits<A>::scalar_type Ta;" NL
            TAB1 "typedef typename mat_traits<B>::scalar_type Tb;" NL
            ;
        for( int i=0; i!=d; ++i )
            for( int j=0; j!=d; ++j )
                g<<TAB1 "Ta const a"<<i<<j<<" = mat_traits<A>::template read_element<"<<i<<','<<j<<">(a);" NL;
        for( int i=0; i!=d; ++i )
            for( int j=0; j!=d; ++j )
                g<<TAB1 "Tb const b"<<i<<j<<" = mat_traits<B>::template read_element<"<<i<<','<<j<<">(b);" NL;
        for( int i=0; i!=d; ++i )
            for( int j=0; j!=d; ++j )
                {
                g<<TAB1 "write_mat_element<"<<i<<","<<j<<">(a,";
                for( int k=0; k!=d; ++k )
                    {
                    if( k )
                        g<<'+';
                    g<<'a'<<i<<k<<"*b"<<k<<j;
                    }
                g<<");" NL;
                }
        g<<
            TAB1 "return a;" NL
            TAB1 "}" NL
            ;
        defined(g,d,"operator*=",suffix);
        }

    void
    vr_mult_ma_vb( output_file & out, int r, int c, char const * suffix )
        {
        assert(r>0);
        assert(c>0);
        header_vr_ma_vb_mult(out,r,c,"operator*");
        out.require_include(INCLUDE_CONFIG);
        out.require_include(INCLUDE_V_TRAITS);
        out.require_include(INCLUDE_M_TRAITS);
        out.require_include(INCLUDE_ENABLE_IF);
        out.require_include(INCLUDE_DEDUCE_V);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename mat_traits<A>::scalar_type Ta;" NL
            TAB1 "typedef typename vec_traits<B>::scalar_type Tb;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB1 "Ta const a"<<i<<j<<" = mat_traits<A>::template read_element<"<<i<<','<<j<<">(a);" NL;
        for( int i=0; i!=c; ++i )
            g<<TAB1 "Tb const b"<<i<<" = vec_traits<B>::template read_element<"<<i<<">(b);" NL;
        g<<
            TAB1 "typedef typename deduce_vec2<A,B,"<<c<<">::type R;" NL
            TAB1 "BOOST_QVM_STATIC_ASSERT(vec_traits<R>::dim=="<<c<<");" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            {
            g<<TAB1 "write_vec_element<"<<i<<">(r,";
            for( int j=0; j!=c; ++j )
                {
                if( j )
                    g<<'+';
                g<<'a'<<i<<j<<"*b"<<j;
                }
            g<<");" NL;
            }
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,r,c,"operator*",suffix);
        }

    void
    vr_mult_va_mb( output_file & out, int r, int c, char const * suffix )
        {
        assert(r>0);
        assert(c>0);
        header_vr_va_mb_mult(out,r,c,"operator*");
        out.require_include(INCLUDE_CONFIG);
        out.require_include(INCLUDE_V_TRAITS);
        out.require_include(INCLUDE_M_TRAITS);
        out.require_include(INCLUDE_ENABLE_IF);
        out.require_include(INCLUDE_DEDUCE_V);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename vec_traits<A>::scalar_type Ta;" NL
            TAB1 "typedef typename mat_traits<B>::scalar_type Tb;" NL
            ;
        for( int i=0; i!=r; ++i )
            g<<TAB1 "Ta const a"<<i<<" = vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB1 "Tb const b"<<i<<j<<" = mat_traits<B>::template read_element<"<<i<<','<<j<<">(b);" NL;
        g<<
            TAB1 "typedef typename deduce_vec2<A,B,"<<r<<">::type R;" NL
            TAB1 "BOOST_QVM_STATIC_ASSERT(vec_traits<R>::dim=="<<r<<");" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=c; ++i )
            {
            g<<TAB1 "write_vec_element<"<<i<<">(r,";
            for( int j=0; j!=r; ++j )
                {
                if( j )
                    g<<'+';
                g<<'a'<<j<<"*b"<<j<<i;
                }
            g<<");" NL;
            }
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,r,c,"operator*",suffix);
        }

    void
    vr_op_va_vb_same_size( output_file & out, int d, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(!op.empty());
        header_vr_va_vb_same_size(out,d,fn);
        out.require_include(INCLUDE_DEDUCE_V);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename deduce_vec2<A,B,"<<d<<">::type R;" NL
            TAB1 "BOOST_QVM_STATIC_ASSERT(vec_traits<R>::dim=="<<d<<");" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "write_vec_element<"<<i<<">(r,vec_traits<A>::template read_element<"<<i<<">(a)"<<op<<"vec_traits<B>::template read_element<"<<i<<">(b));" NL;
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,d,fn,suffix);
        }

    void
    bool_eq_ma_mb( output_file & out, int r, int c, char const * suffix )
        {
        header_bool_ma_mb_same_size(out,r,c,"operator==");
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "return" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<
                    TAB2"mat_traits<A>::template read_element<"<<i<<','<<j<<">(a)==mat_traits<B>::template read_element<"<<i<<','<<j<<">(b)"<<(i!=r-1||j!=c-1?" &&":";")<<NL;
                    ;
        g<<
            TAB1 "}" NL
            ;
        defined(g,r,c,"operator==",suffix);
        }

    void
    bool_eq_va_vb( output_file & out, int d, char const * suffix )
        {
        header_bool_va_vb_same_size(out,d,"operator==");
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "return" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<
                TAB2"vec_traits<A>::template read_element<"<<i<<">(a)==vec_traits<B>::template read_element<"<<i<<">(b)"<<(i!=d-1?" &&":";")<<NL;
                ;
        g<<
            TAB1 "}" NL
            ;
        defined(g,d,"operator==",suffix);
        }

    void
    bool_neq_ma_mb( output_file & out, int r, int c, char const * suffix )
        {
        header_bool_ma_mb_same_size(out,r,c,"operator!=");
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "return" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<
                    TAB2"!(mat_traits<A>::template read_element<"<<i<<','<<j<<">(a)==mat_traits<B>::template read_element<"<<i<<','<<j<<">(b))"<<(i!=r-1||j!=c-1?" ||":";")<<NL;
                    ;
        g<<
            TAB1 "}" NL
            ;
        defined(g,r,c,"operator!=",suffix);
        }

    void
    bool_neq_va_vb( output_file & out, int d, char const * suffix )
        {
        header_bool_va_vb_same_size(out,d,"operator!=");
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "return" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<
                TAB2"!(vec_traits<A>::template read_element<"<<i<<">(a)==vec_traits<B>::template read_element<"<<i<<">(b))"<<(i!=d-1?" ||":";")<<NL;
                ;
        g<<
            TAB1 "}" NL
            ;
        defined(g,d,"operator!=",suffix);
        }

    void
    mr_op_ma_mb_same_size( output_file & out, int r, int c, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(r>0);
        assert(c>0);
        assert(!op.empty());
        header_mr_ma_mb_same_size(out,r,c,fn);
        out.require_include(INCLUDE_DEDUCE_M);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename deduce_mat2<A,B,"<<r<<','<<c<<">::type R;" NL
            TAB1 "BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows=="<<r<<");" NL
            TAB1 "BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols=="<<c<<");" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB1 "write_mat_element<"<<i<<","<<j<<">(r,mat_traits<A>::template read_element<"<<i<<","<<j<<">(a)"<<op<<"mat_traits<B>::template read_element<"<<i<<","<<j<<">(b));" NL;
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,r,c,fn,suffix);
        }

    void
    ma_op_ma_mb_same_size( output_file & out, int r, int c, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(!op.empty());
        header_ma_mb_same_size(out,r,c,fn);
        std::ostream & g=out.stream();
        g<<TAB1 "{" NL;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB1 "write_mat_element<"<<i<<","<<j<<">(a,mat_traits<A>::template read_element<"<<i<<","<<j<<">(a)"<<op<<"mat_traits<B>::template read_element<"<<i<<","<<j<<">(b));" NL;
        g<<
            TAB1 "return a;" NL
            TAB1 "}" NL
            ;
        defined(g,r,c,fn,suffix);
        }

    void
    va_op_va_vb_same_size( output_file & out, int d, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(!op.empty());
        header_va_vb_same_size(out,d,fn);
        std::ostream & g=out.stream();
        g<<TAB1 "{" NL;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "write_vec_element<"<<i<<">(a,vec_traits<A>::template read_element<"<<i<<">(a)"<<op<<"vec_traits<B>::template read_element<"<<i<<">(b));" NL;
        g<<
            TAB1 "return a;" NL
            TAB1 "}" NL
            ;
        defined(g,d,fn,suffix);
        }

    void
    mr_op_ma( output_file & out, int r, int c, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(!op.empty());
        header_mr_ma(out,r,c,fn);
        out.require_include(INCLUDE_DEDUCE_M);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename deduce_mat<A>::type R;" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB1 "write_mat_element<"<<i<<","<<j<<">(r,"<<op<<"mat_traits<A>::template read_element<"<<i<<","<<j<<">(a));" NL;
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,r,c,fn,suffix);
        }

    void
    vr_op_va( output_file & out, int d, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(!op.empty());
        header_vr_va(out,d,fn);
        out.require_include(INCLUDE_DEDUCE_V);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename deduce_vec<A>::type R;" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "write_vec_element<"<<i<<">(r,"<<op<<"vec_traits<A>::template read_element<"<<i<<">(a));" NL;
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,d,fn,suffix);
        }

    void
    mr_op_ma_sb( output_file & out, int r, int c, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(!op.empty());
        header_mr_ma_sb(out,r,c,fn);
        out.require_include(INCLUDE_DEDUCE_M);
        out.require_include(INCLUDE_DEDUCE_V);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename deduce_mat2<A,B,mat_traits<A>::rows,mat_traits<A>::cols>::type R;" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB1 "write_mat_element<"<<i<<","<<j<<">(r,mat_traits<A>::template read_element<"<<i<<","<<j<<">(a)"<<op<<"b);" NL;
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,r,c,fn,suffix);
        }

    void
    mr_op_sa_mb( output_file & out, int r, int c, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(!op.empty());
        header_mr_sa_mb(out,r,c,fn);
        out.require_include(INCLUDE_DEDUCE_M);
        out.require_include(INCLUDE_DEDUCE_V);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename deduce_mat2<A,B,mat_traits<B>::rows,mat_traits<B>::cols>::type R;" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB1 "write_mat_element<"<<i<<","<<j<<">(r,a"<<op<<"mat_traits<B>::template read_element<"<<i<<","<<j<<">(b));" NL;
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,r,c,fn,suffix);
        }

    void
    vr_op_va_sb( output_file & out, int d, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(!op.empty());
        header_vr_va_sb(out,d,fn);
        out.require_include(INCLUDE_DEDUCE_V);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename deduce_vec2<A,B,vec_traits<A>::dim>::type R;" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "write_vec_element<"<<i<<">(r,vec_traits<A>::template read_element<"<<i<<">(a)"<<op<<"b);" NL;
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,d,fn,suffix);
        }

    void
    vr_op_sa_vb( output_file & out, int d, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(!op.empty());
        header_vr_sa_vb(out,d,fn);
        out.require_include(INCLUDE_DEDUCE_V);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename deduce_vec2<A,B,vec_traits<B>::dim>::type R;" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "write_vec_element<"<<i<<">(r,a"<<op<<"vec_traits<B>::template read_element<"<<i<<">(b));" NL;
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,d,fn,suffix);
        }

    void
    ma_op_ma_sb( output_file & out, int r, int c, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(!op.empty());
        header_ma_sb(out,r,c,fn);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB1 "write_mat_element<"<<i<<","<<j<<">(a,mat_traits<A>::template read_element<"<<i<<","<<j<<">(a)"<<op<<"b);" NL;
        g<<
            TAB1 "return a;" NL
            TAB1 "}" NL
            ;
        defined(g,r,c,fn,suffix);
        }

    void
    va_op_va_sb( output_file & out, int d, std::string const & fn, std::string const & op, char const * suffix )
        {
        assert(!op.empty());
        header_va_sb(out,d,fn);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "write_vec_element<"<<i<<">(a, vec_traits<A>::template read_element<"<<i<<">(a)"<<op<<"b);" NL;
        g<<
            TAB1 "return a;" NL
            TAB1 "}" NL
            ;
        defined(g,d,fn,suffix);
        }

    void
    ma_assign_ma_mb( output_file & out, int r, int c, char const * suffix )
        {
        header_ma_mb_same_size(out,r,c,"assign");
        out.require_include(INCLUDE_M_TRAITS);
        out.require_include(INCLUDE_CONFIG);
        out.require_include(INCLUDE_ENABLE_IF);
        std::ostream & g=out.stream();
        g<<TAB1 "{" NL;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB1 "write_mat_element<"<<i<<","<<j<<">(a,mat_traits<B>::template read_element<"<<i<<","<<j<<">(b));" NL;
        g<<
            TAB1 "return a;" NL
            TAB1 "}" NL
            ;
        defined(g,r,c,"assign",suffix);
        }

    void
    va_assign_va_vb( output_file & out, int d, char const * suffix )
        {
        header_va_vb_same_size(out,d,"assign");
        out.require_include(INCLUDE_V_TRAITS);
        out.require_include(INCLUDE_CONFIG);
        out.require_include(INCLUDE_ENABLE_IF);
        std::ostream & g=out.stream();
        g<<TAB1 "{" NL;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "write_vec_element<"<<i<<">(a,vec_traits<B>::template read_element<"<<i<<">(b));" NL;
        g<<
            TAB1 "return a;" NL
            TAB1 "}" NL
            ;
        defined(g,d,"assign",suffix);
        }

    void
    mr_convert_to_ma( output_file & out, int r, int c, char const * suffix )
        {
        if( r==c && r>=3 )
            {
            out.require_include(INCLUDE_Q_TRAITS);
            out.require_include(INCLUDE_S_TRAITS);
            }
        std::ostream & g=out.stream();
        g<<
            "template <class R,class A>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename enable_if_c<" NL
            TAB1 "mat_traits<R>::rows=="<<r<<" && mat_traits<A>::rows=="<<r<<" &&" NL
            TAB1 "mat_traits<R>::cols=="<<c<<" && mat_traits<A>::cols=="<<c<<"," NL
            TAB1 "R>::type" NL
            <<"convert_to( A const & a )" NL
            TAB1 "{" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB1 "write_mat_element<"<<i<<","<<j<<">(r,mat_traits<A>::template read_element<"<<i<<","<<j<<">(a));" NL;
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        if( r==c && r>=3 )
            {
            g<<
                NL
                "template <class R,class A>" NL
                "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE" NL
                "typename enable_if_c<" NL
                TAB1 "is_mat<R>::value && is_quat<A>::value &&" NL
                TAB1 "mat_traits<R>::rows=="<<r<<" && mat_traits<R>::cols=="<<c<<"," NL
                TAB1 "R>::type" NL
                "convert_to( A const & q )" NL
                TAB1 "{" NL
                TAB1 "typedef typename mat_traits<R>::scalar_type T;" NL
                TAB1 "T const a=quat_traits<A>::template read_element<0>(q);" NL
                TAB1 "T const b=quat_traits<A>::template read_element<1>(q);" NL
                TAB1 "T const c=quat_traits<A>::template read_element<2>(q);" NL
                TAB1 "T const d=quat_traits<A>::template read_element<3>(q);" NL
                TAB1 "T const bb = b*b;" NL
                TAB1 "T const cc = c*c;" NL
                TAB1 "T const dd = d*d;" NL
                TAB1 "T const bc = b*c;" NL
                TAB1 "T const bd = b*d;" NL
                TAB1 "T const cd = c*d;" NL
                TAB1 "T const ab = a*b;" NL
                TAB1 "T const ac = a*c;" NL
                TAB1 "T const ad = a*d;" NL<<
                (r>3?TAB1 "T const zero = scalar_traits<T>::value(0);" NL:"")<<
                TAB1 "T const one = scalar_traits<T>::value(1);" NL
                TAB1 "T const two = one+one;" NL
                TAB1 "R r;" NL
                TAB1 "write_mat_element<0,0>(r,one - two*(cc+dd));" NL
                TAB1 "write_mat_element<0,1>(r,two*(bc-ad));" NL
                TAB1 "write_mat_element<0,2>(r,two*(bd+ac));" NL
                ;
            for( int i=3; i!=c; ++i )
                g<<TAB1 "write_mat_element<0,"<<i<<">(r,zero);" NL;
            g<<
                TAB1 "write_mat_element<1,0>(r,two*(bc+ad));" NL
                TAB1 "write_mat_element<1,1>(r,one - two*(bb+dd));" NL
                TAB1 "write_mat_element<1,2>(r,two*(cd-ab));" NL
                ;
            for( int i=3; i!=c; ++i )
                g<<TAB1 "write_mat_element<1,"<<i<<">(r,zero);" NL;
            g<<
                TAB1 "write_mat_element<2,0>(r,two*(bd-ac));" NL
                TAB1 "write_mat_element<2,1>(r,two*(cd+ab));" NL
                TAB1 "write_mat_element<2,2>(r,one - two*(bb+cc));" NL
                ;
            for( int i=3; i!=c; ++i )
                g<<TAB1 "write_mat_element<2,"<<i<<">(r,zero);" NL;
            for( int i=3; i!=r; ++i )
                for( int j=0; j!=c; ++j )
                    g<<TAB1 "write_mat_element<"<<i<<","<<j<<">(r,"<<(i==j?"one":"zero")<<");" NL;
            g<<
                TAB1 "return r;" NL
                TAB1 "}" NL
                ;
            }
        defined(g,r,c,"convert_to",suffix);
        }

    void
    vr_convert_to_va( output_file & out, int d, char const * suffix )
        {
        header_vr_va_same_size(out,d,"convert_to");
        std::ostream & g=out.stream();
        g<<TAB1 "{" NL<<
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "write_vec_element<"<<i<<">(r,vec_traits<A>::template read_element<"<<i<<">(a));" NL;
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            ;
        defined(g,d,"convert_to",suffix);
        }

    struct
    del_row_col
        {
        del_row_col const * next;
        int i, j;
        char var;
        explicit
        del_row_col( char var ):
            next(0),
            i(std::numeric_limits<int>::max()),
            j(std::numeric_limits<int>::max()),
            var(var)
            {
            }
        del_row_col( del_row_col const & next, int i, int j ):
            next(&next),
            i(i),
            j(j),
            var(next.var)
            {
            }
        std::pair<int,int>
        idx( std::pair<int,int> const & x ) const
            {
            std::pair<int,int> r(x.first+(x.first>=i),x.second+(x.second>=j));
            if( next )
                return next->idx(r);
            else
                return r;

            }
        void
        operator()( std::ostream & g, int r, int c ) const
            {
            std::pair<int,int> p=idx(std::make_pair(r,c));
            g << var << p.first << p.second;
            }
        };

    void
    determinant_impl( std::ostream & g, int n, del_row_col const & a )
        {
        if( n==1 )
            return a(g,0,0);
        g << "(";
        char const * plus="";
        for( int i=0; i!=n; ++i,plus="+" )
            {
            g<<((i&1)?"-":plus);
            a(g,0,i);
            g<<'*';
            determinant_impl(g,n-1,del_row_col(a,0,i));
            }
        g << ")";
        }

    void
    determinant( output_file & out, int d, char const * suffix )
        {
        header_sr_ma(out,d,d,"determinant");
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename mat_traits<A>::scalar_type T;" NL
            ;
        for( int i=0; i!=d; ++i )
            for( int j=0; j!=d; ++j )
                g<<TAB1<<"T const a"<<i<<j<<"=mat_traits<A>::template read_element<"<<i<<','<<j<<">(a);" NL;
        g<<TAB1 "T det=";
        determinant_impl(g,d,del_row_col('a'));
        g<<";" NL;
        g<<
            TAB1 "return det;" NL
            TAB1 "}" NL
            ;
        defined(g,d,"determinant",suffix);
        }

    void
    inverse_ma( output_file & out, int d, char const * suffix )
        {
        assert(d>1);
        out.require_include(INCLUDE_DEDUCE_M);
        out.require_include(INCLUDE_ASSERT);
        out.require_include(INCLUDE_THROW_EXCEPTION);
        out.require_include(INCLUDE_ERROR);
        std::ostream & g=out.stream();
        g<<
            "template <class A,class B>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "mat_traits<A>::rows=="<<d<<" && mat_traits<A>::cols=="<<d<<" && is_scalar<B>::value," NL
            TAB1 "deduce_mat2<A,B,mat_traits<A>::rows,mat_traits<A>::cols> >::type" NL
            "inverse( A const & a, B det )" NL
            TAB1 "{" NL
            TAB1 "typedef typename mat_traits<A>::scalar_type T;" NL
            TAB1 "BOOST_QVM_ASSERT(det!=scalar_traits<B>::value(0));" NL
            ;
        for( int i=0; i!=d; ++i )
            for( int j=0; j!=d; ++j )
                g<<TAB1 "T const a"<<i<<j<<"=mat_traits<A>::template read_element<"<<i<<','<<j<<">(a);" NL;
        g<<
            TAB1 "T const f=scalar_traits<T>::value(1)/det;" NL
            TAB1 "typedef typename deduce_mat2<A,B,mat_traits<A>::rows,mat_traits<A>::cols>::type R;" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            for( int j=0; j!=d; ++j )
                {
                g<<TAB1 "write_mat_element<"<<i<<','<<j<<">(r,"<<(((i+j)&1)?'-':' ')<<"f*";
                determinant_impl(g,d-1,del_row_col(del_row_col('a'),j,i));
                g<<");" NL;
                }
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            NL
            "template <class A>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename lazy_enable_if_c<" NL
            TAB1 "mat_traits<A>::rows=="<<d<<" && mat_traits<A>::cols=="<<d<<"," NL
            TAB1 "deduce_mat<A> >::type" NL
            "inverse( A const & a )" NL
            TAB1 "{" NL
            TAB1 "typedef typename mat_traits<A>::scalar_type T;" NL
            TAB1 "T det=determinant(a);" NL
            TAB1 "if( det==scalar_traits<T>::value(0) )" NL
            TAB2"BOOST_QVM_THROW_EXCEPTION(zero_determinant_error());" NL
            TAB1 "return inverse(a,det);" NL
            TAB1 "}" NL
            ;
        defined(g,d,"inverse",suffix);
        }

    void
    mag_sqr( output_file & out, int d, char const * suffix )
        {
        header_sr_va(out,d,"mag_sqr");
        out.require_include(INCLUDE_MATH);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename vec_traits<A>::scalar_type T;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "T const a"<<i<<"=vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        g<<TAB1 "T const m2=";
        for( int i=0; i!=d; ++i )
            {
            if( i )
                g<<'+';
            g<<'a'<<i<<"*a"<<i;
            }
        g<<
            ";" NL
            TAB1 "return m2;" NL
            TAB1 "}" NL
            ;
        defined(g,d,"mag_sqr",suffix);
        }

    void
    mag( output_file & out, int d, char const * suffix )
        {
        header_sr_va(out,d,"mag");
        out.require_include(INCLUDE_MATH);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename vec_traits<A>::scalar_type T;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "T const a"<<i<<"=vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        g<<TAB1 "T const m2=";
        for( int i=0; i!=d; ++i )
            {
            if( i )
                g<<'+';
            g<<'a'<<i<<"*a"<<i;
            }
        g<<
            ";" NL
            TAB1 "T const mag=sqrt(m2);" NL
            TAB1 "return mag;" NL
            TAB1 "}" NL
            ;
        defined(g,d,"mag",suffix);
        }

    void
    normalize( output_file & out, int d, char const * suffix )
        {
        header_vr_va(out,d,"normalized");
        out.require_include(INCLUDE_MATH);
        out.require_include(INCLUDE_THROW_EXCEPTION);
        out.require_include(INCLUDE_ERROR);
        out.require_include(INCLUDE_DEDUCE_V);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename vec_traits<A>::scalar_type T;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "T const a"<<i<<"=vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        g<<TAB1 "T const m2=";
        for( int i=0; i!=d; ++i )
            {
            if( i )
                g<<'+';
            g<<'a'<<i<<"*a"<<i;
            }
        g<<
            ";" NL
            TAB1 "if( m2==scalar_traits<typename vec_traits<A>::scalar_type>::value(0) )" NL
            TAB2"BOOST_QVM_THROW_EXCEPTION(zero_magnitude_error());" NL
            TAB1 "T const rm=scalar_traits<T>::value(1)/sqrt(m2);" NL
            TAB1 "typedef typename deduce_vec<A>::type R;" NL
            TAB1 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "write_vec_element<"<<i<<">(r,a"<<i<<"*rm);" NL;
        g<<
            TAB1 "return r;" NL
            TAB1 "}" NL
            NL
            "namespace" NL
            "sfinae" NL
            TAB1 "{" NL
            TAB1 "using ::boost::qvm::normalized;" NL
            TAB1 "}" NL
            NL
            "template <class A>" NL
            "BOOST_QVM_CONSTEXPR BOOST_QVM_INLINE_OPERATIONS" NL
            "typename enable_if_c<" NL
            TAB1 "vec_traits<A>::dim=="<<d<<"," NL
            TAB1 "void>::type" NL
            <<"normalize( A & a )" NL
            TAB1 "{" NL
            TAB1 "typedef typename vec_traits<A>::scalar_type T;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "T const a"<<i<<"=vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        g<<TAB1 "T const m2=";
        for( int i=0; i!=d; ++i )
            {
            if( i )
                g<<'+';
            g<<'a'<<i<<"*a"<<i;
            }
        g<<
            ";" NL
            TAB1 "if( m2==scalar_traits<typename vec_traits<A>::scalar_type>::value(0) )" NL
            TAB2"BOOST_QVM_THROW_EXCEPTION(zero_magnitude_error());" NL
            TAB1 "T const rm=scalar_traits<T>::value(1)/sqrt(m2);" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "write_vec_element<"<<i<<">(a,vec_traits<A>::template read_element<"<<i<<">(a)*rm);" NL;
        g<<TAB1 "}" NL;
        defined(g,d,"normalize",suffix);
        }

    void
    dot( output_file & out, int d, char const * suffix )
        {
        header_sr_va_vb(out,d,"dot");
        out.require_include(INCLUDE_DEDUCE_S);
        out.require_include(INCLUDE_STATIC_ASSERT);
        std::ostream & g=out.stream();
        g<<
            TAB1 "{" NL
            TAB1 "typedef typename vec_traits<A>::scalar_type Ta;" NL
            TAB1 "typedef typename vec_traits<B>::scalar_type Tb;" NL
            TAB1 "typedef typename deduce_scalar<Ta,Tb>::type Tr;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "Ta const a"<<i<<"=vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        for( int i=0; i!=d; ++i )
            g<<TAB1 "Tb const b"<<i<<"=vec_traits<B>::template read_element<"<<i<<">(b);" NL;
        g<<TAB1 "Tr const dot=";
        for( int i=0; i!=d; ++i )
            {
            if( i )
                g<<'+';
            g<<'a'<<i<<"*b"<<i;
            }
        g<<
            ";" NL
            TAB1 "return dot;" NL
            TAB1 "}" NL
            ;
        defined(g,d,"dot",suffix);
        }

    struct
    swizzle_pair
        {
        char ch;
        int idx;
        };

    template <int N>
    void
    swizzle_impl( std::ostream & g, int d, swizzle_pair const (&ids)[N], std::vector<int> const & initial_count )
        {
        assert(d>=2);
        std::vector<int> count(initial_count);
        for( char const * const ref_id[2] = { " const &", " &" };; )
            {
            int max_dim=-100;
            for( int i=0; i!=d; ++i )
                max_dim=std::max(max_dim,ids[count[i]-1].idx);
            if( max_dim<0 )
                {
                g<<
                    "BOOST_QVM_INLINE_TRIVIAL" NL
                    "qvm_detail::sw01_<";
                for( int k=0; k!=d; ++k )
                    g<<(k?",":"")<<"qvm_detail::swizzle_idx<"<<ids[count[k]-1].idx;
                for( int k=0; k!=d; ++k )
                    g<<" >";
                g<<
                    " > const &" NL
                    "_";
                for( int k=0; k!=d; ++k )
                    {
                    char f=ids[count[k]-1].ch;
                    assert(f>='0' && f<='9');
                    g<<f;
                    }
                g<<
                    "()" NL
                    TAB1 "{" NL
                    TAB1 "return *reinterpret_cast<qvm_detail::sw01_<";
                for( int k=0; k!=d; ++k )
                    g<<(k?",":"")<<"qvm_detail::swizzle_idx<"<<ids[count[k]-1].idx;
                for( int k=0; k!=d; ++k )
                    g<<" >";
                g<<
                    " > const *>(qvm_detail::get_null());" NL
                    TAB1 "}" NL;
                }
            else
                for( int rfid=0; rfid<2; ++rfid )
                    {
                    for( int scalar=0; scalar!=2; ++scalar )
                        {
                        if( scalar && max_dim>0 )
                            break;
                        if( scalar )
                            g<<
                                "template <class S>" NL
                                "BOOST_QVM_INLINE_TRIVIAL" NL
                                "typename enable_if_c<" NL
                                TAB1 "is_scalar<S>::value," NL
                                TAB1 "qvm_detail::sws_<S,";
                        else
                            g<<
                                "template <class V>" NL
                                "BOOST_QVM_INLINE_TRIVIAL" NL
                                "typename enable_if_c<" NL
                                TAB1 "is_vec<V>::value && vec_traits<V>::dim>="<<max_dim+1<<"," NL
                                TAB1 "qvm_detail::sw_<V,";
                        for( int k=0; k!=d; ++k )
                            g<<(k?",":"")<<"qvm_detail::swizzle_idx<"<<ids[count[k]-1].idx;
                        for( int k=0; k!=d; ++k )
                            g<<" >";
                        g<<" >"<<ref_id[rfid]<<">::type" NL;
                        for( int k=0; k!=d; ++k )
                            {
                            char f=ids[count[k]-1].ch;
                            if( !k && f>='0' && f<='9' )
                                g<<'_';
                            g<<f;
                            }
                        if( scalar )
                            g<<
                                "( S"<<ref_id[rfid]<<" a )" NL
                                TAB1 "{" NL
                                TAB1 "return reinterpret_cast<qvm_detail::sws_<S,";
                        else
                            g<<
                                "( V"<<ref_id[rfid]<<" a )" NL
                                TAB1 "{" NL
                                TAB1 "return reinterpret_cast<qvm_detail::sw_<V,";
                        for( int k=0; k!=d; ++k )
                            g<<(k?",":"")<<"qvm_detail::swizzle_idx<"<<ids[count[k]-1].idx;
                        for( int k=0; k!=d; ++k )
                            g<<" >";
                        g<<
                            " >"<<ref_id[rfid]<<">(a);" NL
                            TAB1 "}" NL;
                        }
                    }
            int j;
            for( j=0; j!=d; ++j )
                if( --count[j] )
                    break;
                else
                    count[j]=initial_count[j];
            if( j==d )
                break;
            }
        }

    void
    swizzle( output_file & out, int d )
        {
        assert(d>1);
        out.require_include(INCLUDE_CONFIG);
        out.require_include(INCLUDE_SWIZZLE_TRAITS);
        out.require_include(INCLUDE_ENABLE_IF);
        std::ostream & g=out.stream();
        swizzle_pair const swizzle_ids[6] =
            {
                {'X',0},
                {'Y',1},
                {'Z',2},
                {'W',3},
                {'0',-1},
                {'1',-2}
            };
        std::vector<int> initial_count(d,6);
        swizzle_impl(g,d,swizzle_ids,initial_count);
        }

    command_line_options
    parse_command_line( int argc, char const * argv[] )
        {
        class
        next
            {
            char const * const * const argv;
            public:
            int const argc;
            next( int argc, char const * argv[] ):
                argv(argv),
                argc(argc)
                {
                }
            std::string
            operator()( int & i ) const
                {
                assert(i<argc);
                if( ++i==argc )
                    BOOST_THROW_EXCEPTION(bad_command_line() << cmd_arg(argv[i-1]));
                return argv[i];
                }
            } next_token(argc,argv);
        command_line_options r;
        for( int i=1; i!=argc; ++i )
            if( argv[i][0]=='-' )
                {
                char const * arg=argv[i];
                if( arg==std::string("-od") )
                    r.output_directory=next_token(i);
                else if( arg==std::string("-con") )
                    r.con=true;
                else
                    BOOST_THROW_EXCEPTION(bad_command_line() << cmd_arg(arg));
                }
        return r;
        }

    void
    gen( int argc, char const * argv[] )
        {
        command_line_options opt=parse_command_line(argc,argv);
        for( int d=2; d!=5; ++d )
            {
            output_file f(opt);
                {
                char buf[1024];
                sprintf(buf,INCLUDE_MAT_ASSIGN,d);
                f.require_include(buf);
                }
            mr_op_ma_mb_same_size(f,d,d,"operator+","+","mm");
            mr_op_ma_mb_same_size(f,d,1,"operator+","+","mm");
            mr_op_ma_mb_same_size(f,1,d,"operator+","+","mm");
            mr_op_ma_mb_same_size(f,d,d,"operator-","-","mm");
            mr_op_ma_mb_same_size(f,d,1,"operator-","-","mm");
            mr_op_ma_mb_same_size(f,1,d,"operator-","-","mm");
            ma_op_ma_mb_same_size(f,d,d,"operator+=","+","mm");
            ma_op_ma_mb_same_size(f,d,1,"operator+=","+","mm");
            ma_op_ma_mb_same_size(f,1,d,"operator+=","+","mm");
            ma_op_ma_mb_same_size(f,d,d,"operator-=","-","mm");
            ma_op_ma_mb_same_size(f,d,1,"operator-=","-","mm");
            ma_op_ma_mb_same_size(f,1,d,"operator-=","-","mm");
            mr_op_ma_sb(f,d,d,"operator*","*","ms");
            mr_op_sa_mb(f,d,d,"operator*","*","sm");
            mr_op_ma_sb(f,d,1,"operator*","*","ms");
            mr_op_sa_mb(f,d,1,"operator*","*","sm");
            mr_op_ma_sb(f,1,d,"operator*","*","ms");
            mr_op_sa_mb(f,1,d,"operator*","*","sm");
            ma_op_ma_sb(f,d,d,"operator*=","*","ms");
            ma_op_ma_sb(f,d,1,"operator*=","*","ms");
            ma_op_ma_sb(f,1,d,"operator*=","*","ms");
            mr_op_ma_sb(f,d,d,"operator/","/","ms");
            mr_op_sa_mb(f,d,d,"operator/","/","sm");
            mr_op_ma_sb(f,d,1,"operator/","/","ms");
            mr_op_sa_mb(f,d,1,"operator/","/","sm");
            mr_op_ma_sb(f,1,d,"operator/","/","ms");
            ma_op_ma_sb(f,d,d,"operator/=","/","ms");
            ma_op_ma_sb(f,d,1,"operator/=","/","ms");
            ma_op_ma_sb(f,1,d,"operator/=","/","ms");
            mr_convert_to_ma(f,d,d,"m");
            mr_convert_to_ma(f,d,1,"m");
            mr_convert_to_ma(f,1,d,"m");
            bool_eq_ma_mb(f,d,d,"mm");
            bool_eq_ma_mb(f,d,1,"mm");
            bool_eq_ma_mb(f,1,d,"mm");
            bool_neq_ma_mb(f,d,d,"mm");
            bool_neq_ma_mb(f,d,1,"mm");
            bool_neq_ma_mb(f,1,d,"mm");
            mr_op_ma(f,d,d,"operator-","-","m");
            mr_op_ma(f,d,1,"operator-","-","m");
            mr_op_ma(f,1,d,"operator-","-","m");
            determinant(f,d,0);
            inverse_ma(f,d,"m");
            mr_mult_ma_mb(f,d,d,d,"mm");
            ma_mult_ma_mb(f,d,"mm");
            mr_mult_ma_mb(f,d,d,1,"mm");
            mr_mult_ma_mb(f,1,d,d,"mm");
            f.dump("mat_operations"+to_string(d)+".hpp");
            }

        for( int d=2; d!=5; ++d )
            {
            output_file f(opt);
            ma_assign_ma_mb(f,d,d,"mm");
            ma_assign_ma_mb(f,d,1,"mm");
            ma_assign_ma_mb(f,1,d,"mm");
            f.dump("mat_assign"+to_string(d)+".hpp");
            }

        for( int d=2; d!=5; ++d )
            {
            output_file f(opt);
                {
                char buf[1024];
                sprintf(buf,INCLUDE_VEC_ASSIGN,d);
                f.require_include(buf);
                }
            vr_op_va_vb_same_size(f,d,"operator+","+","vv");
            vr_op_va_vb_same_size(f,d,"operator-","-","vv");
            va_op_va_vb_same_size(f,d,"operator+=","+","vv");
            va_op_va_vb_same_size(f,d,"operator-=","-","vv");
            vr_op_va_sb(f,d,"operator*","*","vs");
            vr_op_sa_vb(f,d,"operator*","*","sv");
            va_op_va_sb(f,d,"operator*=","*","vs");
            vr_op_va_sb(f,d,"operator/","/","vs");
            va_op_va_sb(f,d,"operator/=","/","vs");
            vr_convert_to_va(f,d,"v");
            bool_eq_va_vb(f,d,"vv");
            bool_neq_va_vb(f,d,"vv");
            vr_op_va(f,d,"operator-","-","v");
            mag(f,d,"v");
            mag_sqr(f,d,"v");
            normalize(f,d,"v");
            dot(f,d,"vv");
            f.dump("vec_operations"+to_string(d)+".hpp");
            }

        for( int d=2; d!=5; ++d )
            {
            output_file f(opt);
            va_assign_va_vb(f,d,"vv");
            f.dump("vec_assign"+to_string(d)+".hpp");
            }

        for( int d=2; d!=5; ++d )
            {
            output_file f(opt);
            vr_mult_ma_vb(f,d,d,"mv");
            vr_mult_va_mb(f,d,d,"vm");
            f.dump("vec_mat_operations"+to_string(d)+".hpp");
            }

            {
            output_file f(opt);
            swizzle(f,2);
            f.dump("swizzle2.hpp");
            }
            {
            output_file f(opt);
            swizzle(f,3);
            f.dump("swizzle3.hpp");
            }
            {
            output_file f(opt);
            swizzle(f,4);
            f.dump("swizzle4.hpp");
            }
        }
    }

int
main( int argc, char const * argv[] )
    {
    try
        {
        gen(argc,argv);
        }
    catch(
    std::ifstream::failure & )
        {
        std::cerr << "Failed to write generated output file" << std::endl;
        }
    catch(
    ... )
        {
        std::cerr << "Unexpected exception" << std::endl << boost::current_exception_diagnostic_information();
        }
    return 1;
    }
