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

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

#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 TAB3 TAB TAB TAB
#define TAB4 TAB TAB TAB TAB
#define TAB5 TAB TAB TAB 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_INLINE "boost/qvm/inline.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::ostringstream s;
        s << std::setw(2) << std::setfill('0') << std::hex << std::uppercase;
        s<<"BOOST_QVM_";
        for( int i=0; i!=16; ++i )
            s<<(rand()%256);
        return s.str();
        }

    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();
            *out <<
                "//Copyright (c) 2008-2017 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
                "#ifndef " << include_guard << NL
                "#define " << include_guard << 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" NL
                "boost" NL
                TAB1 "{" NL
                TAB1 "namespace" NL
                TAB1 "qvm" NL
                TAB2 "{" NL <<
                out_.str() <<
                TAB2 "}" NL
                TAB1 "}" 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() <<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "mat_traits<A>::rows=="<<r<<" && mat_traits<B>::rows=="<<r<<" &&" NL
            TAB3 "mat_traits<A>::cols=="<<c<<" && mat_traits<B>::cols=="<<c<<"," NL
            TAB3 "deduce_mat2<A,B,"<<r<<','<<c<<"> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "mat_traits<A>::rows=="<<m<<" && mat_traits<B>::rows=="<<n<<" &&" NL
            TAB3 "mat_traits<A>::cols=="<<n<<" && mat_traits<B>::cols=="<<p<<"," NL
            TAB3 "deduce_mat2<A,B,"<<m<<','<<p<<"> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "mat_traits<A>::rows=="<<r<<" && mat_traits<A>::cols=="<<c<<" &&" NL
            TAB3 "vec_traits<B>::dim=="<<c<<"," NL
            TAB3 "deduce_vec2<A,B,"<<c<<"> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "mat_traits<B>::rows=="<<r<<" && mat_traits<B>::cols=="<<c<<" &&" NL
            TAB3 "vec_traits<A>::dim=="<<c<<"," NL
            TAB3 "deduce_vec2<A,B,"<<r<<"> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "vec_traits<A>::dim=="<<d<<" && vec_traits<B>::dim=="<<d<<"," NL
            TAB3 "deduce_vec2<A,B,"<<d<<"> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename enable_if_c<" NL
            TAB3 "mat_traits<A>::rows=="<<r<<" && mat_traits<B>::rows=="<<r<<" &&" NL
            TAB3 "mat_traits<A>::cols=="<<c<<" && mat_traits<B>::cols=="<<c<<"," NL
            TAB3 "bool>::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename enable_if_c<" NL
            TAB3 "vec_traits<A>::dim=="<<d<<" && vec_traits<B>::dim=="<<d<<"," NL
            TAB2 "bool>::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename enable_if_c<" NL
            TAB3 "mat_traits<A>::rows=="<<r<<" && mat_traits<B>::rows=="<<r<<" &&" NL
            TAB3 "mat_traits<A>::cols=="<<c<<" && mat_traits<B>::cols=="<<c<<"," NL
            TAB3 "A &>::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename enable_if_c<" NL
            TAB3 "vec_traits<A>::dim=="<<d<<" && vec_traits<B>::dim=="<<d<<"," NL
            TAB3 "A &>::type" NL
            TAB2<<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()<<
            TAB2 "template <class A>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename enable_if_c<" NL
            TAB3 "mat_traits<A>::rows=="<<r<<" && mat_traits<A>::cols=="<<c<<"," NL
            TAB3 "typename mat_traits<A>::scalar_type>::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "vec_traits<A>::dim=="<<d<<" && vec_traits<B>::dim=="<<d<<"," NL
            TAB3 "deduce_scalar<typename vec_traits<A>::scalar_type,typename vec_traits<B>::scalar_type> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class A>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename enable_if_c<" NL
            TAB3 "is_vec<A>::value && vec_traits<A>::dim=="<<d<<"," NL
            TAB3 "typename vec_traits<A>::scalar_type>::type" NL
            TAB2<<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()<<
            TAB2 "template <class A>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "mat_traits<A>::rows=="<<r<<" && mat_traits<A>::cols=="<<c<<"," NL
            TAB3 "deduce_mat<A> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class A>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "vec_traits<A>::dim=="<<d<<"," NL
            TAB3 "deduce_vec<A> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class R,class A>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename enable_if_c<" NL
            TAB3 "is_vec<A>::value &&" NL
            TAB3 "vec_traits<R>::dim=="<<d<<" && vec_traits<A>::dim=="<<d<<"," NL
            TAB3 "R>::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "mat_traits<A>::rows=="<<r<<" && mat_traits<A>::cols=="<<c<<" && is_scalar<B>::value," NL
            TAB3 "deduce_mat<A> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "is_scalar<A>::value && mat_traits<B>::rows=="<<r<<" && mat_traits<B>::cols=="<<c<<"," NL
            TAB3 "deduce_mat<B> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "vec_traits<A>::dim=="<<d<<" && is_scalar<B>::value," NL
            TAB3 "deduce_vec<A> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "is_scalar<A>::value && vec_traits<B>::dim=="<<d<<"," NL
            TAB3 "deduce_vec<B> >::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename enable_if_c<" NL
            TAB3 "mat_traits<A>::rows=="<<r<<" && mat_traits<A>::cols=="<<c<<" && is_scalar<B>::value," NL
            TAB3 "A &>::type" NL
            TAB2<<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()<<
            TAB2 "template <class A,class  B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename enable_if_c<" NL
            TAB3 "vec_traits<A>::dim=="<<d<<" && is_scalar<B>::value," NL
            TAB3 "A &>::type" NL
            TAB2<<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
            TAB2 "namespace" NL
            TAB2 "sfinae" NL
            TAB3 "{" NL
            TAB3 "using ::boost::qvm::"<<fn<<";" NL
            TAB3 "}" NL
            NL
            TAB2 "namespace" NL
            TAB2 "qvm_detail" NL
            TAB3 "{" NL
            TAB3 "template <int R,int CR,int C>" NL
            TAB3 "struct "<<name<<";" NL
            NL
            TAB3 "template <>" NL
            TAB3 "struct" NL
            TAB3<<name<<'<'<<r<<','<<cr<<','<<c<<">" NL
            TAB4"{" NL
            TAB4"static bool const value=true;" NL
            TAB4"};" NL
            TAB3 "}" 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
            TAB2 "namespace" NL
            TAB2 "sfinae" NL
            TAB3 "{" NL
            TAB3 "using ::boost::qvm::"<<fn<<";" NL
            TAB3 "}" NL
            NL
            TAB2 "namespace" NL
            TAB2 "qvm_detail" NL
            TAB3 "{" NL
            TAB3 "template <int R,int C>" NL
            TAB3 "struct "<<name<<";" NL
            NL
            TAB3 "template <>" NL
            TAB3 "struct" NL
            TAB3<<name<<"<"<<r<<","<<c<<">" NL
            TAB4"{" NL
            TAB4"static bool const value=true;" NL
            TAB4"};" NL
            TAB3 "}" 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
            TAB2 "namespace" NL
            TAB2 "sfinae" NL
            TAB3 "{" NL
            TAB3 "using ::boost::qvm::"<<fn<<";" NL
            TAB3 "}" NL
            NL
            TAB2 "namespace" NL
            TAB2 "qvm_detail" NL
            TAB3 "{" NL
            TAB3 "template <int D>" NL
            TAB3 "struct "<<name<<";" NL
            NL
            TAB3 "template <>" NL
            TAB3 "struct" NL
            TAB3<<name<<"<"<<d<<">" NL
            TAB4"{" NL
            TAB4"static bool const value=true;" NL
            TAB4"};" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename mat_traits<A>::scalar_type Ta;" NL
            TAB3 "typedef typename mat_traits<B>::scalar_type Tb;" NL
            ;
        for( int i=0; i!=m; ++i )
            for( int j=0; j!=n; ++j )
                g<<TAB3 "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<<TAB3 "Tb const b"<<i<<j<<" = mat_traits<B>::template read_element<"<<i<<','<<j<<">(b);" NL;
        g<<
            TAB3 "typedef typename deduce_mat2<A,B,"<<m<<','<<p<<">::type R;" NL
            TAB3 "BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows=="<<m<<");" NL
            TAB3 "BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols=="<<p<<");" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=m; ++i )
            for( int j=0; j!=p; ++j )
                {
                g<<TAB3 "mat_traits<R>::template write_element<"<<i<<","<<j<<">(r)=";
                for( int k=0; k!=n; ++k )
                    {
                    if( k )
                        g<<'+';
                    g<<'a'<<i<<k<<"*b"<<k<<j;
                    }
                g<<";" NL;
                }
        g<<
            TAB3 "return r;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename mat_traits<A>::scalar_type Ta;" NL
            TAB3 "typedef typename mat_traits<B>::scalar_type Tb;" NL
            ;
        for( int i=0; i!=d; ++i )
            for( int j=0; j!=d; ++j )
                g<<TAB3 "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<<TAB3 "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<<TAB3 "mat_traits<A>::template write_element<"<<i<<","<<j<<">(a)=";
                for( int k=0; k!=d; ++k )
                    {
                    if( k )
                        g<<'+';
                    g<<'a'<<i<<k<<"*b"<<k<<j;
                    }
                g<<";" NL;
                }
        g<<
            TAB3 "return a;" NL
            TAB3 "}" 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_INLINE);
        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<<
            TAB3 "{" NL
            TAB3 "typedef typename mat_traits<A>::scalar_type Ta;" NL
            TAB3 "typedef typename vec_traits<B>::scalar_type Tb;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB3 "Ta const a"<<i<<j<<" = mat_traits<A>::template read_element<"<<i<<','<<j<<">(a);" NL;
        for( int i=0; i!=c; ++i )
            g<<TAB3 "Tb const b"<<i<<" = vec_traits<B>::template read_element<"<<i<<">(b);" NL;
        g<<
            TAB3 "typedef typename deduce_vec2<A,B,"<<c<<">::type R;" NL
            TAB3 "BOOST_QVM_STATIC_ASSERT(vec_traits<R>::dim=="<<c<<");" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            {
            g<<TAB3 "vec_traits<R>::template write_element<"<<i<<">(r)=";
            for( int j=0; j!=c; ++j )
                {
                if( j )
                    g<<'+';
                g<<'a'<<i<<j<<"*b"<<j;
                }
            g<<";" NL;
            }
        g<<
            TAB3 "return r;" NL
            TAB3 "}" 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_INLINE);
        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<<
            TAB3 "{" NL
            TAB3 "typedef typename vec_traits<A>::scalar_type Ta;" NL
            TAB3 "typedef typename mat_traits<B>::scalar_type Tb;" NL
            ;
        for( int i=0; i!=r; ++i )
            g<<TAB3 "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<<TAB3 "Tb const b"<<i<<j<<" = mat_traits<B>::template read_element<"<<i<<','<<j<<">(b);" NL;
        g<<
            TAB3 "typedef typename deduce_vec2<A,B,"<<r<<">::type R;" NL
            TAB3 "BOOST_QVM_STATIC_ASSERT(vec_traits<R>::dim=="<<r<<");" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=c; ++i )
            {
            g<<TAB3 "vec_traits<R>::template write_element<"<<i<<">(r)=";
            for( int j=0; j!=r; ++j )
                {
                if( j )
                    g<<'+';
                g<<'a'<<j<<"*b"<<j<<i;
                }
            g<<";" NL;
            }
        g<<
            TAB3 "return r;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename deduce_vec2<A,B,"<<d<<">::type R;" NL
            TAB3 "BOOST_QVM_STATIC_ASSERT(vec_traits<R>::dim=="<<d<<");" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "vec_traits<R>::template write_element<"<<i<<">(r)=vec_traits<A>::template read_element<"<<i<<">(a)"<<op<<"vec_traits<B>::template read_element<"<<i<<">(b);" NL;
        g<<
            TAB3 "return r;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "return" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<
                    TAB4"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<<
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "return" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<
                TAB4"vec_traits<A>::template read_element<"<<i<<">(a)==vec_traits<B>::template read_element<"<<i<<">(b)"<<(i!=d-1?" &&":";")<<NL;
                ;
        g<<
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "return" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<
                    TAB4"!(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<<
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "return" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<
                TAB4"!(vec_traits<A>::template read_element<"<<i<<">(a)==vec_traits<B>::template read_element<"<<i<<">(b))"<<(i!=d-1?" ||":";")<<NL;
                ;
        g<<
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename deduce_mat2<A,B,"<<r<<','<<c<<">::type R;" NL
            TAB3 "BOOST_QVM_STATIC_ASSERT(mat_traits<R>::rows=="<<r<<");" NL
            TAB3 "BOOST_QVM_STATIC_ASSERT(mat_traits<R>::cols=="<<c<<");" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB3 "mat_traits<R>::template write_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<<
            TAB3 "return r;" NL
            TAB3 "}" 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<<TAB3 "{" NL;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB3 "mat_traits<A>::template write_element<"<<i<<","<<j<<">(a)"<<op<<"mat_traits<B>::template read_element<"<<i<<","<<j<<">(b);" NL;
        g<<
            TAB3 "return a;" NL
            TAB3 "}" 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<<TAB3 "{" NL;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "vec_traits<A>::template write_element<"<<i<<">(a)"<<op<<"vec_traits<B>::template read_element<"<<i<<">(b);" NL;
        g<<
            TAB3 "return a;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename deduce_mat<A>::type R;" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB3 "mat_traits<R>::template write_element<"<<i<<","<<j<<">(r)="<<op<<"mat_traits<A>::template read_element<"<<i<<","<<j<<">(a);" NL;
        g<<
            TAB3 "return r;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename deduce_vec<A>::type R;" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "vec_traits<R>::template write_element<"<<i<<">(r)="<<op<<"vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        g<<
            TAB3 "return r;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename deduce_mat<A>::type R;" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB3 "mat_traits<R>::template write_element<"<<i<<","<<j<<">(r)=mat_traits<A>::template read_element<"<<i<<","<<j<<">(a)"<<op<<"b;" NL;
        g<<
            TAB3 "return r;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename deduce_mat<B>::type R;" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB3 "mat_traits<R>::template write_element<"<<i<<","<<j<<">(r)=a"<<op<<"mat_traits<B>::template read_element<"<<i<<","<<j<<">(b);" NL;
        g<<
            TAB3 "return r;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename deduce_vec<A>::type R;" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "vec_traits<R>::template write_element<"<<i<<">(r)=vec_traits<A>::template read_element<"<<i<<">(a)"<<op<<"b;" NL;
        g<<
            TAB3 "return r;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename deduce_vec<B>::type R;" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "vec_traits<R>::template write_element<"<<i<<">(r)=a"<<op<<"vec_traits<B>::template read_element<"<<i<<">(b);" NL;
        g<<
            TAB3 "return r;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB3 "mat_traits<A>::template write_element<"<<i<<","<<j<<">(a)"<<op<<"b;" NL;
        g<<
            TAB3 "return a;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "vec_traits<A>::template write_element<"<<i<<">(a)"<<op<<"b;" NL;
        g<<
            TAB3 "return a;" NL
            TAB3 "}" 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_INLINE);
        out.require_include(INCLUDE_ENABLE_IF);
        std::ostream & g=out.stream();
        g<<TAB3 "{" NL;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB3 "mat_traits<A>::template write_element<"<<i<<","<<j<<">(a)=mat_traits<B>::template read_element<"<<i<<","<<j<<">(b);" NL;
        g<<
            TAB3 "return a;" NL
            TAB3 "}" 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_INLINE);
        out.require_include(INCLUDE_ENABLE_IF);
        std::ostream & g=out.stream();
        g<<TAB3 "{" NL;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "vec_traits<A>::template write_element<"<<i<<">(a)=vec_traits<B>::template read_element<"<<i<<">(b);" NL;
        g<<
            TAB3 "return a;" NL
            TAB3 "}" 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<<
            TAB2 "template <class R,class A>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename enable_if_c<" NL
            TAB3 "mat_traits<R>::rows=="<<r<<" && mat_traits<A>::rows=="<<r<<" &&" NL
            TAB3 "mat_traits<R>::cols=="<<c<<" && mat_traits<A>::cols=="<<c<<"," NL
            TAB3 "R>::type" NL
            TAB2<<"convert_to( A const & a )" NL
            TAB3 "{" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=r; ++i )
            for( int j=0; j!=c; ++j )
                g<<TAB3 "mat_traits<R>::template write_element<"<<i<<","<<j<<">(r) = mat_traits<A>::template read_element<"<<i<<","<<j<<">(a);" NL;
        g<<
            TAB3 "return r;" NL
            TAB3 "}" NL
            ;
        if( r==c && r>=3 )
            {
            g<<
                NL
                TAB2 "template <class R,class A>" NL
                TAB2 "BOOST_QVM_INLINE" NL
                TAB2 "typename enable_if_c<" NL
                TAB3 "is_mat<R>::value && is_quat<A>::value &&" NL
                TAB3 "mat_traits<R>::rows=="<<r<<" && mat_traits<R>::cols=="<<c<<"," NL
                TAB3 "R>::type" NL
                TAB2 "convert_to( A const & q )" NL
                TAB3 "{" NL
                TAB3 "typedef typename mat_traits<R>::scalar_type T;" NL
                TAB3 "T const a=quat_traits<A>::template read_element<0>(q);" NL
                TAB3 "T const b=quat_traits<A>::template read_element<1>(q);" NL
                TAB3 "T const c=quat_traits<A>::template read_element<2>(q);" NL
                TAB3 "T const d=quat_traits<A>::template read_element<3>(q);" NL
                TAB3 "T const bb = b*b;" NL
                TAB3 "T const cc = c*c;" NL
                TAB3 "T const dd = d*d;" NL
                TAB3 "T const bc = b*c;" NL
                TAB3 "T const bd = b*d;" NL
                TAB3 "T const cd = c*d;" NL
                TAB3 "T const ab = a*b;" NL
                TAB3 "T const ac = a*c;" NL
                TAB3 "T const ad = a*d;" NL<<
                (r>3?TAB3 "T const zero = scalar_traits<T>::value(0);" NL:"")<<
                TAB3 "T const one = scalar_traits<T>::value(1);" NL
                TAB3 "T const two = one+one;" NL
                TAB3 "R r;" NL
                TAB3 "mat_traits<R>::template write_element<0,0>(r) = one - two*(cc+dd);" NL
                TAB3 "mat_traits<R>::template write_element<0,1>(r) = two*(bc-ad);" NL
                TAB3 "mat_traits<R>::template write_element<0,2>(r) = two*(bd+ac);" NL
                ;
            for( int i=3; i!=c; ++i )
                g<<TAB3 "mat_traits<R>::template write_element<0,"<<i<<">(r) = zero;" NL;
            g<<
                TAB3 "mat_traits<R>::template write_element<1,0>(r) = two*(bc+ad);" NL
                TAB3 "mat_traits<R>::template write_element<1,1>(r) = one - two*(bb+dd);" NL
                TAB3 "mat_traits<R>::template write_element<1,2>(r) = two*(cd-ab);" NL
                ;
            for( int i=3; i!=c; ++i )
                g<<TAB3 "mat_traits<R>::template write_element<1,"<<i<<">(r) = zero;" NL;
            g<<
                TAB3 "mat_traits<R>::template write_element<2,0>(r) = two*(bd-ac);" NL
                TAB3 "mat_traits<R>::template write_element<2,1>(r) = two*(cd+ab);" NL
                TAB3 "mat_traits<R>::template write_element<2,2>(r) = one - two*(bb+cc);" NL
                ;
            for( int i=3; i!=c; ++i )
                g<<TAB3 "mat_traits<R>::template write_element<2,"<<i<<">(r) = zero;" NL;
            for( int i=3; i!=r; ++i )
                for( int j=0; j!=c; ++j )
                    g<<TAB3 "mat_traits<R>::template write_element<"<<i<<","<<j<<">(r) = "<<(i==j?"one":"zero")<<";" NL;
            g<<
                TAB3 "return r;" NL
                TAB3 "}" 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<<TAB3 "{" NL<<
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "vec_traits<R>::template write_element<"<<i<<">(r)=vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        g<<
            TAB3 "return r;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename mat_traits<A>::scalar_type T;" NL
            ;
        for( int i=0; i!=d; ++i )
            for( int j=0; j!=d; ++j )
                g<<TAB3<<"T const a"<<i<<j<<"=mat_traits<A>::template read_element<"<<i<<','<<j<<">(a);" NL;
        g<<TAB3 "T det=";
        determinant_impl(g,d,del_row_col('a'));
        g<<";" NL;
        g<<
            TAB3 "return det;" NL
            TAB3 "}" 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<<
            TAB2 "template <class A,class B>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "mat_traits<A>::rows=="<<d<<" && mat_traits<A>::cols=="<<d<<" && is_scalar<B>::value," NL
            TAB3 "deduce_mat<A> >::type" NL
            TAB2 "inverse( A const & a, B det )" NL
            TAB3 "{" NL
            TAB3 "typedef typename mat_traits<A>::scalar_type T;" NL
            TAB3 "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<<TAB3 "T const a"<<i<<j<<"=mat_traits<A>::template read_element<"<<i<<','<<j<<">(a);" NL;
        g<<
            TAB3 "T const f=scalar_traits<T>::value(1)/det;" NL
            TAB3 "typedef typename deduce_mat<A>::type R;" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            for( int j=0; j!=d; ++j )
                {
                g<<TAB3 "mat_traits<R>::template write_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<<
            TAB3 "return r;" NL
            TAB3 "}" NL
            NL
            TAB2 "template <class A>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename lazy_enable_if_c<" NL
            TAB3 "mat_traits<A>::rows=="<<d<<" && mat_traits<A>::cols=="<<d<<"," NL
            TAB3 "deduce_mat<A> >::type" NL
            TAB2 "inverse( A const & a )" NL
            TAB3 "{" NL
            TAB3 "typedef typename mat_traits<A>::scalar_type T;" NL
            TAB3 "T det=determinant(a);" NL
            TAB3 "if( det==scalar_traits<T>::value(0) )" NL
            TAB4"BOOST_QVM_THROW_EXCEPTION(zero_determinant_error());" NL
            TAB3 "return inverse(a,det);" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename vec_traits<A>::scalar_type T;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "T const a"<<i<<"=vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        g<<TAB3 "T const m2=";
        for( int i=0; i!=d; ++i )
            {
            if( i )
                g<<'+';
            g<<'a'<<i<<"*a"<<i;
            }
        g<<
            ";" NL
            TAB3 "return m2;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename vec_traits<A>::scalar_type T;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "T const a"<<i<<"=vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        g<<TAB3 "T const m2=";
        for( int i=0; i!=d; ++i )
            {
            if( i )
                g<<'+';
            g<<'a'<<i<<"*a"<<i;
            }
        g<<
            ";" NL
            TAB3 "T const mag=sqrt<T>(m2);" NL
            TAB3 "return mag;" NL
            TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename vec_traits<A>::scalar_type T;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "T const a"<<i<<"=vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        g<<TAB3 "T const m2=";
        for( int i=0; i!=d; ++i )
            {
            if( i )
                g<<'+';
            g<<'a'<<i<<"*a"<<i;
            }
        g<<
            ";" NL
            TAB3 "if( m2==scalar_traits<typename vec_traits<A>::scalar_type>::value(0) )" NL
            TAB4"BOOST_QVM_THROW_EXCEPTION(zero_magnitude_error());" NL
            TAB3 "T const rm=scalar_traits<T>::value(1)/sqrt<T>(m2);" NL
            TAB3 "typedef typename deduce_vec<A>::type R;" NL
            TAB3 "R r;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "vec_traits<R>::template write_element<"<<i<<">(r)=a"<<i<<"*rm;" NL;
        g<<
            TAB3 "return r;" NL
            TAB3 "}" NL
            NL
            TAB2 "namespace" NL
            TAB2 "sfinae" NL
            TAB3 "{" NL
            TAB3 "using ::boost::qvm::normalized;" NL
            TAB3 "}" NL
            NL
            TAB2 "template <class A>" NL
            TAB2 "BOOST_QVM_INLINE_OPERATIONS" NL
            TAB2 "typename enable_if_c<" NL
            TAB3 "vec_traits<A>::dim=="<<d<<"," NL
            TAB3 "void>::type" NL
            TAB2<<"normalize( A & a )" NL
            TAB3 "{" NL
            TAB3 "typedef typename vec_traits<A>::scalar_type T;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "T const a"<<i<<"=vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        g<<TAB3 "T const m2=";
        for( int i=0; i!=d; ++i )
            {
            if( i )
                g<<'+';
            g<<'a'<<i<<"*a"<<i;
            }
        g<<
            ";" NL
            TAB3 "if( m2==scalar_traits<typename vec_traits<A>::scalar_type>::value(0) )" NL
            TAB4"BOOST_QVM_THROW_EXCEPTION(zero_magnitude_error());" NL
            TAB3 "T const rm=scalar_traits<T>::value(1)/sqrt<T>(m2);" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "vec_traits<A>::template write_element<"<<i<<">(a)*=rm;" NL;
        g<<TAB3 "}" 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<<
            TAB3 "{" NL
            TAB3 "typedef typename vec_traits<A>::scalar_type Ta;" NL
            TAB3 "typedef typename vec_traits<B>::scalar_type Tb;" NL
            TAB3 "typedef typename deduce_scalar<Ta,Tb>::type Tr;" NL
            ;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "Ta const a"<<i<<"=vec_traits<A>::template read_element<"<<i<<">(a);" NL;
        for( int i=0; i!=d; ++i )
            g<<TAB3 "Tb const b"<<i<<"=vec_traits<B>::template read_element<"<<i<<">(b);" NL;
        g<<TAB3 "Tr const dot=";
        for( int i=0; i!=d; ++i )
            {
            if( i )
                g<<'+';
            g<<'a'<<i<<"*b"<<i;
            }
        g<<
            ";" NL
            TAB3 "return dot;" NL
            TAB3 "}" 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<<
                    TAB2 "BOOST_QVM_INLINE_TRIVIAL" NL
                    TAB2 "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
                    TAB2 "_";
                for( int k=0; k!=d; ++k )
                    {
                    char f=ids[count[k]-1].ch;
                    assert(f>='0' && f<='9');
                    g<<f;
                    }
                g<<
                    "()" NL
                    TAB3 "{" NL
                    TAB3 "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
                    TAB3 "}" 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<<
                                TAB2 "template <class S>" NL
                                TAB2 "BOOST_QVM_INLINE_TRIVIAL" NL
                                TAB2 "typename enable_if_c<" NL
                                TAB3 "is_scalar<S>::value," NL
                                TAB3 "qvm_detail::sws_<S,";
                        else
                            g<<
                                TAB2 "template <class V>" NL
                                TAB2 "BOOST_QVM_INLINE_TRIVIAL" NL
                                TAB2 "typename enable_if_c<" NL
                                TAB3 "is_vec<V>::value && vec_traits<V>::dim>="<<max_dim+1<<"," NL
                                TAB3 "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 TAB2;
                        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
                                TAB3 "{" NL
                                TAB3 "return reinterpret_cast<qvm_detail::sws_<S,";
                        else
                            g<<
                                "( V"<<ref_id[rfid]<<" a )" NL
                                TAB3 "{" NL
                                TAB3 "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
                            TAB3 "}" 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_INLINE);
        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[] ):
                argc(argc),
                argv(argv)
                {
                }
            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;
    }
