//
// Copyright (c) 2003 Kresimir Fresl
// Copyright (c) 2009 Rutger ter Borg
//
// 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_NUMERIC_BINDINGS_DETAIL_ARRAY_HPP
#define BOOST_NUMERIC_BINDINGS_DETAIL_ARRAY_HPP

#include <new>
#include <boost/noncopyable.hpp> 
#include <boost/numeric/bindings/detail/adaptor.hpp>


/*
 very simple dynamic array class which is used in `higher level' 
 bindings functions for pivot and work arrays 

 Namely, there are (at least) two versions of all bindings functions 
 where called LAPACK function expects work and/or pivot array, e.g.
 
      `lower' level (user should provide work and pivot arrays):
           int sysv (SymmA& a, IVec& i, MatrB& b, Work& w);
 
      `higher' level (with `internal' work and pivot arrays):
           int sysv (SymmA& a, MatrB& b);
 
 Probably you ask why I didn't use std::vector. There are two reasons. 
 First is efficiency -- std::vector's constructor initialises vector 
 elements. Second is consistency. LAPACK functions use `info' parameter 
 as an error indicator. On the other hand, std::vector's allocator can
 throw an exception if memory allocation fails. detail::array's 
 constructor uses `new (nothrow)' which returns 0 if allocation fails. 
 So I can check whether array::storage == 0 and return appropriate error 
 in `info'.*/

namespace boost {
namespace numeric {
namespace bindings {
namespace detail {

template <typename T> 
class array : private noncopyable {
public:
    typedef std::ptrdiff_t size_type ;

    array (size_type n) {
        stg = new (std::nothrow) T[n]; 
        sz = (stg != nullptr) ? n : 0; 
    }

    ~array() {
        delete[] stg;
    }

    size_type size() const {
        return sz;
    }

    bool valid() const {
        return stg != 0;
    }

    void resize (int n) {
        delete[] stg; 
        stg = new (std::nothrow) T[n]; 
        sz = (stg != 0) ? n : 0; 
    }

    T* storage() {
        return stg;
    }

    T const* storage() const {
        return stg;
    }

    T& operator[] (int i) {
        return stg[i];
    }

    T const& operator[] (int i) const {
        return stg[i];
    }

private:
    size_type sz; 
    T*        stg; 
};


template< typename T, typename Id, typename Enable >
struct adaptor< array< T >, Id, Enable > {

    typedef typename copy_const< Id, T >::type value_type;
    typedef mpl::map<
        mpl::pair< tag::value_type, value_type >,
        mpl::pair< tag::entity, tag::vector >,
        mpl::pair< tag::size_type<1>, std::ptrdiff_t >,
        mpl::pair< tag::data_structure, tag::linear_array >,
        mpl::pair< tag::stride_type<1>, tag::contiguous >
    > property_map;

    static std::ptrdiff_t size1( const Id& t ) {
        return t.size();
    }

    static value_type* begin_value( Id& t ) {
        return t.storage();
    }

    static value_type* end_value( Id& t ) {
        return t.storage() + t.size();
    }

};


} // namespace detail
} // namespace bindings
} // namespace numeric
} // namespace boost

#endif 
