//
//  Copyright (c) 2000-2002
//  Joerg Walter, Mathias Koch
//
//  Permission to use, copy, modify, distribute and sell this software
//  and its documentation for any purpose is hereby granted without fee,
//  provided that the above copyright notice appear in all copies and
//  that both that copyright notice and this permission notice appear
//  in supporting documentation.  The authors make no representations
//  about the suitability of this software for any purpose.
//  It is provided "as is" without express or implied warranty.
//
//  The authors gratefully acknowledge the support of
//  GeNeSys mbH & Co. KG in producing this work.
//

#ifndef _BOOST_UBLAS_VECTOR_PROXY_
#define _BOOST_UBLAS_VECTOR_PROXY_

#include <boost/numeric/ublas/vector_expression.hpp>
#include <boost/numeric/ublas/detail/vector_assign.hpp>
#include <boost/numeric/ublas/detail/temporary.hpp>

// Iterators based on ideas of Jeremy Siek

namespace boost { namespace numeric { namespace ublas {

    // Vector based range class
    template<class V>
    class vector_range:
        public vector_expression<vector_range<V> > {

        typedef vector_range<V> self_type;
    public:
#ifdef BOOST_UBLAS_ENABLE_PROXY_SHORTCUTS
        using vector_expression<self_type>::operator ();
#endif
        typedef const V const_vector_type;
        typedef V vector_type;
        typedef typename V::size_type size_type;
        typedef typename V::difference_type difference_type;
        typedef typename V::value_type value_type;
        typedef typename V::const_reference const_reference;
        typedef typename boost::mpl::if_<boost::is_const<V>,
                                          typename V::const_reference,
                                          typename V::reference>::type reference;
        typedef typename boost::mpl::if_<boost::is_const<V>,
                                          typename V::const_closure_type,
                                          typename V::closure_type>::type vector_closure_type;
        typedef basic_range<size_type, difference_type> range_type;
        typedef const self_type const_closure_type;
        typedef self_type closure_type;
        typedef typename storage_restrict_traits<typename V::storage_category,
                                                 dense_proxy_tag>::storage_category storage_category;

        // Construction and destruction
        BOOST_UBLAS_INLINE
        vector_range (vector_type &data, const range_type &r):
            data_ (data), r_ (r.preprocess (data.size ())) {
            // Early checking of preconditions here.
            // BOOST_UBLAS_CHECK (r_.start () <= data_.size () &&
            //                   r_.start () + r_.size () <= data_.size (), bad_index ());
        }
        BOOST_UBLAS_INLINE
        vector_range (const vector_closure_type &data, const range_type &r, bool):
            data_ (data), r_ (r.preprocess (data.size ())) {
            // Early checking of preconditions here.
            // BOOST_UBLAS_CHECK (r_.start () <= data_.size () &&
            //                    r_.start () + r_.size () <= data_.size (), bad_index ());
        }

        // Accessors
        BOOST_UBLAS_INLINE
        size_type start () const {
            return r_.start ();
        }
        BOOST_UBLAS_INLINE
        size_type size () const {
            return r_.size ();
        }

        // Storage accessors
        BOOST_UBLAS_INLINE
        const vector_closure_type &data () const {
            return data_;
        }
        BOOST_UBLAS_INLINE
        vector_closure_type &data () {
            return data_;
        }

        // Element access
#ifndef BOOST_UBLAS_PROXY_CONST_MEMBER
        BOOST_UBLAS_INLINE
        const_reference operator () (size_type i) const {
            return data_ (r_ (i));
        }
        BOOST_UBLAS_INLINE
        reference operator () (size_type i) {
            return data_ (r_ (i));
        }

        BOOST_UBLAS_INLINE
        const_reference operator [] (size_type i) const {
            return (*this) (i);
        }
        BOOST_UBLAS_INLINE
        reference operator [] (size_type i) {
            return (*this) (i);
        }
#else
        BOOST_UBLAS_INLINE
        reference operator () (size_type i) const {
            return data_ (r_ (i));
        }

        BOOST_UBLAS_INLINE
        reference operator [] (size_type i) const {
            return (*this) (i);
        }
#endif

        // ISSUE can this be done in free project function?
        // Although a const function can create a non-const proxy to a non-const object
        // Critical is that vector_type and data_ (vector_closure_type) are const correct
        BOOST_UBLAS_INLINE
        vector_range<vector_type> project (const range_type &r) const {
            return vector_range<vector_type> (data_, r_.compose (r.preprocess (data_.size ())), false);
        }

        // Assignment
        BOOST_UBLAS_INLINE
        vector_range &operator = (const vector_range &vr) {
            // ISSUE need a temporary, proxy can be overlaping alias
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (vr));
            return *this;
        }
        BOOST_UBLAS_INLINE
        vector_range &assign_temporary (vector_range &vr) {
            // assign elements, proxied container remains the same
            vector_assign<scalar_assign> (*this, vr);
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_range &operator = (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (ae));
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_range &assign (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, ae);
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_range &operator += (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (*this + ae));
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_range &plus_assign (const vector_expression<AE> &ae) {
            vector_assign<scalar_plus_assign> (*this, ae);
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_range &operator -= (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (*this - ae));
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_range &minus_assign (const vector_expression<AE> &ae) {
            vector_assign<scalar_minus_assign> (*this, ae);
            return *this;
        }
        template<class AT>
        BOOST_UBLAS_INLINE
        vector_range &operator *= (const AT &at) {
            vector_assign_scalar<scalar_multiplies_assign> (*this, at);
            return *this;
        }
        template<class AT>
        BOOST_UBLAS_INLINE
        vector_range &operator /= (const AT &at) {
            vector_assign_scalar<scalar_divides_assign> (*this, at);
            return *this;
        }

        // Closure comparison
        BOOST_UBLAS_INLINE
        bool same_closure (const vector_range &vr) const {
            return (*this).data_.same_closure (vr.data_);
        }

        // Comparison
        BOOST_UBLAS_INLINE
        bool operator == (const vector_range &vr) const {
            return (*this).data_ == vr.data_ && r_ == vr.r_;
        }

        // Swapping
        BOOST_UBLAS_INLINE
        void swap (vector_range vr) {
            if (this != &vr) {
                BOOST_UBLAS_CHECK (size () == vr.size (), bad_size ());
                // Sparse ranges may be nonconformant now.
                // std::swap_ranges (begin (), end (), vr.begin ());
                vector_swap<scalar_swap> (*this, vr);
            }
        }
        BOOST_UBLAS_INLINE
        friend void swap (vector_range vr1, vector_range vr2) {
            vr1.swap (vr2);
        }

        // Iterator types
    private:
        typedef typename V::const_iterator const_subiterator_type;
        typedef typename boost::mpl::if_<boost::is_const<V>,
                                          typename V::const_iterator,
                                          typename V::iterator>::type subiterator_type;

    public:
#ifdef BOOST_UBLAS_USE_INDEXED_ITERATOR
        typedef indexed_iterator<vector_range<vector_type>,
                                 typename subiterator_type::iterator_category> iterator;
        typedef indexed_const_iterator<vector_range<vector_type>,
                                       typename const_subiterator_type::iterator_category> const_iterator;
#else
        class const_iterator;
        class iterator;
#endif

        // Element lookup
        BOOST_UBLAS_INLINE
        const_iterator find (size_type i) const {
            const_subiterator_type it (data_.find (start () + i));
#ifdef BOOST_UBLAS_USE_INDEXED_ITERATOR
            return const_iterator (*this, it.index ());
#else
            return const_iterator (*this, it);
#endif
        }
        BOOST_UBLAS_INLINE
        iterator find (size_type i) {
            subiterator_type it (data_.find (start () + i));
#ifdef BOOST_UBLAS_USE_INDEXED_ITERATOR
            return iterator (*this, it.index ());
#else
            return iterator (*this, it);
#endif
        }

#ifndef BOOST_UBLAS_USE_INDEXED_ITERATOR
        class const_iterator:
            public container_const_reference<vector_range>,
            public iterator_base_traits<typename const_subiterator_type::iterator_category>::template
                        iterator_base<const_iterator, value_type>::type {
        public:
            typedef typename const_subiterator_type::difference_type difference_type;
            typedef typename const_subiterator_type::value_type value_type;
            typedef typename const_subiterator_type::reference reference;
            typedef typename const_subiterator_type::pointer pointer;

            // Construction and destruction
            BOOST_UBLAS_INLINE
            const_iterator ():
                container_const_reference<self_type> (), it_ () {}
            BOOST_UBLAS_INLINE
            const_iterator (const self_type &vr, const const_subiterator_type &it):
                container_const_reference<self_type> (vr), it_ (it) {}
            BOOST_UBLAS_INLINE
            const_iterator (const typename self_type::iterator &it):  // ISSUE self_type:: stops VC8 using std::iterator here
                container_const_reference<self_type> (it ()), it_ (it.it_) {}

            // Arithmetic
            BOOST_UBLAS_INLINE
            const_iterator &operator ++ () {
                ++ it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            const_iterator &operator -- () {
                -- it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            const_iterator &operator += (difference_type n) {
                it_ += n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            const_iterator &operator -= (difference_type n) {
                it_ -= n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            difference_type operator - (const const_iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ - it.it_;
            }

            // Dereference
            BOOST_UBLAS_INLINE
            const_reference operator * () const {
                BOOST_UBLAS_CHECK (index () < (*this) ().size (), bad_index ());
                return *it_;
            }
            BOOST_UBLAS_INLINE
            const_reference operator [] (difference_type n) const {
                return *(*this + n);
            }

            // Index
            BOOST_UBLAS_INLINE
            size_type index () const {
                return it_.index () - (*this) ().start ();
            }

            // Assignment
            BOOST_UBLAS_INLINE
            const_iterator &operator = (const const_iterator &it) {
                container_const_reference<self_type>::assign (&it ());
                it_ = it.it_;
                return *this;
            }

            // Comparison
            BOOST_UBLAS_INLINE
            bool operator == (const const_iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ == it.it_;
            }
            BOOST_UBLAS_INLINE
            bool operator < (const const_iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ < it.it_;
            }

        private:
            const_subiterator_type it_;
        };
#endif

        BOOST_UBLAS_INLINE
        const_iterator begin () const {
            return find (0);
        }
        BOOST_UBLAS_INLINE
        const_iterator end () const {
            return find (size ());
        }

#ifndef BOOST_UBLAS_USE_INDEXED_ITERATOR
        class iterator:
            public container_reference<vector_range>,
            public iterator_base_traits<typename subiterator_type::iterator_category>::template
                        iterator_base<iterator, value_type>::type {
        public:
            typedef typename subiterator_type::difference_type difference_type;
            typedef typename subiterator_type::value_type value_type;
            typedef typename subiterator_type::reference reference;
            typedef typename subiterator_type::pointer pointer;

            // Construction and destruction
            BOOST_UBLAS_INLINE
            iterator ():
                container_reference<self_type> (), it_ () {}
            BOOST_UBLAS_INLINE
            iterator (self_type &vr, const subiterator_type &it):
                container_reference<self_type> (vr), it_ (it) {}

            // Arithmetic
            BOOST_UBLAS_INLINE
            iterator &operator ++ () {
                ++ it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            iterator &operator -- () {
                -- it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            iterator &operator += (difference_type n) {
                it_ += n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            iterator &operator -= (difference_type n) {
                it_ -= n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            difference_type operator - (const iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ - it.it_;
            }

            // Dereference
            BOOST_UBLAS_INLINE
            reference operator * () const {
                BOOST_UBLAS_CHECK (index () < (*this) ().size (), bad_index ());
                return *it_;
            }
            BOOST_UBLAS_INLINE
            reference operator [] (difference_type n) const {
                return *(*this + n);
            }

            // Index
            BOOST_UBLAS_INLINE
            size_type index () const {
                return it_.index () - (*this) ().start ();
            }

            // Assignment
            BOOST_UBLAS_INLINE
            iterator &operator = (const iterator &it) {
                container_reference<self_type>::assign (&it ());
                it_ = it.it_;
                return *this;
            }

            // Comparison
            BOOST_UBLAS_INLINE
            bool operator == (const iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ == it.it_;
            }
            BOOST_UBLAS_INLINE
            bool operator < (const iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ < it.it_;
            }

        private:
            subiterator_type it_;

            friend class const_iterator;
        };
#endif

        BOOST_UBLAS_INLINE
        iterator begin () {
            return find (0);
        }
        BOOST_UBLAS_INLINE
        iterator end () {
            return find (size ());
        }

        // Reverse iterator
        typedef reverse_iterator_base<const_iterator> const_reverse_iterator;
        typedef reverse_iterator_base<iterator> reverse_iterator;

        BOOST_UBLAS_INLINE
        const_reverse_iterator rbegin () const {
            return const_reverse_iterator (end ());
        }
        BOOST_UBLAS_INLINE
        const_reverse_iterator rend () const {
            return const_reverse_iterator (begin ());
        }
        BOOST_UBLAS_INLINE
        reverse_iterator rbegin () {
            return reverse_iterator (end ());
        }
        BOOST_UBLAS_INLINE
        reverse_iterator rend () {
            return reverse_iterator (begin ());
        }

    private:
        vector_closure_type data_;
        range_type r_;
    };

    // Simple Projections
    template<class V>
    BOOST_UBLAS_INLINE
    vector_range<V> subrange (V &data, typename V::size_type start, typename V::size_type stop) {
        typedef basic_range<typename V::size_type, typename V::difference_type> range_type;
        return vector_range<V> (data, range_type (start, stop));
    }
    template<class V>
    BOOST_UBLAS_INLINE
    vector_range<const V> subrange (const V &data, typename V::size_type start, typename V::size_type stop) {
        typedef basic_range<typename V::size_type, typename V::difference_type> range_type;
        return vector_range<const V> (data, range_type (start, stop));
    }

    // Generic Projections
    template<class V>
    BOOST_UBLAS_INLINE
    vector_range<V> project (V &data, typename vector_range<V>::range_type const &r) {
        return vector_range<V> (data, r);
    }
    template<class V>
    BOOST_UBLAS_INLINE
    const vector_range<const V> project (const V &data, typename vector_range<V>::range_type const &r) {
        // ISSUE was: return vector_range<V> (const_cast<V &> (data), r);
        return vector_range<const V> (data, r);
    }
    template<class V>
    BOOST_UBLAS_INLINE
    vector_range<V> project (vector_range<V> &data, const typename vector_range<V>::range_type &r) {
        return data.project (r);
    }
    template<class V>
    BOOST_UBLAS_INLINE
    const vector_range<V> project (const vector_range<V> &data, const typename vector_range<V>::range_type &r) {
        return data.project (r);
    }

    // Specialization of temporary_traits
    template <class V>
    struct vector_temporary_traits< vector_range<V> >
    : vector_temporary_traits< V > {} ;
    template <class V>
    struct vector_temporary_traits< const vector_range<V> >
    : vector_temporary_traits< V > {} ;

    // Vector based slice class
    template<class V>
    class vector_slice:
        public vector_expression<vector_slice<V> > {

        typedef vector_slice<V> self_type;
    public:
#ifdef BOOST_UBLAS_ENABLE_PROXY_SHORTCUTS
        using vector_expression<self_type>::operator ();
#endif
        typedef const V const_vector_type;
        typedef V vector_type;
        typedef typename V::size_type size_type;
        typedef typename V::difference_type difference_type;
        typedef typename V::value_type value_type;
        typedef typename V::const_reference const_reference;
        typedef typename boost::mpl::if_<boost::is_const<V>,
                                          typename V::const_reference,
                                          typename V::reference>::type reference;
        typedef typename boost::mpl::if_<boost::is_const<V>,
                                          typename V::const_closure_type,
                                          typename V::closure_type>::type vector_closure_type;
        typedef basic_range<size_type, difference_type> range_type;
        typedef basic_slice<size_type, difference_type> slice_type;
        typedef const self_type const_closure_type;
        typedef self_type closure_type;
        typedef typename storage_restrict_traits<typename V::storage_category,
                                                 dense_proxy_tag>::storage_category storage_category;

        // Construction and destruction
        BOOST_UBLAS_INLINE
        vector_slice (vector_type &data, const slice_type &s):
            data_ (data), s_ (s.preprocess (data.size ())) {
            // Early checking of preconditions here.
            // BOOST_UBLAS_CHECK (s_.start () <= data_.size () &&
            //                    s_.start () + s_.stride () * (s_.size () - (s_.size () > 0)) <= data_.size (), bad_index ());
        }
        BOOST_UBLAS_INLINE
        vector_slice (const vector_closure_type &data, const slice_type &s, int):
            data_ (data), s_ (s.preprocess (data.size ())) {
            // Early checking of preconditions here.
            // BOOST_UBLAS_CHECK (s_.start () <= data_.size () &&
            //                    s_.start () + s_.stride () * (s_.size () - (s_.size () > 0)) <= data_.size (), bad_index ());
        }

        // Accessors
        BOOST_UBLAS_INLINE
        size_type start () const {
            return s_.start ();
        }
        BOOST_UBLAS_INLINE
        difference_type stride () const {
            return s_.stride ();
        }
        BOOST_UBLAS_INLINE
        size_type size () const {
            return s_.size ();
        }

        // Storage accessors
        BOOST_UBLAS_INLINE
        const vector_closure_type &data () const {
            return data_;
        }
        BOOST_UBLAS_INLINE
        vector_closure_type &data () {
            return data_;
        }

        // Element access
#ifndef BOOST_UBLAS_PROXY_CONST_MEMBER
        BOOST_UBLAS_INLINE
        const_reference operator () (size_type i) const {
            return data_ (s_ (i));
        }
        BOOST_UBLAS_INLINE
        reference operator () (size_type i) {
            return data_ (s_ (i));
        }

        BOOST_UBLAS_INLINE
        const_reference operator [] (size_type i) const {
            return (*this) (i);
        }
        BOOST_UBLAS_INLINE
        reference operator [] (size_type i) {
            return (*this) (i);
        }
#else
        BOOST_UBLAS_INLINE
        reference operator () (size_type i) const {
            return data_ (s_ (i));
        }

        BOOST_UBLAS_INLINE
        reference operator [] (size_type i) const {
            return (*this) (i);
        }
#endif

        // ISSUE can this be done in free project function?
        // Although a const function can create a non-const proxy to a non-const object
        // Critical is that vector_type and data_ (vector_closure_type) are const correct
        BOOST_UBLAS_INLINE
        vector_slice<vector_type> project (const range_type &r) const {
            return vector_slice<vector_type>  (data_, s_.compose (r.preprocess (data_.size ())), false);
        }
        BOOST_UBLAS_INLINE
        vector_slice<vector_type> project (const slice_type &s) const {
            return vector_slice<vector_type>  (data_, s_.compose (s.preprocess (data_.size ())), false);
        }

        // Assignment
        BOOST_UBLAS_INLINE
        vector_slice &operator = (const vector_slice &vs) {
            // ISSUE need a temporary, proxy can be overlaping alias
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (vs));
            return *this;
        }
        BOOST_UBLAS_INLINE
        vector_slice &assign_temporary (vector_slice &vs) {
            // assign elements, proxied container remains the same
            vector_assign<scalar_assign> (*this, vs);
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_slice &operator = (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (ae));
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_slice &assign (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, ae);
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_slice &operator += (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (*this + ae));
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_slice &plus_assign (const vector_expression<AE> &ae) {
            vector_assign<scalar_plus_assign> (*this, ae);
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_slice &operator -= (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (*this - ae));
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_slice &minus_assign (const vector_expression<AE> &ae) {
            vector_assign<scalar_minus_assign> (*this, ae);
            return *this;
        }
        template<class AT>
        BOOST_UBLAS_INLINE
        vector_slice &operator *= (const AT &at) {
            vector_assign_scalar<scalar_multiplies_assign> (*this, at);
            return *this;
        }
        template<class AT>
        BOOST_UBLAS_INLINE
        vector_slice &operator /= (const AT &at) {
            vector_assign_scalar<scalar_divides_assign> (*this, at);
            return *this;
        }

        // Closure comparison
        BOOST_UBLAS_INLINE
        bool same_closure (const vector_slice &vr) const {
            return (*this).data_.same_closure (vr.data_);
        }

        // Comparison
        BOOST_UBLAS_INLINE
        bool operator == (const vector_slice &vs) const {
            return (*this).data_ == vs.data_ && s_ == vs.s_;
        }

        // Swapping
        BOOST_UBLAS_INLINE
        void swap (vector_slice vs) {
            if (this != &vs) {
                BOOST_UBLAS_CHECK (size () == vs.size (), bad_size ());
                // Sparse ranges may be nonconformant now.
                // std::swap_ranges (begin (), end (), vs.begin ());
                vector_swap<scalar_swap> (*this, vs);
            }
        }
        BOOST_UBLAS_INLINE
        friend void swap (vector_slice vs1, vector_slice vs2) {
            vs1.swap (vs2);
        }

        // Iterator types
    private:
        // Use slice as an index - FIXME this fails for packed assignment
        typedef typename slice_type::const_iterator const_subiterator_type;
        typedef typename slice_type::const_iterator subiterator_type;

    public:
#ifdef BOOST_UBLAS_USE_INDEXED_ITERATOR
        typedef indexed_iterator<vector_slice<vector_type>,
                                 typename vector_type::iterator::iterator_category> iterator;
        typedef indexed_const_iterator<vector_slice<vector_type>,
                                       typename vector_type::const_iterator::iterator_category> const_iterator;
#else
        class const_iterator;
        class iterator;
#endif

        // Element lookup
        BOOST_UBLAS_INLINE
        const_iterator find (size_type i) const {
#ifdef BOOST_UBLAS_USE_INDEXED_ITERATOR
            return const_iterator (*this, i);
#else
            return const_iterator (*this, s_.begin () + i);
#endif
        }
        BOOST_UBLAS_INLINE
        iterator find (size_type i) {
#ifdef BOOST_UBLAS_USE_INDEXED_ITERATOR
            return iterator (*this, i);
#else
            return iterator (*this, s_.begin () + i);
#endif
        }

#ifndef BOOST_UBLAS_USE_INDEXED_ITERATOR
        class const_iterator:
            public container_const_reference<vector_slice>,
            public iterator_base_traits<typename V::const_iterator::iterator_category>::template
                        iterator_base<const_iterator, value_type>::type {
        public:
            typedef typename V::const_iterator::difference_type difference_type;
            typedef typename V::const_iterator::value_type value_type;
            typedef typename V::const_reference reference;    //FIXME due to indexing access
            typedef typename V::const_iterator::pointer pointer;

            // Construction and destruction
            BOOST_UBLAS_INLINE
            const_iterator ():
                container_const_reference<self_type> (), it_ () {}
            BOOST_UBLAS_INLINE
            const_iterator (const self_type &vs, const const_subiterator_type &it):
                container_const_reference<self_type> (vs), it_ (it) {}
            BOOST_UBLAS_INLINE
            const_iterator (const typename self_type::iterator &it):  // ISSUE self_type:: stops VC8 using std::iterator here
                container_const_reference<self_type> (it ()), it_ (it.it_) {}

            // Arithmetic
            BOOST_UBLAS_INLINE
            const_iterator &operator ++ () {
                ++ it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            const_iterator &operator -- () {
                -- it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            const_iterator &operator += (difference_type n) {
                it_ += n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            const_iterator &operator -= (difference_type n) {
                it_ -= n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            difference_type operator - (const const_iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ - it.it_;
            }

            // Dereference
            BOOST_UBLAS_INLINE
            const_reference operator * () const {
                // FIXME replace find with at_element
                BOOST_UBLAS_CHECK (index () < (*this) ().size (), bad_index ());
                return (*this) ().data_ (*it_);
            }
            BOOST_UBLAS_INLINE
            const_reference operator [] (difference_type n) const {
                return *(*this + n);
            }

            // Index
            BOOST_UBLAS_INLINE
            size_type index () const {
                return it_.index ();
            }

            // Assignment
            BOOST_UBLAS_INLINE
            const_iterator &operator = (const const_iterator &it) {
                container_const_reference<self_type>::assign (&it ());
                it_ = it.it_;
                return *this;
            }

            // Comparison
            BOOST_UBLAS_INLINE
            bool operator == (const const_iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ == it.it_;
            }
            BOOST_UBLAS_INLINE
            bool operator < (const const_iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ < it.it_;
            }

        private:
            const_subiterator_type it_;
        };
#endif

        BOOST_UBLAS_INLINE
        const_iterator begin () const {
            return find (0);
        }
        BOOST_UBLAS_INLINE
        const_iterator end () const {
            return find (size ());
        }

#ifndef BOOST_UBLAS_USE_INDEXED_ITERATOR
        class iterator:
            public container_reference<vector_slice>,
            public iterator_base_traits<typename V::iterator::iterator_category>::template
                        iterator_base<iterator, value_type>::type {
        public:
            typedef typename V::iterator::difference_type difference_type;
            typedef typename V::iterator::value_type value_type;
            typedef typename V::reference reference;    //FIXME due to indexing access
            typedef typename V::iterator::pointer pointer;

            // Construction and destruction
            BOOST_UBLAS_INLINE
            iterator ():
                container_reference<self_type> (), it_ () {}
            BOOST_UBLAS_INLINE
            iterator (self_type &vs, const subiterator_type &it):
                container_reference<self_type> (vs), it_ (it) {}

            // Arithmetic
            BOOST_UBLAS_INLINE
            iterator &operator ++ () {
                ++ it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            iterator &operator -- () {
                -- it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            iterator &operator += (difference_type n) {
                it_ += n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            iterator &operator -= (difference_type n) {
                it_ -= n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            difference_type operator - (const iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ - it.it_;
            }

            // Dereference
            BOOST_UBLAS_INLINE
            reference operator * () const {
                // FIXME replace find with at_element
                BOOST_UBLAS_CHECK (index () < (*this) ().size (), bad_index ());
                return (*this) ().data_ (*it_);
            }
            BOOST_UBLAS_INLINE
            reference operator [] (difference_type n) const {
                return *(*this + n);
            }

            // Index
            BOOST_UBLAS_INLINE
            size_type index () const {
                return it_.index ();
            }

            // Assignment
            BOOST_UBLAS_INLINE
            iterator &operator = (const iterator &it) {
                container_reference<self_type>::assign (&it ());
                it_ = it.it_;
                return *this;
            }

            // Comparison
            BOOST_UBLAS_INLINE
            bool operator == (const iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ == it.it_;
            }
            BOOST_UBLAS_INLINE
            bool operator < (const iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ < it.it_;
            }

        private:
            subiterator_type it_;

            friend class const_iterator;
        };
#endif

        BOOST_UBLAS_INLINE
        iterator begin () {
            return find (0);
        }
        BOOST_UBLAS_INLINE
        iterator end () {
            return find (size ());
        }

        // Reverse iterator
        typedef reverse_iterator_base<const_iterator> const_reverse_iterator;
        typedef reverse_iterator_base<iterator> reverse_iterator;

        BOOST_UBLAS_INLINE
        const_reverse_iterator rbegin () const {
            return const_reverse_iterator (end ());
        }
        BOOST_UBLAS_INLINE
        const_reverse_iterator rend () const {
            return const_reverse_iterator (begin ());
        }
        BOOST_UBLAS_INLINE
        reverse_iterator rbegin () {
            return reverse_iterator (end ());
        }
        BOOST_UBLAS_INLINE
        reverse_iterator rend () {
            return reverse_iterator (begin ());
        }

    private:
        vector_closure_type data_;
        slice_type s_;
    };

    // Simple Projections
    template<class V>
    BOOST_UBLAS_INLINE
    vector_slice<V> subslice (V &data, typename V::size_type start, typename V::difference_type stride, typename V::size_type size) {
        typedef basic_slice<typename V::size_type, typename V::difference_type> slice_type;
        return vector_slice<V> (data, slice_type (start, stride, size));
    }
    template<class V>
    BOOST_UBLAS_INLINE
    vector_slice<const V> subslice (const V &data, typename V::size_type start, typename V::difference_type stride, typename V::size_type size)  {
        typedef basic_slice<typename V::size_type, typename V::difference_type> slice_type;
        return vector_slice<const V> (data, slice_type (start, stride, size));
    }

    // Generic Projections
    template<class V>
    BOOST_UBLAS_INLINE
    vector_slice<V> project (V &data, const typename vector_slice<V>::slice_type &s) {
        return vector_slice<V> (data, s);
    }
    template<class V>
    BOOST_UBLAS_INLINE
    const vector_slice<const V> project (const V &data, const typename vector_slice<V>::slice_type &s) {
        // ISSUE was: return vector_slice<V> (const_cast<V &> (data), s);
        return vector_slice<const V> (data, s);
    }
    template<class V>
    BOOST_UBLAS_INLINE
    vector_slice<V> project (vector_slice<V> &data, const typename vector_slice<V>::slice_type &s) {
        return data.project (s);
    }
    template<class V>
    BOOST_UBLAS_INLINE
    const vector_slice<V> project (const vector_slice<V> &data, const typename vector_slice<V>::slice_type &s) {
        return data.project (s);
    }
    // ISSUE in the following two functions it would be logical to use vector_slice<V>::range_type but this confuses VC7.1 and 8.0
    template<class V>
    BOOST_UBLAS_INLINE
    vector_slice<V> project (vector_slice<V> &data, const typename vector_range<V>::range_type &r) {
        return data.project (r);
    }
    template<class V>
    BOOST_UBLAS_INLINE
    const vector_slice<V> project (const vector_slice<V> &data, const typename vector_range<V>::range_type &r) {
        return data.project (r);
    }

    // Specialization of temporary_traits
    template <class V>
    struct vector_temporary_traits< vector_slice<V> >
    : vector_temporary_traits< V > {} ;
    template <class V>
    struct vector_temporary_traits< const vector_slice<V> >
    : vector_temporary_traits< V > {} ;

    // Vector based indirection class
    // Contributed by Toon Knapen.
    // Extended and optimized by Kresimir Fresl.
    template<class V, class IA>
    class vector_indirect:
        public vector_expression<vector_indirect<V, IA> > {

        typedef vector_indirect<V, IA> self_type;
    public:
#ifdef BOOST_UBLAS_ENABLE_PROXY_SHORTCUTS
        using vector_expression<self_type>::operator ();
#endif
        typedef const V const_vector_type;
        typedef V vector_type;
        typedef const IA const_indirect_array_type;
        typedef IA indirect_array_type;
        typedef typename V::size_type size_type;
        typedef typename V::difference_type difference_type;
        typedef typename V::value_type value_type;
        typedef typename V::const_reference const_reference;
        typedef typename boost::mpl::if_<boost::is_const<V>,
                                          typename V::const_reference,
                                          typename V::reference>::type reference;
        typedef typename boost::mpl::if_<boost::is_const<V>,
                                          typename V::const_closure_type,
                                          typename V::closure_type>::type vector_closure_type;
        typedef basic_range<size_type, difference_type> range_type;
        typedef basic_slice<size_type, difference_type> slice_type;
        typedef const self_type const_closure_type;
        typedef self_type closure_type;
        typedef typename storage_restrict_traits<typename V::storage_category,
                                                 dense_proxy_tag>::storage_category storage_category;

        // Construction and destruction
        BOOST_UBLAS_INLINE
        vector_indirect (vector_type &data, size_type size):
            data_ (data), ia_ (size) {}
        BOOST_UBLAS_INLINE
        vector_indirect (vector_type &data, const indirect_array_type &ia):
            data_ (data), ia_ (ia.preprocess (data.size ())) {}
        BOOST_UBLAS_INLINE
        vector_indirect (const vector_closure_type &data, const indirect_array_type &ia, int):
            data_ (data), ia_ (ia.preprocess (data.size ())) {}

        // Accessors
        BOOST_UBLAS_INLINE
        size_type size () const {
            return ia_.size ();
        }
        BOOST_UBLAS_INLINE
        const_indirect_array_type &indirect () const {
            return ia_;
        }
        BOOST_UBLAS_INLINE
        indirect_array_type &indirect () {
            return ia_;
        }

        // Storage accessors
        BOOST_UBLAS_INLINE
        const vector_closure_type &data () const {
            return data_;
        }
        BOOST_UBLAS_INLINE
        vector_closure_type &data () {
            return data_;
        }

        // Element access
#ifndef BOOST_UBLAS_PROXY_CONST_MEMBER
        BOOST_UBLAS_INLINE
        const_reference operator () (size_type i) const {
            return data_ (ia_ (i));
        }
        BOOST_UBLAS_INLINE
        reference operator () (size_type i) {
            return data_ (ia_ (i));
        }

        BOOST_UBLAS_INLINE
        const_reference operator [] (size_type i) const {
            return (*this) (i);
        }
        BOOST_UBLAS_INLINE
        reference operator [] (size_type i) {
            return (*this) (i);
        }
#else
        BOOST_UBLAS_INLINE
        reference operator () (size_type i) const {
            return data_ (ia_ (i));
        }

        BOOST_UBLAS_INLINE
        reference operator [] (size_type i) const {
            return (*this) (i);
        }
#endif

        // ISSUE can this be done in free project function?
        // Although a const function can create a non-const proxy to a non-const object
        // Critical is that vector_type and data_ (vector_closure_type) are const correct
        BOOST_UBLAS_INLINE
        vector_indirect<vector_type, indirect_array_type> project (const range_type &r) const {
            return vector_indirect<vector_type, indirect_array_type> (data_, ia_.compose (r.preprocess (data_.size ())), 0);
        }
        BOOST_UBLAS_INLINE
        vector_indirect<vector_type, indirect_array_type> project (const slice_type &s) const {
            return vector_indirect<vector_type, indirect_array_type> (data_, ia_.compose (s.preprocess (data_.size ())), 0);
        }
        BOOST_UBLAS_INLINE
        vector_indirect<vector_type, indirect_array_type> project (const indirect_array_type &ia) const {
            return vector_indirect<vector_type, indirect_array_type> (data_, ia_.compose (ia.preprocess (data_.size ())), 0);
        }

        // Assignment
        BOOST_UBLAS_INLINE
        vector_indirect &operator = (const vector_indirect &vi) {
            // ISSUE need a temporary, proxy can be overlaping alias
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (vi));
            return *this;
        }
        BOOST_UBLAS_INLINE
        vector_indirect &assign_temporary (vector_indirect &vi) {
            // assign elements, proxied container remains the same
            vector_assign<scalar_assign> (*this, vi);
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_indirect &operator = (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (ae));
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_indirect &assign (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, ae);
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_indirect &operator += (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (*this + ae));
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_indirect &plus_assign (const vector_expression<AE> &ae) {
            vector_assign<scalar_plus_assign> (*this, ae);
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_indirect &operator -= (const vector_expression<AE> &ae) {
            vector_assign<scalar_assign> (*this, typename vector_temporary_traits<V>::type (*this - ae));
            return *this;
        }
        template<class AE>
        BOOST_UBLAS_INLINE
        vector_indirect &minus_assign (const vector_expression<AE> &ae) {
            vector_assign<scalar_minus_assign> (*this, ae);
            return *this;
        }
        template<class AT>
        BOOST_UBLAS_INLINE
        vector_indirect &operator *= (const AT &at) {
            vector_assign_scalar<scalar_multiplies_assign> (*this, at);
            return *this;
        }
        template<class AT>
        BOOST_UBLAS_INLINE
        vector_indirect &operator /= (const AT &at) {
            vector_assign_scalar<scalar_divides_assign> (*this, at);
            return *this;
        }

        // Closure comparison
        BOOST_UBLAS_INLINE
        bool same_closure (const vector_indirect &vr) const {
return true;
        }

        // Comparison
        BOOST_UBLAS_INLINE
        bool operator == (const vector_indirect &vi) const {
            return (*this).data_ == vi.data_ && ia_ == vi.ia_;
        }

        // Swapping
        BOOST_UBLAS_INLINE
        void swap (vector_indirect vi) {
            if (this != &vi) {
                BOOST_UBLAS_CHECK (size () == vi.size (), bad_size ());
                // Sparse ranges may be nonconformant now.
                // std::swap_ranges (begin (), end (), vi.begin ());
                vector_swap<scalar_swap> (*this, vi);
            }
        }
        BOOST_UBLAS_INLINE
        friend void swap (vector_indirect vi1, vector_indirect vi2) {
            vi1.swap (vi2);
        }

        // Iterator types
    private:
        // Use indirect array as an index - FIXME this fails for packed assignment
        typedef typename IA::const_iterator const_subiterator_type;
        typedef typename IA::const_iterator subiterator_type;

    public:
#ifdef BOOST_UBLAS_USE_INDEXED_ITERATOR
        typedef indexed_iterator<vector_indirect<vector_type, indirect_array_type>,
                                 typename vector_type::iterator::iterator_category> iterator;
        typedef indexed_const_iterator<vector_indirect<vector_type, indirect_array_type>,
                                       typename vector_type::const_iterator::iterator_category> const_iterator;
#else
        class const_iterator;
        class iterator;
#endif
        // Element lookup
        BOOST_UBLAS_INLINE
        const_iterator find (size_type i) const {
#ifdef BOOST_UBLAS_USE_INDEXED_ITERATOR
            return const_iterator (*this, i);
#else
            return const_iterator (*this, ia_.begin () + i);
#endif
        }
        BOOST_UBLAS_INLINE
        iterator find (size_type i) {
#ifdef BOOST_UBLAS_USE_INDEXED_ITERATOR
            return iterator (*this, i);
#else
            return iterator (*this, ia_.begin () + i);
#endif
        }

        // Iterators simply are indices.

#ifndef BOOST_UBLAS_USE_INDEXED_ITERATOR
        class const_iterator:
            public container_const_reference<vector_indirect>,
            public iterator_base_traits<typename V::const_iterator::iterator_category>::template
                        iterator_base<const_iterator, value_type>::type {
        public:
            typedef typename V::const_iterator::difference_type difference_type;
            typedef typename V::const_iterator::value_type value_type;
            typedef typename V::const_reference reference;    //FIXME due to indexing access
            typedef typename V::const_iterator::pointer pointer;

            // Construction and destruction
            BOOST_UBLAS_INLINE
            const_iterator ():
                container_const_reference<self_type> (), it_ () {}
            BOOST_UBLAS_INLINE
            const_iterator (const self_type &vi, const const_subiterator_type &it):
                container_const_reference<self_type> (vi), it_ (it) {}
            BOOST_UBLAS_INLINE
            const_iterator (const typename self_type::iterator &it):  // ISSUE self_type:: stops VC8 using std::iterator here
                container_const_reference<self_type> (it ()), it_ (it.it_) {}

            // Arithmetic
            BOOST_UBLAS_INLINE
            const_iterator &operator ++ () {
                ++ it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            const_iterator &operator -- () {
                -- it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            const_iterator &operator += (difference_type n) {
                it_ += n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            const_iterator &operator -= (difference_type n) {
                it_ -= n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            difference_type operator - (const const_iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ - it.it_;
            }

            // Dereference
            BOOST_UBLAS_INLINE
            const_reference operator * () const {
                // FIXME replace find with at_element
                BOOST_UBLAS_CHECK (index () < (*this) ().size (), bad_index ());
                return (*this) ().data_ (*it_);
            }
            BOOST_UBLAS_INLINE
            const_reference operator [] (difference_type n) const {
                return *(*this + n);
            }

            // Index
            BOOST_UBLAS_INLINE
            size_type index () const {
                return it_.index ();
            }

            // Assignment
            BOOST_UBLAS_INLINE
            const_iterator &operator = (const const_iterator &it) {
                container_const_reference<self_type>::assign (&it ());
                it_ = it.it_;
                return *this;
            }

            // Comparison
            BOOST_UBLAS_INLINE
            bool operator == (const const_iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ == it.it_;
            }
            BOOST_UBLAS_INLINE
            bool operator < (const const_iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ < it.it_;
            }

        private:
            const_subiterator_type it_;
        };
#endif

        BOOST_UBLAS_INLINE
        const_iterator begin () const {
            return find (0);
        }
        BOOST_UBLAS_INLINE
        const_iterator end () const {
            return find (size ());
        }

#ifndef BOOST_UBLAS_USE_INDEXED_ITERATOR
        class iterator:
            public container_reference<vector_indirect>,
            public iterator_base_traits<typename V::iterator::iterator_category>::template
                        iterator_base<iterator, value_type>::type {
        public:
            typedef typename V::iterator::difference_type difference_type;
            typedef typename V::iterator::value_type value_type;
            typedef typename V::reference reference;    //FIXME due to indexing access
            typedef typename V::iterator::pointer pointer;

            // Construction and destruction
            BOOST_UBLAS_INLINE
            iterator ():
                container_reference<self_type> (), it_ () {}
            BOOST_UBLAS_INLINE
            iterator (self_type &vi, const subiterator_type &it):
                container_reference<self_type> (vi), it_ (it) {}

            // Arithmetic
            BOOST_UBLAS_INLINE
            iterator &operator ++ () {
                ++ it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            iterator &operator -- () {
                -- it_;
                return *this;
            }
            BOOST_UBLAS_INLINE
            iterator &operator += (difference_type n) {
                it_ += n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            iterator &operator -= (difference_type n) {
                it_ -= n;
                return *this;
            }
            BOOST_UBLAS_INLINE
            difference_type operator - (const iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ - it.it_;
            }

            // Dereference
            BOOST_UBLAS_INLINE
            reference operator * () const {
                // FIXME replace find with at_element
                BOOST_UBLAS_CHECK (index () < (*this) ().size (), bad_index ());
                return (*this) ().data_ (*it_);
            }
            BOOST_UBLAS_INLINE
            reference operator [] (difference_type n) const {
                return *(*this + n);
            }

            // Index
            BOOST_UBLAS_INLINE
            size_type index () const {
                return it_.index ();
            }

            // Assignment
            BOOST_UBLAS_INLINE
            iterator &operator = (const iterator &it) {
                container_reference<self_type>::assign (&it ());
                it_ = it.it_;
                return *this;
            }

            // Comparison
            BOOST_UBLAS_INLINE
            bool operator == (const iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ == it.it_;
            }
            BOOST_UBLAS_INLINE
            bool operator < (const iterator &it) const {
                BOOST_UBLAS_CHECK ((*this) ().same_closure (it ()), external_logic ());
                return it_ < it.it_;
            }

        private:
            subiterator_type it_;

            friend class const_iterator;
        };
#endif

        BOOST_UBLAS_INLINE
        iterator begin () {
            return find (0);
        }
        BOOST_UBLAS_INLINE
        iterator end () {
            return find (size ());
        }

        // Reverse iterator
        typedef reverse_iterator_base<const_iterator> const_reverse_iterator;
        typedef reverse_iterator_base<iterator> reverse_iterator;

        BOOST_UBLAS_INLINE
        const_reverse_iterator rbegin () const {
            return const_reverse_iterator (end ());
        }
        BOOST_UBLAS_INLINE
        const_reverse_iterator rend () const {
            return const_reverse_iterator (begin ());
        }
        BOOST_UBLAS_INLINE
        reverse_iterator rbegin () {
            return reverse_iterator (end ());
        }
        BOOST_UBLAS_INLINE
        reverse_iterator rend () {
            return reverse_iterator (begin ());
        }

    private:
        vector_closure_type data_;
        indirect_array_type ia_;
    };

    // Projections
    template<class V, class A>
    BOOST_UBLAS_INLINE
    vector_indirect<V, indirect_array<A> > project (V &data, const indirect_array<A> &ia) {
        return vector_indirect<V, indirect_array<A> > (data, ia);
    }
    template<class V, class A>
    BOOST_UBLAS_INLINE
    const vector_indirect<const V, indirect_array<A> > project (const V &data, const indirect_array<A> &ia) {
        // ISSUE was: return vector_indirect<V, indirect_array<A> > (const_cast<V &> (data), ia)
        return vector_indirect<const V, indirect_array<A> > (data, ia);
    }
    template<class V, class IA>
    BOOST_UBLAS_INLINE
    vector_indirect<V, IA> project (vector_indirect<V, IA> &data, const typename vector_indirect<V, IA>::range_type &r) {
        return data.project (r);
    }
    template<class V, class IA>
    BOOST_UBLAS_INLINE
    const vector_indirect<V, IA> project (const vector_indirect<V, IA> &data, const typename vector_indirect<V, IA>::range_type &r) {
        return data.project (r);
    }
    template<class V, class IA>
    BOOST_UBLAS_INLINE
    vector_indirect<V, IA> project (vector_indirect<V, IA> &data, const typename vector_indirect<V, IA>::slice_type &s) {
        return data.project (s);
    }
    template<class V, class IA>
    BOOST_UBLAS_INLINE
    const vector_indirect<V, IA> project (const vector_indirect<V, IA> &data, const typename vector_indirect<V, IA>::slice_type &s) {
        return data.project (s);
    }
    template<class V, class A>
    BOOST_UBLAS_INLINE
    vector_indirect<V, indirect_array<A> > project (vector_indirect<V, indirect_array<A> > &data, const indirect_array<A> &ia) {
        return data.project (ia);
    }
    template<class V, class A>
    BOOST_UBLAS_INLINE
    const vector_indirect<V, indirect_array<A> > project (const vector_indirect<V, indirect_array<A> > &data, const indirect_array<A> &ia) {
        return data.project (ia);
    }

    // Specialization of temporary_traits
    template <class V>
    struct vector_temporary_traits< vector_indirect<V> >
    : vector_temporary_traits< V > {} ;
    template <class V>
    struct vector_temporary_traits< const vector_indirect<V> >
    : vector_temporary_traits< V > {} ;

}}}

#endif


#define  __declspec(dllexport)

#include <boost/shared_ptr.hpp>

#include <iostream>
#include <fstream>
#include <string>

#include "geodesic_mesh.h"
#include "geodesic_algorithm_dijkstra_alternative.h"
#include "geodesic_algorithm_dijkstra.h"
#include "geodesic_algorithm_subdivision.h"
#include "geodesic_algorithm_exact.h"
#include "geodesic_matlab_api.h"

typedef boost::shared_ptr<geodesic::Mesh> mesh_shared_pointer;
std::vector<mesh_shared_pointer> meshes;

typedef boost::shared_ptr<geodesic::GeodesicAlgorithmBase> algorithm_shared_pointer;
std::vector<algorithm_shared_pointer> algorithms;

std::vector<geodesic::SurfacePoint> output_path;
geodesic::OutputBuffer output_buffer, output_buffer1;

std::size_t find_mesh_id(geodesic::Mesh* mesh)
{
	for(std::size_t i=0; i<meshes.size(); ++i)
	{
		if(meshes[i].get() == mesh)
		{
			return i;
		}
	}
}

 long distance_and_source(long algorithm_id,		//quickly find what source this point belongs to and what is the distance to this source
											 double* destination,
											 double* best_source_distance)
{
	geodesic::SurfacePoint point;
	geodesic::GeodesicAlgorithmBase* algorithm = algorithms[algorithm_id].get();
	std::size_t mesh_id = find_mesh_id(algorithm->mesh());
	geodesic::fill_surface_point_structure(&point,
										   destination,
										   algorithm->mesh());

	std::size_t best_source = algorithm->best_source(point,
													 *best_source_distance);
	return best_source;
}

 long distance_and_source_for_all_vertices(long algorithm_id,	//same idea as in the previous function
															  double** distances,	//list distance/source info for all vertices of the mesh
															  long** sources)
{

	geodesic::GeodesicAlgorithmBase* algorithm = algorithms[algorithm_id].get();
	geodesic::Mesh* mesh = algorithm->mesh();

	output_buffer.allocate<double>(mesh->vertices().size());	//allocate space for distances
	*distances = output_buffer.get<double>();

	output_buffer1.allocate<long>(mesh->vertices().size());		//allocate space for sources
	*sources = output_buffer1.get<long>();

	for(std::size_t i = 0; i < mesh->vertices().size(); ++i)
	{
		geodesic::SurfacePoint point(&mesh->vertices()[i]);
		double* distace_location = *distances + i;
		(*sources)[i] = algorithm->best_source(point, *distace_location);
	}

	return mesh->vertices().size();
}

 long trace_back(long algorithm_id,
									double* destination,
									double** path)
{
	geodesic::SurfacePoint point;
	geodesic::GeodesicAlgorithmBase* algorithm = algorithms[algorithm_id].get();
	geodesic::fill_surface_point_structure(&point,
										   destination,
										   algorithm->mesh());

	algorithm->trace_back(point,
						  output_path);

	std::size_t mesh_id = find_mesh_id(algorithm->mesh());
	output_buffer.allocate<double>(output_path.size()*5);
	for(std::size_t i=0; i<output_path.size(); ++i)
	{
		geodesic::fill_surface_point_double(&output_path[i],
											output_buffer.get<double>() + 5*i,
											mesh_id);
	}

	*path = output_buffer.get<double>();

	return output_path.size();
}

 void propagate(long algorithm_id,
									double* source_points,
									long num_sources,
									double* stop_points,
									long num_stop_points,
									double max_propagation_distance)
{
	std::vector<geodesic::SurfacePoint> sources(num_sources);

	geodesic::Mesh* mesh = algorithms[algorithm_id]->mesh();
	for(std::size_t i=0; i<num_sources; ++i)
	{
		geodesic::fill_surface_point_structure(&sources[i],
												source_points + 5*i,
												mesh);
	}

	std::vector<geodesic::SurfacePoint> stop(num_stop_points);
	for(std::size_t i=0; i<num_stop_points; ++i)
	{
		geodesic::fill_surface_point_structure(&stop[i],
												stop_points + 5*i,
												mesh);
	}

	algorithms[algorithm_id]->propagate(sources,
										max_propagation_distance,
										&stop);
}

 long new_mesh(long num_points,
								  double* points,
								  long num_triangles,
								  long* triangles,
								  long* num_edges,
								  double** edges)
{
	mesh_shared_pointer new_mesh = mesh_shared_pointer(new geodesic::Mesh);
	meshes.push_back(new_mesh);

	new_mesh->initialize_mesh_data(num_points,
								   points,
								   num_triangles,
								   triangles);

	*num_edges = new_mesh->edges().size();

	output_buffer.allocate<double>(*num_edges * 4);
	*edges = output_buffer.get<double>();

	for(std::size_t i=0; i<*num_edges; ++i)
	{
		geodesic::Edge& edge = new_mesh->edges()[i];
		double* buffer = *edges + 4*i;

		buffer[0] = edge.adjacent_vertices()[0]->id();
		buffer[1] = edge.adjacent_vertices()[1]->id();
		buffer[2] = edge.adjacent_faces()[0]->id();
		buffer[3] = edge.adjacent_faces().size() == 2 ?
					edge.adjacent_faces()[1]->id() :
					-1.0;
	}

	return meshes.size() - 1;
}

 long new_algorithm(long mesh_id,
						               long type,
									   long subdivision)
{
	if(subdivision == 0 && type == 1)//SUBDIVISION)
	{
		type = 2;		//DIJKSTRA;
	}

	geodesic::Mesh* mesh = meshes[mesh_id].get();
	geodesic::GeodesicAlgorithmBase* algorithm;
	switch(type)
	{
		case 2: //DIJKSTRA
		{
			algorithm = new geodesic::GeodesicAlgorithmDijkstra(mesh);
			break;
		}
		case 1: //SUBDIVISION:
		{
			algorithm = new geodesic::GeodesicAlgorithmSubdivision(mesh, subdivision);
			break;
		}
		case 0://EXACT:
		default:
		{
			algorithm = new geodesic::GeodesicAlgorithmExact(mesh);
			break;
		}
	}

	algorithms.push_back(algorithm_shared_pointer(algorithm));

	return algorithms.size() - 1;
}

 void delete_mesh(long id)		//delete mesh and all related algorithms
{
	assert(id < meshes.size());

	geodesic::Mesh* mesh = meshes[id].get();
	for(std::size_t i=0; i<algorithms.size(); ++i)
	{
		geodesic::GeodesicAlgorithmBase* algorithm = algorithms[i].get();
		if(algorithm && algorithm->mesh() == mesh)
		{
			algorithms[i] = algorithm_shared_pointer();
		}
	}

	meshes[id] = mesh_shared_pointer();
}

 void delete_algorithm(long id)
{
	if(id < algorithms.size())
	{
		algorithms[id] = algorithm_shared_pointer();
	}
}


/*
 * Copyright (c) 2009 Carnegie Mellon University.
 *     All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an "AS
 *  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 *  express or implied.  See the License for the specific language
 *  governing permissions and limitations under the License.
 *
 *
 */

#include <iostream>
#include <iomanip>
#include <fstream>
#include <algorithm>

#include <stdint.h>
#include <vector>
#include <map>

#include <boost/program_options.hpp>
#include <boost/math/special_functions/gamma.hpp>

#include <graphlab.hpp>

#include <graphlab/macros_def.hpp>

typedef uint32_t word_id_type;
typedef uint32_t doc_id_type;
typedef uint16_t topic_id_type;
typedef uint32_t count_type;
#define NULL_TOPIC topic_id_type(-1)

struct token_type {
  word_id_type word;
  doc_id_type doc;
  token_type(const word_id_type& word = 0, const doc_id_type& doc = 0) :
    word(word), doc(doc) { }
};
std::ostream& operator<<(std::ostream& out, const token_type& tok) {
  return out << "(" << tok.word << ", " << tok.doc << ")";
}

struct corpus_type {
  size_t nwords, ndocs, ntokens;
  std::vector< token_type > tokens;
  std::vector<std::string> dictionary;
  std::vector< word_id_type > ntokens_in_doc;

  corpus_type(const std::string& dictionary_fname,
              const std::string& counts_fname ) :
    nwords(0), ndocs(0), ntokens(0) {
    dictionary.reserve(20000);
    ntokens_in_doc.reserve(5000);
    tokens.reserve(100000);
    load_dictionary(dictionary_fname);
    load_counts(counts_fname);
  }
  void load_dictionary(const std::string& fname) {
    std::ifstream fin(fname.c_str());
    std::string str;
    while(fin.good()) {
      std::getline(fin, str);
      if(fin.good()) { dictionary.push_back(str); nwords++; }
    }
    fin.close();
  }

  void load_counts(const std::string& fname)  {
    std::ifstream fin(fname.c_str());
    while(fin.good()) {
      // Read a collection of tokens
      const size_t NULL_VALUE(-1);
      size_t word = NULL_VALUE, doc = NULL_VALUE, count = NULL_VALUE;
      fin >> doc >> word >> count;
      if(fin.good()) {
        assert(word != NULL_VALUE && doc != NULL_VALUE && count != NULL_VALUE);
        // update the doc counter
        ndocs = std::max(ndocs, doc + 1);
        // Assert valid word
        assert(word < nwords);
        // Update the words in document counter
        if(doc >= ntokens_in_doc.size())
          ntokens_in_doc.resize(doc+1, 0);
        ntokens_in_doc[doc] += count;
        // Add all the tokens
        token_type tok; tok.word = word; tok.doc = doc;
        for(size_t i = 0; i < count; ++i) tokens.push_back(tok);
      }
    }
    fin.close();
    ntokens = tokens.size();
  } // end of load counts

  void shuffle_tokens() { graphlab::random::shuffle(tokens); }
}; // end of corpus

template<typename T>
class matrix {
private:
  size_t _rows, _cols;
  std::vector<T> data;

  const size_t linear_index(const size_t& i, const size_t& j) const {
    assert(i < _rows && j < _cols);
    return i + j * _rows;
  }

public:
  matrix(const size_t& rows, const size_t& cols, const T& zero = T(0)) :
    _rows(rows), _cols(cols), data(rows*cols, zero) { };
  const T& operator()(const size_t& i, const size_t& j) const {
    return data[linear_index(i,j)];
  }
  size_t rows() const { return _rows; }
  size_t cols() const { return _cols; }
  T& operator()(const size_t& i, const size_t& j) {
    return data[linear_index(i,j)];
  }
  const T& operator()(const size_t& i) const {
    assert(i < data.size());
    return data[i];
  }
  T& operator()(const size_t& i) {
    assert(i < data.size());
    return data[i];
  }
  void zeros() {
    std::fill(data.begin(), data.end(), T(0));
  }
  void operator+=(const matrix& other) {
    assert(_rows == other._rows);
    assert(_cols == other._cols);
    for(size_t i = 0; i < data.size(); ++i) data[i] += other.data[i];
  }
  T sum() const {
    T z(0);
    for(size_t i = 0; i < data.size(); ++i) z += data[i];
    return z;
  }
}; // end of matrix
typedef matrix<count_type> mat_type;

class collapsed_gibbs {
public:
  const corpus_type* corpus_ptr;
  const size_t ntopics;
  const double alpha, beta;

  std::vector< topic_id_type > topics;
  //! n_td(t,d) Number of occurences of topic t in document d
  mat_type n_td;
  //! n_wt(w,t) Number of occurences of word w in topic t
  mat_type n_wt;
  //! n_t(t) The total number of words assigned to topic t
  mat_type n_t;
  //! number of times a token was assigned to a new topic
  size_t nchanges;

  collapsed_gibbs(const corpus_type& corpus,
                  const size_t& ntopics,
                  const double& alpha,
                  const double& beta) :
    corpus_ptr(&corpus), ntopics(ntopics), alpha(alpha),
    beta(beta), topics(corpus.ntokens, NULL_TOPIC),
    n_td(ntopics, corpus.ndocs, 0),
    n_wt(corpus.nwords, ntopics, 0),
    n_t(ntopics, 1, 0),
    nchanges(0) { }

  void iterate() {
    assert(corpus_ptr != NULL);
    const corpus_type& corpus = *corpus_ptr;
    // Reset the number of changes
    nchanges = 0;
    std::vector<double> conditional(ntopics);

    // Loop over all the tokens
    for(size_t i = 0; i < corpus.ntokens; ++i) {
      // Get the word and document for the ith token
      const word_id_type w = corpus.tokens[i].word;
      const doc_id_type d = corpus.tokens[i].doc;
      const topic_id_type old_topic = topics[i];

      // Remove the word from the current counters
      if(old_topic != NULL_TOPIC) {
        --n_td(old_topic, d); --n_wt(w, old_topic), --n_t(old_topic);
      }

      // Construct the conditional
      double normalizer = 0;
      for(size_t t = 0; t < ntopics; ++t) {
        conditional[t] = (alpha + n_td(t,d)) * (beta + n_wt(w,t)) /
          (beta * corpus.nwords + n_t(t));
        normalizer += conditional[t];
      }
      assert(normalizer > 0);

      // Draw a new value
      topic_id_type new_topic = 0;
      // normalize and then sample
      for(size_t t = 0; t < ntopics; ++t) conditional[t] /= normalizer;
      new_topic = graphlab::random::multinomial(conditional);

      // Update the topic assignment and counters
      topics[i] = new_topic;
      if(new_topic != old_topic) nchanges++;
      ++n_td(new_topic, d); ++n_wt(w, new_topic), ++n_t(new_topic);
    } // end of loop over tokens

    const size_t n_td_sum = n_td.sum();
    const size_t n_wt_sum = n_wt.sum();
    const size_t n_t_sum = n_t.sum();
    assert(n_td_sum == corpus.ntokens);
    assert(n_wt_sum == corpus.ntokens);
    assert(n_t_sum == corpus.ntokens);
  }
};

double log_likelihood(const double& alpha, const double& beta,
                      const mat_type& n_td, const mat_type& n_wt) {
  using boost::math::lgamma;
  const size_t ndocs  = n_td.cols();
  const size_t ntopics = n_td.rows();
  const size_t nwords = n_wt.rows();

  mat_type n_t(ntopics,1, 0);
  for(size_t t = 0; t < n_wt.cols(); ++t)
    for(size_t w = 0; w < n_wt.rows(); ++w) n_t(t) += n_wt(w,t);

  // Matlab Functions:
  //
  //  llik_w_given_z = ...
  //    ntopics * (gammaln(nwords * beta) - nwords * gammaln(beta)) + ...
  //    sum((sum(gammaln(n_wt + beta)) - gammaln( sum(n_wt) + nwords*beta)));
  //
  //  llik_z = ...
  //    ndocs * (gammaln(ntopics * alpha) - ntopics * gammaln(alpha)) + ...
  //    sum(sum(gammaln(n_td + alpha)) - gammaln(sum(n_td) + ntopics * alpha));

  double llik_words_given_topics =
    ntopics * (lgamma(nwords * beta) - nwords * lgamma(beta));
  for(size_t t = 0; t < ntopics; ++t) {
    for(size_t w = 0; w < nwords; ++w) {
      llik_words_given_topics += lgamma(n_wt(w,t) + beta);
    }
    llik_words_given_topics -= lgamma(n_t(t) + nwords * beta);
  }
  double llik_topics = ndocs * (lgamma(ntopics * alpha) - ntopics * lgamma(alpha));
  for(size_t d = 0; d < ndocs; ++d) {
    size_t ntokens_in_doc = 0;
    for(size_t t = 0; t < ntopics; ++t) {
      llik_topics += lgamma(n_td(t,d) + alpha);
      ntokens_in_doc += n_td(t,d);
    }
    llik_topics -= lgamma(ntokens_in_doc + ntopics * alpha);
  }
  return llik_words_given_topics + llik_topics;
} // end of log_likelihood

void display_top(const corpus_type& corpus,
                 const mat_type& n_wt,
                 const size_t& ntop) {
  assert(ntop > 0);
  const size_t nwords = n_wt.rows();
  const size_t ntopics = n_wt.cols();
  typedef std::pair<size_t, word_id_type> cw_pair_type;
  for(size_t t = 0; t < ntopics; ++t) {
    std::set< cw_pair_type > top_words;
    for(size_t w = 0; w < nwords; ++w) {
      if(top_words.size() < ntop || n_wt(w,t) > top_words.begin()->first) {
        top_words.insert(std::make_pair(n_wt(w,t), w));
        if(top_words.size() > ntop) top_words.erase(top_words.begin());
      }
    }
    std::cout << std::endl;
    rev_foreach(const cw_pair_type& pair, top_words) {
      std::cout << corpus.dictionary.at(pair.second) << ", ";
    }
    std::cout << std::endl;
  }
} // end of display top

int main(int argc, char** argv) {

  std::string dictionary_fname("dictionary.txt");
  std::string counts_fname("counts.tsv");
  size_t ntopics(50);
  size_t nburnin(50);
  size_t nsamples(10);
  double alpha(50.0/double(ntopics));
  double beta(0.1);
  size_t topk(20);
  std::string llik_fname("llik.txt");
  std::string doctop_fname("doctop.txt");
  std::string wordtop_fname("wordtop.txt");

  // Parse command line options
  namespace po = boost::program_options;
  po::options_description desc("LDA sampler code");
  desc.add_options()
    ("help", "produce help message")
    ("dictionary", po::value<std::string>(&dictionary_fname)->
     default_value(dictionary_fname), "Dictionary file")
    ("counts", po::value<std::string>(&counts_fname)->
     default_value(counts_fname), "Counts file")
    ("ntopics", po::value<size_t>(&ntopics)->
     default_value(ntopics), "Number of topics")
    ("nburnin", po::value<size_t>(&nburnin)->
     default_value(nburnin), "Number of iterations")
    ("nsamples", po::value<size_t>(&nsamples)->
     default_value(nsamples), "Number of iterations")
    ("alpha", po::value<double>(&alpha)->
     default_value(alpha), "Alpha prior")
    ("beta", po::value<double>(&beta)->
     default_value(beta), "Beta prior")
    ("doctop_fname", po::value<std::string>(&doctop_fname)->
     default_value(doctop_fname), "doctop_fname")
    ("wordtop_fname", po::value<std::string>(&wordtop_fname)->
     default_value(wordtop_fname), "wordtop_fname")
    ("topk", po::value<size_t>(&topk)->
     default_value(topk), "number of top k to show");

  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, desc), vm);
  po::notify(vm);
  if (vm.count("help")) {
    std::cout << desc << "\n";
    return EXIT_FAILURE;
  }

  if (dictionary_fname.length() == 0 || counts_fname.length() == 0) {
    std::cout << "Both counts and dictionary must be specified" << std::endl;
    std::cout << desc << "\n";
    return EXIT_FAILURE;
  }

  std::cout << "Loading the corpus." << std::endl;
  corpus_type corpus(dictionary_fname, counts_fname);

  std::cout << "Number of words:   " << corpus.nwords << std::endl
            << "Number of docs:    " << corpus.ndocs << std::endl
            << "Number of tokens:  " << corpus.ntokens << std::endl
            << "Ntopics:           " << ntopics << std::endl
            << "Alpha:             " << alpha   << std::endl
            << "Beta:              " << beta    << std::endl;

  std::cout << "Seeding Generator: " << std::endl;
  graphlab::random::nondet_seed();
  std::cout << "Shuffling corpus: " << std::endl;
  corpus.shuffle_tokens();

  std::cout << "Constructing Gibbs Sampler: " << std::endl;
  collapsed_gibbs gibbs(corpus, ntopics, alpha, beta);

  std::ofstream llik_fout(llik_fname.c_str());
  llik_fout.precision(16);

  std::cout << "Starting Burnin" << std::endl;
  for(size_t i = 0; i < nburnin; ++i) {
    std::cout << "Burnin iteration: " << i << std::endl;
    gibbs.iterate();
    std::cout << "Computing top " << topk << " of each topic" << std::endl;
    display_top(corpus, gibbs.n_wt, topk);
    std::cout << "Number of changes: " << gibbs.nchanges << std::endl
              << "Prop. Changes:     "
              << double(gibbs.nchanges)/ corpus.ntokens << std::endl;
    double llik = log_likelihood(gibbs.alpha, gibbs.beta, gibbs.n_td, gibbs.n_wt);
    std::cout << "Log-likelihood:    " // std::setprecision(8) <<
              <<  llik << std::endl;
    llik_fout << llik << '\t' << gibbs.nchanges << std::endl;

  }
  std::cout << "Finished burnin.  Preparing final sample set." << std::endl;

  mat_type n_td(ntopics, corpus.ndocs, 0);
  mat_type n_wt(corpus.nwords, ntopics, 0);
  mat_type n_t(ntopics, 1, 0);

  for(size_t i = 0; i < nsamples; ++i) {
    std::cout << "Sampling iteration: " << i << std::endl;
    gibbs.iterate();
    std::cout << "Number of changes: " << gibbs.nchanges << std::endl
              << "Prop. Changes:     "
              << double(gibbs.nchanges)/ corpus.ntokens << std::endl;
    std::cout << "Accumulating sample" << std::endl;
    n_td += gibbs.n_td;
    n_wt += gibbs.n_wt;
    n_t  += gibbs.n_t;

    std::cout << "Computing top " << topk << " of each topic" << std::endl;
    display_top(corpus, n_wt, topk);
    std::cout << "Number of changes: " << gibbs.nchanges << std::endl
              << "Prop. Changes:     "
              << double(gibbs.nchanges)/ corpus.ntokens << std::endl;
    double llik = log_likelihood(gibbs.alpha, gibbs.beta, gibbs.n_td, gibbs.n_wt);
    std::cout << "Log-likelihood:    " // std::setprecision(8) <<
              <<  llik << std::endl;
    llik_fout << llik << '\t' << gibbs.nchanges << std::endl;

  }
  llik_fout.close();

  std::cout << "Saving doctop: " << doctop_fname << std::endl;
  std::ofstream doctop_fout(doctop_fname.c_str());
  for(size_t d = 0; d < corpus.ndocs; ++d) {
    double normalizer = ntopics * alpha;
    for(size_t t = 0; t < ntopics; ++t)
      normalizer += double(n_td(t,d)) / double(nsamples);
    for(size_t t = 0; t < ntopics; ++t) {
      const double value =
        (double(n_td(t,d))/double(nsamples) + alpha) / normalizer;
      doctop_fout << value << ((t+1 < ntopics)? '\t' : '\n');
    }
  }
  doctop_fout.close();

  std::cout << "Saving wordtop: " << wordtop_fname << std::endl;

  return EXIT_SUCCESS;
}


// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_UNITS_CODATA_HELION_CONSTANTS_HPP
#define BOOST_UNITS_CODATA_HELION_CONSTANTS_HPP

#include <boost/units/quantity.hpp>
#include <boost/units/static_constant.hpp>

#include <boost/units/systems/detail/constants.hpp>
#include <boost/units/systems/si/amount.hpp>
#include <boost/units/systems/si/area.hpp>
#include <boost/units/systems/si/electric_charge.hpp>
#include <boost/units/systems/si/energy.hpp>
#include <boost/units/systems/si/frequency.hpp>
#include <boost/units/systems/si/length.hpp>
#include <boost/units/systems/si/mass.hpp>
#include <boost/units/systems/si/magnetic_flux_density.hpp>
#include <boost/units/systems/si/time.hpp>
#include <boost/units/systems/si/wavenumber.hpp>

#include <boost/units/systems/si/codata/typedefs.hpp>

/// \file
/// CODATA recommended values of fundamental atomic and nuclear constants
/// CODATA 2006 values as of 2007/03/30

namespace boost {

namespace units {

namespace si {

namespace constants {

namespace codata {

/// CODATA recommended values of the fundamental physical constants: NIST SP 961

/// helion mass
BOOST_UNITS_PHYSICAL_CONSTANT(m_h,quantity<mass>,5.00641192e-27*kilograms,2.5e-34*kilograms);
/// helion-electron mass ratio
BOOST_UNITS_PHYSICAL_CONSTANT(m_h_over_m_e,quantity<dimensionless>,5495.8852765*dimensionless(),5.2e-6*dimensionless());
/// helion-proton mass ratio
BOOST_UNITS_PHYSICAL_CONSTANT(m_h_over_m_p,quantity<dimensionless>,2.9931526713*dimensionless(),2.6e-9*dimensionless());
/// helion molar mass
BOOST_UNITS_PHYSICAL_CONSTANT(M_h,quantity<mass_over_amount>,3.0149322473e-3*kilograms/mole,2.6e-12*kilograms/mole);
/// helion shielded magnetic moment
BOOST_UNITS_PHYSICAL_CONSTANT(mu_h_prime,quantity<energy_over_magnetic_flux_density>,-1.074552982e-26*joules/tesla,3.0e-34*joules/tesla);
/// shielded helion-Bohr magneton ratio
BOOST_UNITS_PHYSICAL_CONSTANT(mu_h_prime_over_mu_B,quantity<dimensionless>,-1.158671471e-3*dimensionless(),1.4e-11*dimensionless());
/// shielded helion-nuclear magneton ratio
BOOST_UNITS_PHYSICAL_CONSTANT(mu_h_prime_over_mu_N,quantity<dimensionless>,-2.127497718*dimensionless(),2.5e-8*dimensionless());
/// shielded helion-proton magnetic moment ratio
BOOST_UNITS_PHYSICAL_CONSTANT(mu_h_prime_over_mu_p,quantity<dimensionless>,-0.761766558*dimensionless(),1.1e-8*dimensionless());
/// shielded helion-shielded proton magnetic moment ratio
BOOST_UNITS_PHYSICAL_CONSTANT(mu_h_prime_over_mu_p_prime,quantity<dimensionless>,-0.7617861313*dimensionless(),3.3e-8*dimensionless());
/// shielded helion gyromagnetic ratio
BOOST_UNITS_PHYSICAL_CONSTANT(gamma_h_prime,quantity<frequency_over_magnetic_flux_density>,2.037894730e8/second/tesla,5.6e-0/second/tesla);

} // namespace codata

} // namespace constants

} // namespace si

} // namespace units

} // namespace boost

#endif // BOOST_UNITS_CODATA_HELION_CONSTANTS_HPP