text:
// Copyright Jim Bosch 2010-2012.
// 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 HALIDE_NUMPY_NDARRAY_HPP_INCLUDED
#define HALIDE_NUMPY_NDARRAY_HPP_INCLUDED

/**
 *  @file numpy/ndarray.hpp
 *  @brief Object manager and various utilities for numpy.ndarray.
 */

#include "dtype.hpp"
#include "numpy_object_mgr_traits.hpp"
#include <boost/python.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/utility/enable_if.hpp>

#include <vector>

namespace Halide {
namespace numpy {

/**
 *  @brief A boost.python "object manager" (subclass of object) for numpy.ndarray.
 *
 *  @todo This could have a lot more functionality (like boost::python::numeric::array).
 *        Right now all that exists is what was needed to move raw data between C++ and Python.
 */
class ndarray : public python::object {

    /**
   *  @brief An internal struct that's byte-compatible with PyArrayObject.
   *
   *  This is just a hack to allow inline access to this stuff while hiding numpy/arrayobject.h
   *  from the user.
   */
    struct array_struct {
        PyObject_HEAD char *data;
        int nd;
        Py_intptr_t *shape;
        Py_intptr_t *strides;
        PyObject *base;
        PyObject *descr;
        int flags;
        PyObject *weakreflist;
    };

    /// @brief Return the held Python object as an array_struct.
    array_struct *get_struct() const { return reinterpret_cast<array_struct *>(this->ptr()); }

public:
    /**
   *  @brief Enum to represent (some) of Numpy's internal flags.
   *
   *  These don't match the actual Numpy flag values; we can't get those without including
   *  numpy/arrayobject.h or copying them directly.  That's very unfortunate.
   *
   *  @todo I'm torn about whether this should be an enum.  It's very convenient to not
   *        make these simple integer values for overloading purposes, but the need to
   *        define every possible combination and custom bitwise operators is ugly.
   */
    enum bitflag {
        NONE = 0x0,
        C_CONTIGUOUS = 0x1,
        F_CONTIGUOUS = 0x2,
        V_CONTIGUOUS = 0x1 | 0x2,
        ALIGNED = 0x4,
        WRITEABLE = 0x8,
        BEHAVED = 0x4 | 0x8,
        CARRAY_RO = 0x1 | 0x4,
        CARRAY = 0x1 | 0x4 | 0x8,
        CARRAY_MIS = 0x1 | 0x8,
        FARRAY_RO = 0x2 | 0x4,
        FARRAY = 0x2 | 0x4 | 0x8,
        FARRAY_MIS = 0x2 | 0x8,
        UPDATE_ALL = 0x1 | 0x2 | 0x4,
        VARRAY = 0x1 | 0x2 | 0x8,
        ALL = 0x1 | 0x2 | 0x4 | 0x8
    };

    BOOST_PYTHON_FORWARD_OBJECT_CONSTRUCTORS(ndarray, object);

    /// @brief Return a view of the scalar with the given dtype.
    ndarray view(dtype const &dt) const;

    /// @brief Copy the array, cast to a specified type.
    ndarray astype(dtype const &dt) const;

    /// @brief Copy the scalar (deep for all non-object fields).
    ndarray copy() const;

    /// @brief Return the size of the nth dimension.
    Py_intptr_t const shape(int n) const { return get_shape()[n]; }

    /// @brief Return the stride of the nth dimension.
    Py_intptr_t const strides(int n) const { return get_strides()[n]; }

    /**
   *  @brief Return the array's raw data pointer.
   *
   *  This returns char so stride math works properly on it.  It's pretty much
   *  expected that the user will have to reinterpret_cast it.
   */
    char *get_data() const { return get_struct()->data; }

    /// @brief Return the array's data-type descriptor object.
    dtype get_dtype() const;

    /// @brief Return the object that owns the array's data, or None if the array owns its own data.
    python::object get_base() const;

    /// @brief Set the object that owns the array's data.  Use with care.
    void set_base(object const &base);

    /// @brief Return the shape of the array as an array of integers (length == get_nd()).
    Py_intptr_t const *get_shape() const { return get_struct()->shape; }

    /// @brief Return the stride of the array as an array of integers (length == get_nd()).
    Py_intptr_t const *get_strides() const { return get_struct()->strides; }

    /// @brief Return the number of array dimensions.
    int const get_nd() const { return get_struct()->nd; }

    /// @brief Return the array flags.
    bitflag const get_flags() const;

    /// @brief Reverse the dimensions of the array.
    ndarray transpose() const;

    /// @brief Eliminate any unit-sized dimensions.
    ndarray squeeze() const;

    /// @brief Equivalent to self.reshape(*shape) in Python.
    ndarray reshape(python::tuple const &shape) const;

    /**
   *  @brief If the array contains only a single element, return it as an array scalar; otherwise return
   *         the array.
   *
   *  @internal This is simply a call to PyArray_Return();
   */
    python::object scalarize() const;
};

/**
 *  @brief Construct a new array with the given shape and data type, with data initialized to zero.
 */
ndarray zeros(python::tuple const &shape, dtype const &dt);
ndarray zeros(int nd, Py_intptr_t const *shape, dtype const &dt);

/**
 *  @brief Construct a new array with the given shape and data type, with data left uninitialized.
 */
ndarray empty(python::tuple const &shape, dtype const &dt);
ndarray empty(int nd, Py_intptr_t const *shape, dtype const &dt);

/**
 *  @brief Construct a new array from an arbitrary Python sequence.
 *
 *  @todo This does't seem to handle ndarray subtypes the same way that "numpy.array" does in Python.
 */
ndarray array(python::object const &obj);
ndarray array(python::object const &obj, dtype const &dt);

namespace detail {

ndarray from_data_impl(void *data,
                       dtype const &dt,
                       std::vector<Py_intptr_t> const &shape,
                       std::vector<Py_intptr_t> const &strides,
                       python::object const &owner,
                       bool writeable);

template <typename Container>
ndarray from_data_impl(void *data,
                       dtype const &dt,
                       Container shape,
                       Container strides,
                       python::object const &owner,
                       bool writeable,
                       typename boost::enable_if<boost::is_integral<typename Container::value_type>>::type *enabled = NULL) {
    std::vector<Py_intptr_t> shape_(shape.begin(), shape.end());
    std::vector<Py_intptr_t> strides_(strides.begin(), strides.end());
    return from_data_impl(data, dt, shape_, strides_, owner, writeable);
}

ndarray from_data_impl(void *data,
                       dtype const &dt,
                       python::object const &shape,
                       python::object const &strides,
                       python::object const &owner,
                       bool writeable);

}  // namespace Halide::numpy::detail

/**
 *  @brief Construct a new ndarray object from a raw pointer.
 *
 *  @param[in] data    Raw pointer to the first element of the array.
 *  @param[in] dt      Data type descriptor.  Often retrieved with dtype::get_builtin().
 *  @param[in] shape   Shape of the array as STL container of integers; must have begin() and end().
 *  @param[in] strides Shape of the array as STL container of integers; must have begin() and end().
 *  @param[in] owner   An arbitray Python object that owns that data pointer.  The array object will
 *                     keep a reference to the object, and decrement it's reference count when the
 *                     array goes out of scope.  Pass None at your own peril.
 *
 *  @todo Should probably take ranges of iterators rather than actual container objects.
 */
template <typename Container>
inline ndarray from_data(void *data,
                         dtype const &dt,
                         Container shape,
                         Container strides,
                         python::object const &owner) {
    return numpy::detail::from_data_impl(data, dt, shape, strides, owner, true);
}

/**
 *  @brief Construct a new ndarray object from a raw pointer.
 *
 *  @param[in] data    Raw pointer to the first element of the array.
 *  @param[in] dt      Data type descriptor.  Often retrieved with dtype::get_builtin().
 *  @param[in] shape   Shape of the array as STL container of integers; must have begin() and end().
 *  @param[in] strides Shape of the array as STL container of integers; must have begin() and end().
 *  @param[in] owner   An arbitray Python object that owns that data pointer.  The array object will
 *                     keep a reference to the object, and decrement it's reference count when the
 *                     array goes out of scope.  Pass None at your own peril.
 *
 *  This overload takes a const void pointer and sets the "writeable" flag of the array to false.
 *
 *  @todo Should probably take ranges of iterators rather than actual container objects.
 */
template <typename Container>
inline ndarray from_data(void const *data,
                         dtype const &dt,
                         Container shape,
                         Container strides,
                         python::object const &owner) {
    return numpy::detail::from_data_impl(const_cast<void *>(data), dt, shape, strides, owner, false);
}

/**
 *  @brief Transform an arbitrary object into a numpy array with the given requirements.
 *
 *  @param[in] obj     An arbitrary python object to convert.  Arrays that meet the requirements
 *                     will be passed through directly.
 *  @param[in] dt      Data type descriptor.  Often retrieved with dtype::get_builtin().
 *  @param[in] nd_min  Minimum number of dimensions.
 *  @param[in] nd_max  Maximum number of dimensions.
 *  @param[in] flags   Bitwise OR of flags specifying additional requirements.
 */
ndarray from_object(python::object const &obj, dtype const &dt,
                    int nd_min, int nd_max, ndarray::bitflag flags = ndarray::NONE);

inline ndarray from_object(python::object const &obj, dtype const &dt,
                           int nd, ndarray::bitflag flags = ndarray::NONE) {
    return from_object(obj, dt, nd, nd, flags);
}

inline ndarray from_object(python::object const &obj, dtype const &dt, ndarray::bitflag flags = ndarray::NONE) {
    return from_object(obj, dt, 0, 0, flags);
}

ndarray from_object(python::object const &obj, int nd_min, int nd_max,
                    ndarray::bitflag flags = ndarray::NONE);

inline ndarray from_object(python::object const &obj, int nd, ndarray::bitflag flags = ndarray::NONE) {
    return from_object(obj, nd, nd, flags);
}

inline ndarray from_object(python::object const &obj, ndarray::bitflag flags = ndarray::NONE) {
    return from_object(obj, 0, 0, flags);
}

inline ndarray::bitflag operator|(ndarray::bitflag a, ndarray::bitflag b) {
    return ndarray::bitflag(int(a) | int(b));
}

inline ndarray::bitflag operator&(ndarray::bitflag a, ndarray::bitflag b) {
    return ndarray::bitflag(int(a) & int(b));
}

}  // namespace Halide::numpy
}  // namespace Halide

namespace boost {
namespace python {
namespace converter {

NUMPY_OBJECT_MANAGER_TRAITS(Halide::numpy::ndarray);

}  // namespace boost::python::converter
}  // namespace boost::python
}  // namespace boost

#endif  // !HALIDE_NUMPY_NDARRAY_HPP_INCLUDED

max_stars_repo_name: akifoezkan/Halide-HLS

max_issues_repo_name: akifoezkan/Halide-HLS

max_forks_repo_name: akifoezkan/Halide-HLS


text:
//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2005-2013. 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)
//
// See http://www.boost.org/libs/container for documentation.
//
//////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_CONTAINER_FLAT_SET_HPP
#define BOOST_CONTAINER_FLAT_SET_HPP

#ifndef BOOST_CONFIG_HPP
#  include <boost/config.hpp>
#endif

#if defined(BOOST_HAS_PRAGMA_ONCE)
#  pragma once
#endif

#include <boost/container/detail/config_begin.hpp>
#include <boost/container/detail/workaround.hpp>

// container
#include <boost/container/allocator_traits.hpp>
#include <boost/container/container_fwd.hpp>
#include <boost/container/new_allocator.hpp> //new_allocator
// container/detail
#include <boost/container/detail/flat_tree.hpp>
#include <boost/container/detail/mpl.hpp>
// move
#include <boost/move/traits.hpp>
#include <boost/move/utility_core.hpp>
// move/detail
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
#include <boost/move/detail/fwd_macros.hpp>
#endif
#include <boost/move/detail/move_helpers.hpp>
// intrusive/detail
#include <boost/intrusive/detail/minimal_pair_header.hpp>      //pair
#include <boost/intrusive/detail/minimal_less_equal_header.hpp>//less, equal
// std
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
#include <initializer_list>
#endif

namespace boost {
namespace container {

#if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
template <class Key, class T, class Compare, class Allocator>
class flat_multimap;
#endif

//! flat_set is a Sorted Associative Container that stores objects of type Key.
//! It is also a Unique Associative Container, meaning that no two elements are the same.
//!
//! flat_set is similar to std::set but it's implemented like an ordered vector.
//! This means that inserting a new element into a flat_set invalidates
//! previous iterators and references
//!
//! Erasing an element of a flat_set invalidates iterators and references
//! pointing to elements that come after (their keys are bigger) the erased element.
//!
//! This container provides random-access iterators.
//!
//! \tparam Key is the type to be inserted in the set, which is also the key_type
//! \tparam Compare is the comparison functor used to order keys
//! \tparam Allocator is the allocator to be used to allocate memory for this container
#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED
template <class Key, class Compare = std::less<Key>, class Allocator = new_allocator<Key> >
#else
template <class Key, class Compare, class Allocator>
#endif
class flat_set
   ///@cond
   : public container_detail::flat_tree<Key, container_detail::identity<Key>, Compare, Allocator>
   ///@endcond
{
   #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
   private:
   BOOST_COPYABLE_AND_MOVABLE(flat_set)
   typedef container_detail::flat_tree<Key, container_detail::identity<Key>, Compare, Allocator> base_t;

   public:
   base_t &tree()
   {  return *this;  }

   const base_t &tree() const
   {  return *this;  }

   #endif   //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED

   public:
   //////////////////////////////////////////////
   //
   //                    types
   //
   //////////////////////////////////////////////
   typedef Key                                                                         key_type;
   typedef Key                                                                         value_type;
   typedef Compare                                                                     key_compare;
   typedef Compare                                                                     value_compare;
   typedef ::boost::container::allocator_traits<Allocator>                             allocator_traits_type;
   typedef typename ::boost::container::allocator_traits<Allocator>::pointer           pointer;
   typedef typename ::boost::container::allocator_traits<Allocator>::const_pointer     const_pointer;
   typedef typename ::boost::container::allocator_traits<Allocator>::reference         reference;
   typedef typename ::boost::container::allocator_traits<Allocator>::const_reference   const_reference;
   typedef typename ::boost::container::allocator_traits<Allocator>::size_type         size_type;
   typedef typename ::boost::container::allocator_traits<Allocator>::difference_type   difference_type;
   typedef Allocator                                                                   allocator_type;
   typedef typename BOOST_CONTAINER_IMPDEF(base_t::stored_allocator_type)              stored_allocator_type;
   typedef typename BOOST_CONTAINER_IMPDEF(base_t::iterator)                           iterator;
   typedef typename BOOST_CONTAINER_IMPDEF(base_t::const_iterator)                     const_iterator;
   typedef typename BOOST_CONTAINER_IMPDEF(base_t::reverse_iterator)                   reverse_iterator;
   typedef typename BOOST_CONTAINER_IMPDEF(base_t::const_reverse_iterator)             const_reverse_iterator;

   public:
   //////////////////////////////////////////////
   //
   //          construct/copy/destroy
   //
   //////////////////////////////////////////////

   //! Effects: Default constructs an empty container.
   //!
   //! Complexity: Constant.
   explicit flat_set() BOOST_NOEXCEPT_IF(container_detail::is_nothrow_default_constructible<Allocator>::value &&
                                         container_detail::is_nothrow_default_constructible<Compare>::value)
      : base_t()
   {}

   //! Effects: Constructs an empty container using the specified
   //! comparison object and allocator.
   //!
   //! Complexity: Constant.
   explicit flat_set(const Compare& comp,
                     const allocator_type& a = allocator_type())
      : base_t(comp, a)
   {}

   //! Effects: Constructs an empty container using the specified allocator.
   //!
   //! Complexity: Constant.
   explicit flat_set(const allocator_type& a)
      : base_t(a)
   {}

   //! Effects: Constructs an empty container using the specified comparison object and
   //! allocator, and inserts elements from the range [first ,last ).
   //!
   //! Complexity: Linear in N if the range [first ,last ) is already sorted using
   //! comp and otherwise N logN, where N is last - first.
   template <class InputIterator>
   flat_set(InputIterator first, InputIterator last,
            const Compare& comp = Compare(),
            const allocator_type& a = allocator_type())
      : base_t(true, first, last, comp, a)
   {}

   //! Effects: Constructs an empty container using the specified
   //! allocator, and inserts elements from the range [first ,last ).
   //!
   //! Complexity: Linear in N if the range [first ,last ) is already sorted using
   //! comp and otherwise N logN, where N is last - first.
   template <class InputIterator>
   flat_set(InputIterator first, InputIterator last, const allocator_type& a)
      : base_t(true, first, last, Compare(), a)
   {}

   //! Effects: Constructs an empty container using the specified comparison object and
   //! allocator, and inserts elements from the ordered unique range [first ,last). This function
   //! is more efficient than the normal range creation for ordered ranges.
   //!
   //! Requires: [first ,last) must be ordered according to the predicate and must be
   //! unique values.
   //!
   //! Complexity: Linear in N.
   //!
   //! Note: Non-standard extension.
   template <class InputIterator>
   flat_set(ordered_unique_range_t, InputIterator first, InputIterator last,
            const Compare& comp = Compare(),
            const allocator_type& a = allocator_type())
      : base_t(ordered_unique_range, first, last, comp, a)
   {}

#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
   //! Effects: Constructs an empty container using the specified comparison object and
   //! allocator, and inserts elements from the range [il.begin(), il.end()).
   //!
   //! Complexity: Linear in N if the range [il.begin(), il.end()) is already sorted using
   //! comp and otherwise N logN, where N is il.begin() - il.end().
   flat_set(std::initializer_list<value_type> il, const Compare& comp = Compare(),
            const allocator_type& a = allocator_type())
      : base_t(true, il.begin(), il.end(), comp, a)
   {}

   //! Effects: Constructs an empty container using the specified
   //! allocator, and inserts elements from the range [il.begin(), il.end()).
   //!
   //! Complexity: Linear in N if the range [il.begin(), il.end()) is already sorted using
   //! comp and otherwise N logN, where N is il.begin() - il.end().
   flat_set(std::initializer_list<value_type> il, const allocator_type& a)
      : base_t(true, il.begin(), il.end(), Compare(), a)
   {}

   //! Effects: Constructs an empty container using the specified comparison object and
   //! allocator, and inserts elements from the ordered unique range [il.begin(), il.end()). This function
   //! is more efficient than the normal range creation for ordered ranges.
   //!
   //! Requires: [il.begin(), il.end()) must be ordered according to the predicate and must be
   //! unique values.
   //!
   //! Complexity: Linear in N.
   //!
   //! Note: Non-standard extension.
   flat_set(ordered_unique_range_t, std::initializer_list<value_type> il,
            const Compare& comp = Compare(), const allocator_type& a = allocator_type())
      : base_t(ordered_unique_range, il.begin(), il.end(), comp, a)
   {}
#endif

   //! Effects: Copy constructs the container.
   //!
   //! Complexity: Linear in x.size().
   flat_set(const flat_set& x)
      : base_t(static_cast<const base_t&>(x))
   {}

   //! Effects: Move constructs thecontainer. Constructs *this using x's resources.
   //!
   //! Complexity: Constant.
   //!
   //! Postcondition: x is emptied.
   flat_set(BOOST_RV_REF(flat_set) x)
      BOOST_NOEXCEPT_IF(boost::container::container_detail::is_nothrow_move_constructible<Compare>::value)
      : base_t(BOOST_MOVE_BASE(base_t, x))
   {}

   //! Effects: Copy constructs a container using the specified allocator.
   //!
   //! Complexity: Linear in x.size().
   flat_set(const flat_set& x, const allocator_type &a)
      : base_t(static_cast<const base_t&>(x), a)
   {}

   //! Effects: Move constructs a container using the specified allocator.
   //!                 Constructs *this using x's resources.
   //!
   //! Complexity: Constant if a == x.get_allocator(), linear otherwise
   flat_set(BOOST_RV_REF(flat_set) x, const allocator_type &a)
      : base_t(BOOST_MOVE_BASE(base_t, x), a)
   {}

   //! Effects: Makes *this a copy of x.
   //!
   //! Complexity: Linear in x.size().
   flat_set& operator=(BOOST_COPY_ASSIGN_REF(flat_set) x)
   {  return static_cast<flat_set&>(this->base_t::operator=(static_cast<const base_t&>(x)));  }

   //! Throws: If allocator_traits_type::propagate_on_container_move_assignment
   //!   is false and (allocation throws or value_type's move constructor throws)
   //!
   //! Complexity: Constant if allocator_traits_type::
   //!   propagate_on_container_move_assignment is true or
   //!   this->get>allocator() == x.get_allocator(). Linear otherwise.
   flat_set& operator=(BOOST_RV_REF(flat_set) x)
      BOOST_NOEXCEPT_IF( (allocator_traits_type::propagate_on_container_move_assignment::value ||
                          allocator_traits_type::is_always_equal::value) &&
                           boost::container::container_detail::is_nothrow_move_assignable<Compare>::value)
   {  return static_cast<flat_set&>(this->base_t::operator=(BOOST_MOVE_BASE(base_t, x)));  }

#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
   //! Effects: Copy all elements from il to *this.
   //!
   //! Complexity: Linear in il.size().
   flat_set& operator=(std::initializer_list<value_type> il)
   {
       this->clear();
       this->insert(il.begin(), il.end());
       return *this;
   }
#endif

   #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED
   //! Effects: Returns a copy of the allocator that
   //!   was passed to the object's constructor.
   //!
   //! Complexity: Constant.
   allocator_type get_allocator() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a reference to the internal allocator.
   //!
   //! Throws: Nothing
   //!
   //! Complexity: Constant.
   //!
   //! Note: Non-standard extension.
   stored_allocator_type &get_stored_allocator() BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a reference to the internal allocator.
   //!
   //! Throws: Nothing
   //!
   //! Complexity: Constant.
   //!
   //! Note: Non-standard extension.
   const stored_allocator_type &get_stored_allocator() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns an iterator to the first element contained in the container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   iterator begin() BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a const_iterator to the first element contained in the container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   const_iterator begin() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns an iterator to the end of the container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   iterator end() BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a const_iterator to the end of the container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   const_iterator end() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a reverse_iterator pointing to the beginning
   //! of the reversed container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   reverse_iterator rbegin() BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a const_reverse_iterator pointing to the beginning
   //! of the reversed container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   const_reverse_iterator rbegin() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a reverse_iterator pointing to the end
   //! of the reversed container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   reverse_iterator rend() BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a const_reverse_iterator pointing to the end
   //! of the reversed container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   const_reverse_iterator rend() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a const_iterator to the first element contained in the container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   const_iterator cbegin() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a const_iterator to the end of the container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   const_iterator cend() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a const_reverse_iterator pointing to the beginning
   //! of the reversed container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   const_reverse_iterator crbegin() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns a const_reverse_iterator pointing to the end
   //! of the reversed container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   const_reverse_iterator crend() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns true if the container contains no elements.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   bool empty() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns the number of the elements contained in the container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   size_type size() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns the largest possible size of the container.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   size_type max_size() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Number of elements for which memory has been allocated.
   //!   capacity() is always greater than or equal to size().
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   size_type capacity() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: If n is less than or equal to capacity(), this call has no
   //!   effect. Otherwise, it is a request for allocation of additional memory.
   //!   If the request is successful, then capacity() is greater than or equal to
   //!   n; otherwise, capacity() is unchanged. In either case, size() is unchanged.
   //!
   //! Throws: If memory allocation allocation throws or Key's copy constructor throws.
   //!
   //! Note: If capacity() is less than "cnt", iterators and references to
   //!   to values might be invalidated.
   void reserve(size_type cnt);

   //! Effects: Tries to deallocate the excess of memory created
   //    with previous allocations. The size of the vector is unchanged
   //!
   //! Throws: If memory allocation throws, or Key's copy constructor throws.
   //!
   //! Complexity: Linear to size().
   void shrink_to_fit();

   #endif   //   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)

   //////////////////////////////////////////////
   //
   //                modifiers
   //
   //////////////////////////////////////////////

   #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED)

   //! Effects: Inserts an object x of type Key constructed with
   //!   std::forward<Args>(args)... if and only if there is no element in the container
   //!   with key equivalent to the key of x.
   //!
   //! Returns: The bool component of the returned pair is true if and only
   //!   if the insertion takes place, and the iterator component of the pair
   //!   points to the element with key equivalent to the key of x.
   //!
   //! Complexity: Logarithmic search time plus linear insertion
   //!   to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   template <class... Args>
   std::pair<iterator,bool> emplace(BOOST_FWD_REF(Args)... args)
   {  return this->base_t::emplace_unique(boost::forward<Args>(args)...); }

   //! Effects: Inserts an object of type Key constructed with
   //!   std::forward<Args>(args)... in the container if and only if there is
   //!   no element in the container with key equivalent to the key of x.
   //!   p is a hint pointing to where the insert should start to search.
   //!
   //! Returns: An iterator pointing to the element with key equivalent
   //!   to the key of x.
   //!
   //! Complexity: Logarithmic search time (constant if x is inserted
   //!   right before p) plus insertion linear to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   template <class... Args>
   iterator emplace_hint(const_iterator p, BOOST_FWD_REF(Args)... args)
   {  return this->base_t::emplace_hint_unique(p, boost::forward<Args>(args)...); }

   #else // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

   #define BOOST_CONTAINER_FLAT_SET_EMPLACE_CODE(N) \
   BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
   std::pair<iterator,bool> emplace(BOOST_MOVE_UREF##N)\
   {  return this->base_t::emplace_unique(BOOST_MOVE_FWD##N);  }\
   \
   BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
   iterator emplace_hint(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\
   {  return this->base_t::emplace_hint_unique(hint BOOST_MOVE_I##N BOOST_MOVE_FWD##N); }\
   //
   BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_FLAT_SET_EMPLACE_CODE)
   #undef BOOST_CONTAINER_FLAT_SET_EMPLACE_CODE

   #endif   // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
   //! Effects: Inserts x if and only if there is no element in the container
   //!   with key equivalent to the key of x.
   //!
   //! Returns: The bool component of the returned pair is true if and only
   //!   if the insertion takes place, and the iterator component of the pair
   //!   points to the element with key equivalent to the key of x.
   //!
   //! Complexity: Logarithmic search time plus linear insertion
   //!   to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   std::pair<iterator, bool> insert(const value_type &x);

   //! Effects: Inserts a new value_type move constructed from the pair if and
   //! only if there is no element in the container with key equivalent to the key of x.
   //!
   //! Returns: The bool component of the returned pair is true if and only
   //!   if the insertion takes place, and the iterator component of the pair
   //!   points to the element with key equivalent to the key of x.
   //!
   //! Complexity: Logarithmic search time plus linear insertion
   //!   to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   std::pair<iterator, bool> insert(value_type &&x);
   #else
   private:
   typedef std::pair<iterator, bool> insert_return_pair;
   public:
   BOOST_MOVE_CONVERSION_AWARE_CATCH(insert, value_type, insert_return_pair, this->priv_insert)
   #endif

   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
   //! Effects: Inserts a copy of x in the container if and only if there is
   //!   no element in the container with key equivalent to the key of x.
   //!   p is a hint pointing to where the insert should start to search.
   //!
   //! Returns: An iterator pointing to the element with key equivalent
   //!   to the key of x.
   //!
   //! Complexity: Logarithmic search time (constant if x is inserted
   //!   right before p) plus insertion linear to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   iterator insert(const_iterator p, const value_type &x);

   //! Effects: Inserts an element move constructed from x in the container.
   //!   p is a hint pointing to where the insert should start to search.
   //!
   //! Returns: An iterator pointing to the element with key equivalent to the key of x.
   //!
   //! Complexity: Logarithmic search time (constant if x is inserted
   //!   right before p) plus insertion linear to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   iterator insert(const_iterator p, value_type &&x);
   #else
   BOOST_MOVE_CONVERSION_AWARE_CATCH_1ARG(insert, value_type, iterator, this->priv_insert, const_iterator, const_iterator)
   #endif

   //! Requires: first, last are not iterators into *this.
   //!
   //! Effects: inserts each element from the range [first,last) if and only
   //!   if there is no element with key equivalent to the key of that element.
   //!
   //! Complexity: At most N log(size()+N) (N is the distance from first to last)
   //!   search time plus N*size() insertion time.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   template <class InputIterator>
   void insert(InputIterator first, InputIterator last)
      {  this->base_t::insert_unique(first, last);  }

   //! Requires: first, last are not iterators into *this and
   //! must be ordered according to the predicate and must be
   //! unique values.
   //!
   //! Effects: inserts each element from the range [first,last) .This function
   //! is more efficient than the normal range creation for ordered ranges.
   //!
   //! Complexity: At most N log(size()+N) (N is the distance from first to last)
   //!   search time plus N*size() insertion time.
   //!
   //! Note: Non-standard extension. If an element is inserted it might invalidate elements.
   template <class InputIterator>
   void insert(ordered_unique_range_t, InputIterator first, InputIterator last)
      {  this->base_t::insert_unique(ordered_unique_range, first, last);  }

#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
   //! Effects: inserts each element from the range [il.begin(), il.end()) if and only
   //!   if there is no element with key equivalent to the key of that element.
   //!
   //! Complexity: At most N log(size()+N) (N is the distance from il.begin() to il.end())
   //!   search time plus N*size() insertion time.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   void insert(std::initializer_list<value_type> il)
   {  this->base_t::insert_unique(il.begin(), il.end()); }

   //! Requires: Range [il.begin(), il.end()) must be ordered according to the predicate
   //! and must be unique values.
   //!
   //! Effects: inserts each element from the range [il.begin(), il.end()) .This function
   //! is more efficient than the normal range creation for ordered ranges.
   //!
   //! Complexity: At most N log(size()+N) (N is the distance from il.begin() to il.end())
   //!   search time plus N*size() insertion time.
   //!
   //! Note: Non-standard extension. If an element is inserted it might invalidate elements.
   void insert(ordered_unique_range_t, std::initializer_list<value_type> il)
   {  this->base_t::insert_unique(ordered_unique_range, il.begin(), il.end()); }
#endif

   //! @copydoc ::boost::container::flat_map::merge(flat_map<Key, T, C2, Allocator>&)
   template<class C2>
   BOOST_CONTAINER_FORCEINLINE void merge(flat_set<Key, C2, Allocator>& source)
   {  this->base_t::merge_unique(source.tree());   }

   //! @copydoc ::boost::container::flat_map::merge(flat_set<Key, C2, Allocator>&)
   template<class C2>
   BOOST_CONTAINER_FORCEINLINE void merge(BOOST_RV_REF_BEG flat_set<Key, C2, Allocator> BOOST_RV_REF_END source)
   {  return this->merge(static_cast<flat_set<Key, C2, Allocator>&>(source));   }

   //! @copydoc ::boost::container::flat_map::merge(flat_multimap<Key, T, C2, Allocator>&)
   template<class C2>
   BOOST_CONTAINER_FORCEINLINE void merge(flat_multiset<Key, C2, Allocator>& source)
   {  this->base_t::merge_unique(source.tree());   }

   //! @copydoc ::boost::container::flat_map::merge(flat_multiset<Key, C2, Allocator>&)
   template<class C2>
   BOOST_CONTAINER_FORCEINLINE void merge(BOOST_RV_REF_BEG flat_multiset<Key, C2, Allocator> BOOST_RV_REF_END source)
   {  return this->merge(static_cast<flat_multiset<Key, C2, Allocator>&>(source));   }

   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)

   //! Effects: Erases the element pointed to by p.
   //!
   //! Returns: Returns an iterator pointing to the element immediately
   //!   following q prior to the element being erased. If no such element exists,
   //!   returns end().
   //!
   //! Complexity: Linear to the elements with keys bigger than p
   //!
   //! Note: Invalidates elements with keys
   //!   not less than the erased element.
   iterator erase(const_iterator p);

   //! Effects: Erases all elements in the container with key equivalent to x.
   //!
   //! Returns: Returns the number of erased elements.
   //!
   //! Complexity: Logarithmic search time plus erasure time
   //!   linear to the elements with bigger keys.
   size_type erase(const key_type& x);

   //! Effects: Erases all the elements in the range [first, last).
   //!
   //! Returns: Returns last.
   //!
   //! Complexity: size()*N where N is the distance from first to last.
   //!
   //! Complexity: Logarithmic search time plus erasure time
   //!   linear to the elements with bigger keys.
   iterator erase(const_iterator first, const_iterator last);

   //! Effects: Swaps the contents of *this and x.
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   void swap(flat_set& x)
      BOOST_NOEXCEPT_IF(  allocator_traits_type::is_always_equal::value
                                 && boost::container::container_detail::is_nothrow_swappable<Compare>::value );

   //! Effects: erase(a.begin(),a.end()).
   //!
   //! Postcondition: size() == 0.
   //!
   //! Complexity: linear in size().
   void clear() BOOST_NOEXCEPT_OR_NOTHROW;

   //! Effects: Returns the comparison object out
   //!   of which a was constructed.
   //!
   //! Complexity: Constant.
   key_compare key_comp() const;

   //! Effects: Returns an object of value_compare constructed out
   //!   of the comparison object.
   //!
   //! Complexity: Constant.
   value_compare value_comp() const;

   //! Returns: An iterator pointing to an element with the key
   //!   equivalent to x, or end() if such an element is not found.
   //!
   //! Complexity: Logarithmic.
   iterator find(const key_type& x);

   //! Returns: A const_iterator pointing to an element with the key
   //!   equivalent to x, or end() if such an element is not found.
   //!
   //! Complexity: Logarithmic.
   const_iterator find(const key_type& x) const;

   //! Requires: size() >= n.
   //!
   //! Effects: Returns an iterator to the nth element
   //!   from the beginning of the container. Returns end()
   //!   if n == size().
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   //!
   //! Note: Non-standard extension
   iterator nth(size_type n) BOOST_NOEXCEPT_OR_NOTHROW;

   //! Requires: size() >= n.
   //!
   //! Effects: Returns a const_iterator to the nth element
   //!   from the beginning of the container. Returns end()
   //!   if n == size().
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   //!
   //! Note: Non-standard extension
   const_iterator nth(size_type n) const BOOST_NOEXCEPT_OR_NOTHROW;

   //! Requires: begin() <= p <= end().
   //!
   //! Effects: Returns the index of the element pointed by p
   //!   and size() if p == end().
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   //!
   //! Note: Non-standard extension
   size_type index_of(iterator p) BOOST_NOEXCEPT_OR_NOTHROW;

   //! Requires: begin() <= p <= end().
   //!
   //! Effects: Returns the index of the element pointed by p
   //!   and size() if p == end().
   //!
   //! Throws: Nothing.
   //!
   //! Complexity: Constant.
   //!
   //! Note: Non-standard extension
   size_type index_of(const_iterator p) const BOOST_NOEXCEPT_OR_NOTHROW;

   #endif   //   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)

   //! Returns: The number of elements with key equivalent to x.
   //!
   //! Complexity: log(size())+count(k)
   size_type count(const key_type& x) const
   {  return static_cast<size_type>(this->base_t::find(x) != this->base_t::cend());  }

   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
   //! Returns: An iterator pointing to the first element with key not less
   //!   than k, or a.end() if such an element is not found.
   //!
   //! Complexity: Logarithmic
   iterator lower_bound(const key_type& x);

   //! Returns: A const iterator pointing to the first element with key not
   //!   less than k, or a.end() if such an element is not found.
   //!
   //! Complexity: Logarithmic
   const_iterator lower_bound(const key_type& x) const;

   //! Returns: An iterator pointing to the first element with key not less
   //!   than x, or end() if such an element is not found.
   //!
   //! Complexity: Logarithmic
   iterator upper_bound(const key_type& x);

   //! Returns: A const iterator pointing to the first element with key not
   //!   less than x, or end() if such an element is not found.
   //!
   //! Complexity: Logarithmic
   const_iterator upper_bound(const key_type& x) const;

   #endif   //   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)

   //! Effects: Equivalent to std::make_pair(this->lower_bound(k), this->upper_bound(k)).
   //!
   //! Complexity: Logarithmic
   std::pair<const_iterator, const_iterator> equal_range(const key_type& x) const
   {  return this->base_t::lower_bound_range(x);  }

   //! Effects: Equivalent to std::make_pair(this->lower_bound(k), this->upper_bound(k)).
   //!
   //! Complexity: Logarithmic
   std::pair<iterator,iterator> equal_range(const key_type& x)
   {  return this->base_t::lower_bound_range(x);  }

   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)

   //! Effects: Returns true if x and y are equal
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator==(const flat_set& x, const flat_set& y);

   //! Effects: Returns true if x and y are unequal
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator!=(const flat_set& x, const flat_set& y);

   //! Effects: Returns true if x is less than y
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator<(const flat_set& x, const flat_set& y);

   //! Effects: Returns true if x is greater than y
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator>(const flat_set& x, const flat_set& y);

   //! Effects: Returns true if x is equal or less than y
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator<=(const flat_set& x, const flat_set& y);

   //! Effects: Returns true if x is equal or greater than y
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator>=(const flat_set& x, const flat_set& y);

   //! Effects: x.swap(y)
   //!
   //! Complexity: Constant.
   friend void swap(flat_set& x, flat_set& y);

   #endif   //#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED

   #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
   private:
   template<class KeyType>
   std::pair<iterator, bool> priv_insert(BOOST_FWD_REF(KeyType) x)
   {  return this->base_t::insert_unique(::boost::forward<KeyType>(x));  }

   template<class KeyType>
   iterator priv_insert(const_iterator p, BOOST_FWD_REF(KeyType) x)
   {  return this->base_t::insert_unique(p, ::boost::forward<KeyType>(x)); }
   #endif   //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
};

#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED

}  //namespace container {

//!has_trivial_destructor_after_move<> == true_type
//!specialization for optimizations
template <class Key, class Compare, class Allocator>
struct has_trivial_destructor_after_move<boost::container::flat_set<Key, Compare, Allocator> >
{
   typedef typename ::boost::container::allocator_traits<Allocator>::pointer pointer;
   static const bool value = ::boost::has_trivial_destructor_after_move<Allocator>::value &&
                             ::boost::has_trivial_destructor_after_move<pointer>::value &&
                             ::boost::has_trivial_destructor_after_move<Compare>::value;
};

namespace container {

#endif   //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED

//! flat_multiset is a Sorted Associative Container that stores objects of type Key.
//!
//! flat_multiset can store multiple copies of the same key value.
//!
//! flat_multiset is similar to std::multiset but it's implemented like an ordered vector.
//! This means that inserting a new element into a flat_multiset invalidates
//! previous iterators and references
//!
//! Erasing an element invalidates iterators and references
//! pointing to elements that come after (their keys are bigger) the erased element.
//!
//! This container provides random-access iterators.
//!
//! \tparam Key is the type to be inserted in the multiset, which is also the key_type
//! \tparam Compare is the comparison functor used to order keys
//! \tparam Allocator is the allocator to be used to allocate memory for this container
#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED
template <class Key, class Compare = std::less<Key>, class Allocator = new_allocator<Key> >
#else
template <class Key, class Compare, class Allocator>
#endif
class flat_multiset
   ///@cond
   : public container_detail::flat_tree<Key, container_detail::identity<Key>, Compare, Allocator>
   ///@endcond
{
   #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
   private:
   BOOST_COPYABLE_AND_MOVABLE(flat_multiset)
   typedef container_detail::flat_tree<Key, container_detail::identity<Key>, Compare, Allocator> base_t;

   public:
   base_t &tree()
   {  return *this;  }

   const base_t &tree() const
   {  return *this;  }
   #endif   //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED

   public:
   //////////////////////////////////////////////
   //
   //                    types
   //
   //////////////////////////////////////////////
   typedef Key                                                                         key_type;
   typedef Key                                                                         value_type;
   typedef Compare                                                                     key_compare;
   typedef Compare                                                                     value_compare;
   typedef ::boost::container::allocator_traits<Allocator>                             allocator_traits_type;
   typedef typename ::boost::container::allocator_traits<Allocator>::pointer           pointer;
   typedef typename ::boost::container::allocator_traits<Allocator>::const_pointer     const_pointer;
   typedef typename ::boost::container::allocator_traits<Allocator>::reference         reference;
   typedef typename ::boost::container::allocator_traits<Allocator>::const_reference   const_reference;
   typedef typename ::boost::container::allocator_traits<Allocator>::size_type         size_type;
   typedef typename ::boost::container::allocator_traits<Allocator>::difference_type   difference_type;
   typedef Allocator                                                                   allocator_type;
   typedef typename BOOST_CONTAINER_IMPDEF(base_t::stored_allocator_type)              stored_allocator_type;
   typedef typename BOOST_CONTAINER_IMPDEF(base_t::iterator)                           iterator;
   typedef typename BOOST_CONTAINER_IMPDEF(base_t::const_iterator)                     const_iterator;
   typedef typename BOOST_CONTAINER_IMPDEF(base_t::reverse_iterator)                   reverse_iterator;
   typedef typename BOOST_CONTAINER_IMPDEF(base_t::const_reverse_iterator)             const_reverse_iterator;

   //! @copydoc ::boost::container::flat_set::flat_set()
   explicit flat_multiset() BOOST_NOEXCEPT_IF(container_detail::is_nothrow_default_constructible<Allocator>::value &&
                                              container_detail::is_nothrow_default_constructible<Compare>::value)
      : base_t()
   {}

   //! @copydoc ::boost::container::flat_set::flat_set(const Compare&, const allocator_type&)
   explicit flat_multiset(const Compare& comp,
                          const allocator_type& a = allocator_type())
      : base_t(comp, a)
   {}

   //! @copydoc ::boost::container::flat_set::flat_set(const allocator_type&)
   explicit flat_multiset(const allocator_type& a)
      : base_t(a)
   {}

   //! @copydoc ::boost::container::flat_set::flat_set(InputIterator, InputIterator, const Compare& comp, const allocator_type&)
   template <class InputIterator>
   flat_multiset(InputIterator first, InputIterator last,
                 const Compare& comp        = Compare(),
                 const allocator_type& a = allocator_type())
      : base_t(false, first, last, comp, a)
   {}

   //! @copydoc ::boost::container::flat_set::flat_set(InputIterator, InputIterator, const allocator_type&)
   template <class InputIterator>
   flat_multiset(InputIterator first, InputIterator last, const allocator_type& a)
      : base_t(false, first, last, Compare(), a)
   {}

   //! Effects: Constructs an empty flat_multiset using the specified comparison object and
   //! allocator, and inserts elements from the ordered range [first ,last ). This function
   //! is more efficient than the normal range creation for ordered ranges.
   //!
   //! Requires: [first ,last) must be ordered according to the predicate.
   //!
   //! Complexity: Linear in N.
   //!
   //! Note: Non-standard extension.
   template <class InputIterator>
   flat_multiset(ordered_range_t, InputIterator first, InputIterator last,
                 const Compare& comp        = Compare(),
                 const allocator_type& a = allocator_type())
      : base_t(ordered_range, first, last, comp, a)
   {}

#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
   //! @copydoc ::boost::container::flat_set::flat_set(std::initializer_list<value_type>, const Compare& comp, const allocator_type&)
   flat_multiset(std::initializer_list<value_type> il, const Compare& comp = Compare(),
            const allocator_type& a = allocator_type())
      : base_t(false, il.begin(), il.end(), comp, a)
   {}

   //! @copydoc ::boost::container::flat_set::flat_set(std::initializer_list<value_type>, const allocator_type&)
   flat_multiset(std::initializer_list<value_type> il, const allocator_type& a)
      : base_t(false, il.begin(), il.end(), Compare(), a)
   {}

   //! Effects: Constructs an empty container using the specified comparison object and
   //! allocator, and inserts elements from the ordered unique range [il.begin(), il.end()). This function
   //! is more efficient than the normal range creation for ordered ranges.
   //!
   //! Requires: [il.begin(), il.end()) must be ordered according to the predicate.
   //!
   //! Complexity: Linear in N.
   //!
   //! Note: Non-standard extension.
   flat_multiset(ordered_range_t, std::initializer_list<value_type> il,
            const Compare& comp = Compare(), const allocator_type& a = allocator_type())
      : base_t(ordered_range, il.begin(), il.end(), comp, a)
   {}
#endif

   //! @copydoc ::boost::container::flat_set::flat_set(const flat_set &)
   flat_multiset(const flat_multiset& x)
      : base_t(static_cast<const base_t&>(x))
   {}

   //! @copydoc ::boost::container::flat_set::flat_set(flat_set &&)
   flat_multiset(BOOST_RV_REF(flat_multiset) x)
      BOOST_NOEXCEPT_IF(boost::container::container_detail::is_nothrow_move_constructible<Compare>::value)
      : base_t(boost::move(static_cast<base_t&>(x)))
   {}

   //! @copydoc ::boost::container::flat_set::flat_set(const flat_set &, const allocator_type &)
   flat_multiset(const flat_multiset& x, const allocator_type &a)
      : base_t(static_cast<const base_t&>(x), a)
   {}

   //! @copydoc ::boost::container::flat_set::flat_set(flat_set &&, const allocator_type &)
   flat_multiset(BOOST_RV_REF(flat_multiset) x, const allocator_type &a)
      : base_t(BOOST_MOVE_BASE(base_t, x), a)
   {}

   //! @copydoc ::boost::container::flat_set::operator=(const flat_set &)
   flat_multiset& operator=(BOOST_COPY_ASSIGN_REF(flat_multiset) x)
   {  return static_cast<flat_multiset&>(this->base_t::operator=(static_cast<const base_t&>(x)));  }

   //! @copydoc ::boost::container::flat_set::operator=(flat_set &&)
   flat_multiset& operator=(BOOST_RV_REF(flat_multiset) x)
      BOOST_NOEXCEPT_IF( (allocator_traits_type::propagate_on_container_move_assignment::value ||
                          allocator_traits_type::is_always_equal::value) &&
                           boost::container::container_detail::is_nothrow_move_assignable<Compare>::value)
   {  return static_cast<flat_multiset&>(this->base_t::operator=(BOOST_MOVE_BASE(base_t, x)));  }

#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
   //! @copydoc ::boost::container::flat_set::operator=(std::initializer_list<value_type>)
   flat_multiset& operator=(std::initializer_list<value_type> il)
   {
       this->clear();
       this->insert(il.begin(), il.end());
       return *this;
   }
#endif

   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)

   //! @copydoc ::boost::container::flat_set::get_allocator()
   allocator_type get_allocator() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::get_stored_allocator()
   stored_allocator_type &get_stored_allocator() BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::get_stored_allocator() const
   const stored_allocator_type &get_stored_allocator() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::begin()
   iterator begin() BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::begin() const
   const_iterator begin() const;

   //! @copydoc ::boost::container::flat_set::cbegin() const
   const_iterator cbegin() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::end()
   iterator end() BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::end() const
   const_iterator end() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::cend() const
   const_iterator cend() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::rbegin()
   reverse_iterator rbegin() BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::rbegin() const
   const_reverse_iterator rbegin() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::crbegin() const
   const_reverse_iterator crbegin() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::rend()
   reverse_iterator rend() BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::rend() const
   const_reverse_iterator rend() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::crend() const
   const_reverse_iterator crend() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::empty() const
   bool empty() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::size() const
   size_type size() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::max_size() const
   size_type max_size() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::capacity() const
   size_type capacity() const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::reserve(size_type)
   void reserve(size_type cnt);

   //! @copydoc ::boost::container::flat_set::shrink_to_fit()
   void shrink_to_fit();

   #endif   //   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)

   //////////////////////////////////////////////
   //
   //                modifiers
   //
   //////////////////////////////////////////////

   #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED)

   //! Effects: Inserts an object of type Key constructed with
   //!   std::forward<Args>(args)... and returns the iterator pointing to the
   //!   newly inserted element.
   //!
   //! Complexity: Logarithmic search time plus linear insertion
   //!   to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   template <class... Args>
   iterator emplace(BOOST_FWD_REF(Args)... args)
   {  return this->base_t::emplace_equal(boost::forward<Args>(args)...); }

   //! Effects: Inserts an object of type Key constructed with
   //!   std::forward<Args>(args)... in the container.
   //!   p is a hint pointing to where the insert should start to search.
   //!
   //! Returns: An iterator pointing to the element with key equivalent
   //!   to the key of x.
   //!
   //! Complexity: Logarithmic search time (constant if x is inserted
   //!   right before p) plus insertion linear to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   template <class... Args>
   iterator emplace_hint(const_iterator p, BOOST_FWD_REF(Args)... args)
   {  return this->base_t::emplace_hint_equal(p, boost::forward<Args>(args)...); }

   #else // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

   #define BOOST_CONTAINER_FLAT_MULTISET_EMPLACE_CODE(N) \
   BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
   iterator emplace(BOOST_MOVE_UREF##N)\
   {  return this->base_t::emplace_equal(BOOST_MOVE_FWD##N);  }\
   \
   BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \
   iterator emplace_hint(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\
   {  return this->base_t::emplace_hint_equal(hint BOOST_MOVE_I##N BOOST_MOVE_FWD##N); }\
   //
   BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_FLAT_MULTISET_EMPLACE_CODE)
   #undef BOOST_CONTAINER_FLAT_MULTISET_EMPLACE_CODE

   #endif   // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
   //! Effects: Inserts x and returns the iterator pointing to the
   //!   newly inserted element.
   //!
   //! Complexity: Logarithmic search time plus linear insertion
   //!   to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   iterator insert(const value_type &x);

   //! Effects: Inserts a new value_type move constructed from x
   //!   and returns the iterator pointing to the newly inserted element.
   //!
   //! Complexity: Logarithmic search time plus linear insertion
   //!   to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   iterator insert(value_type &&x);
   #else
   BOOST_MOVE_CONVERSION_AWARE_CATCH(insert, value_type, iterator, this->priv_insert)
   #endif

   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
   //! Effects: Inserts a copy of x in the container.
   //!   p is a hint pointing to where the insert should start to search.
   //!
   //! Returns: An iterator pointing to the element with key equivalent
   //!   to the key of x.
   //!
   //! Complexity: Logarithmic search time (constant if x is inserted
   //!   right before p) plus insertion linear to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   iterator insert(const_iterator p, const value_type &x);

   //! Effects: Inserts a new value move constructed  from x in the container.
   //!   p is a hint pointing to where the insert should start to search.
   //!
   //! Returns: An iterator pointing to the element with key equivalent
   //!   to the key of x.
   //!
   //! Complexity: Logarithmic search time (constant if x is inserted
   //!   right before p) plus insertion linear to the elements with bigger keys than x.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   iterator insert(const_iterator p, value_type &&x);
   #else
   BOOST_MOVE_CONVERSION_AWARE_CATCH_1ARG(insert, value_type, iterator, this->priv_insert, const_iterator, const_iterator)
   #endif

   //! Requires: first, last are not iterators into *this.
   //!
   //! Effects: inserts each element from the range [first,last) .
   //!
   //! Complexity: At most N log(size()+N) (N is the distance from first to last)
   //!   search time plus N*size() insertion time.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   template <class InputIterator>
   void insert(InputIterator first, InputIterator last)
      {  this->base_t::insert_equal(first, last);  }

   //! Requires: first, last are not iterators into *this and
   //! must be ordered according to the predicate.
   //!
   //! Effects: inserts each element from the range [first,last) .This function
   //! is more efficient than the normal range creation for ordered ranges.
   //!
   //! Complexity: At most N log(size()+N) (N is the distance from first to last)
   //!   search time plus N*size() insertion time.
   //!
   //! Note: Non-standard extension. If an element is inserted it might invalidate elements.
   template <class InputIterator>
   void insert(ordered_range_t, InputIterator first, InputIterator last)
      {  this->base_t::insert_equal(ordered_range, first, last);  }

#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
   //! Effects: inserts each element from the range [il.begin(), il.end()).
   //!
   //! Complexity: At most N log(size()+N) (N is the distance from first to last)
   //!   search time plus N*size() insertion time.
   //!
   //! Note: If an element is inserted it might invalidate elements.
   void insert(std::initializer_list<value_type> il)
   {  this->base_t::insert_equal(il.begin(), il.end()); }

   //! Requires: Range [il.begin(), il.end()) must be ordered according to the predicate.
   //!
   //! Effects: inserts each element from the range [il.begin(), il.end()). This function
   //! is more efficient than the normal range creation for ordered ranges.
   //!
   //! Complexity: At most N log(size()+N) (N is the distance from il.begin() to il.end())
   //!   search time plus N*size() insertion time.
   //!
   //! Note: Non-standard extension. If an element is inserted it might invalidate elements.
   void insert(ordered_range_t, std::initializer_list<value_type> il)
   {  this->base_t::insert_equal(ordered_range, il.begin(), il.end()); }
#endif

   //! @copydoc ::boost::container::flat_multimap::merge(flat_multimap<Key, T, C2, Allocator>&)
   template<class C2>
   BOOST_CONTAINER_FORCEINLINE void merge(flat_multiset<Key, C2, Allocator>& source)
   {  this->base_t::merge_equal(source.tree());   }

   //! @copydoc ::boost::container::flat_multiset::merge(flat_multiset<Key, C2, Allocator>&)
   template<class C2>
   BOOST_CONTAINER_FORCEINLINE void merge(BOOST_RV_REF_BEG flat_multiset<Key, C2, Allocator> BOOST_RV_REF_END source)
   {  return this->merge(static_cast<flat_multiset<Key, C2, Allocator>&>(source));   }

   //! @copydoc ::boost::container::flat_multimap::merge(flat_map<Key, T, C2, Allocator>&)
   template<class C2>
   BOOST_CONTAINER_FORCEINLINE void merge(flat_set<Key, C2, Allocator>& source)
   {  this->base_t::merge_equal(source.tree());   }

   //! @copydoc ::boost::container::flat_multiset::merge(flat_set<Key, C2, Allocator>&)
   template<class C2>
   BOOST_CONTAINER_FORCEINLINE void merge(BOOST_RV_REF_BEG flat_set<Key, C2, Allocator> BOOST_RV_REF_END source)
   {  return this->merge(static_cast<flat_set<Key, C2, Allocator>&>(source));   }

   #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED)

   //! @copydoc ::boost::container::flat_set::erase(const_iterator)
   iterator erase(const_iterator p);

   //! @copydoc ::boost::container::flat_set::erase(const key_type&)
   size_type erase(const key_type& x);

   //! @copydoc ::boost::container::flat_set::erase(const_iterator,const_iterator)
   iterator erase(const_iterator first, const_iterator last);

   //! @copydoc ::boost::container::flat_set::swap
   void swap(flat_multiset& x)
      BOOST_NOEXCEPT_IF(  allocator_traits_type::is_always_equal::value
                                 && boost::container::container_detail::is_nothrow_swappable<Compare>::value );

   //! @copydoc ::boost::container::flat_set::clear
   void clear() BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::key_comp
   key_compare key_comp() const;

   //! @copydoc ::boost::container::flat_set::value_comp
   value_compare value_comp() const;

   //! @copydoc ::boost::container::flat_set::find(const key_type& )
   iterator find(const key_type& x);

   //! @copydoc ::boost::container::flat_set::find(const key_type& ) const
   const_iterator find(const key_type& x) const;

   //! @copydoc ::boost::container::flat_set::nth(size_type)
   iterator nth(size_type n) BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::nth(size_type) const
   const_iterator nth(size_type n) const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::index_of(iterator)
   size_type index_of(iterator p) BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::index_of(const_iterator) const
   size_type index_of(const_iterator p) const BOOST_NOEXCEPT_OR_NOTHROW;

   //! @copydoc ::boost::container::flat_set::count(const key_type& ) const
   size_type count(const key_type& x) const;

   //! @copydoc ::boost::container::flat_set::lower_bound(const key_type& )
   iterator lower_bound(const key_type& x);

   //! @copydoc ::boost::container::flat_set::lower_bound(const key_type& ) const
   const_iterator lower_bound(const key_type& x) const;

   //! @copydoc ::boost::container::flat_set::upper_bound(const key_type& )
   iterator upper_bound(const key_type& x);

   //! @copydoc ::boost::container::flat_set::upper_bound(const key_type& ) const
   const_iterator upper_bound(const key_type& x) const;

   //! @copydoc ::boost::container::flat_set::equal_range(const key_type& ) const
   std::pair<const_iterator, const_iterator> equal_range(const key_type& x) const;

   //! @copydoc ::boost::container::flat_set::equal_range(const key_type& )
   std::pair<iterator,iterator> equal_range(const key_type& x);

   //! Effects: Returns true if x and y are equal
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator==(const flat_multiset& x, const flat_multiset& y);

   //! Effects: Returns true if x and y are unequal
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator!=(const flat_multiset& x, const flat_multiset& y);

   //! Effects: Returns true if x is less than y
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator<(const flat_multiset& x, const flat_multiset& y);

   //! Effects: Returns true if x is greater than y
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator>(const flat_multiset& x, const flat_multiset& y);

   //! Effects: Returns true if x is equal or less than y
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator<=(const flat_multiset& x, const flat_multiset& y);

   //! Effects: Returns true if x is equal or greater than y
   //!
   //! Complexity: Linear to the number of elements in the container.
   friend bool operator>=(const flat_multiset& x, const flat_multiset& y);

   //! Effects: x.swap(y)
   //!
   //! Complexity: Constant.
   friend void swap(flat_multiset& x, flat_multiset& y);

   #endif   //#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED

   #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
   private:
   template <class KeyType>
   iterator priv_insert(BOOST_FWD_REF(KeyType) x)
   {  return this->base_t::insert_equal(::boost::forward<KeyType>(x));  }

   template <class KeyType>
   iterator priv_insert(const_iterator p, BOOST_FWD_REF(KeyType) x)
   {  return this->base_t::insert_equal(p, ::boost::forward<KeyType>(x)); }
   #endif   //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
};

#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED

}  //namespace container {

//!has_trivial_destructor_after_move<> == true_type
//!specialization for optimizations
template <class Key, class Compare, class Allocator>
struct has_trivial_destructor_after_move<boost::container::flat_multiset<Key, Compare, Allocator> >
{
   typedef typename ::boost::container::allocator_traits<Allocator>::pointer pointer;
   static const bool value = ::boost::has_trivial_destructor_after_move<Allocator>::value &&
                             ::boost::has_trivial_destructor_after_move<pointer>::value &&
                             ::boost::has_trivial_destructor_after_move<Compare>::value;
};

namespace container {

#endif   //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED

}}

#include <boost/container/detail/config_end.hpp>

#endif   // BOOST_CONTAINER_FLAT_SET_HPP

ext: hpp

max_stars_repo_name: rudylee/expo

max_issues_repo_name: rudylee/expo

max_forks_repo_name: rudylee/expo


text:
//  (C) Copyright Edward Diener 2011-2015
//  Use, modification and distribution are subject to the Boost Software License,
//  Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt).

#include <boost/vmd/tuple/is_vmd_tuple.hpp>
#include <boost/detail/lightweight_test.hpp>

int main()
  {

#if BOOST_PP_VARIADICS

  #define A_TUPLE (*,#,zz)
  #define AN_EMPTY_VMD_TUPLE

#if !BOOST_VMD_MSVC_V8

  #define AN_EMPTY_TUPLE ()
  #define AN_EMPTY_TUPLE_PLUS () 83

#endif

  #define A_TUPLE2 (*,#,(zz,44,(e7)))
  #define A_TUPLE_PLUS (mmf,34,^^,!) 456
  #define PLUS_ATUPLE yyt (j,ii%)
  #define JDATA ggh
  #define KDATA (ggh)
  #define NOT_TUPLE y6()
  #define NOT_TUPLE2 &(kkkgg,(e))
  #define A_SEQ (r)(%)(#)
  #define AN_ARRAY (4,(5,7,f,x))
  #define A_LIST (e,(g,(&,BOOST_PP_NIL)))

  BOOST_TEST(BOOST_VMD_IS_VMD_TUPLE());
  BOOST_TEST(BOOST_VMD_IS_VMD_TUPLE(AN_EMPTY_VMD_TUPLE));
  BOOST_TEST(BOOST_VMD_IS_VMD_TUPLE(A_TUPLE));
  BOOST_TEST(BOOST_VMD_IS_VMD_TUPLE(A_TUPLE2));
  BOOST_TEST(!BOOST_VMD_IS_VMD_TUPLE(A_TUPLE_PLUS));
  BOOST_TEST(!BOOST_VMD_IS_VMD_TUPLE(PLUS_ATUPLE));
  BOOST_TEST(!BOOST_VMD_IS_VMD_TUPLE(JDATA));
  BOOST_TEST(BOOST_VMD_IS_VMD_TUPLE(KDATA));
  BOOST_TEST(!BOOST_VMD_IS_VMD_TUPLE(NOT_TUPLE));
  BOOST_TEST(!BOOST_VMD_IS_VMD_TUPLE(NOT_TUPLE2));
  BOOST_TEST(!BOOST_VMD_IS_VMD_TUPLE(A_SEQ));
  BOOST_TEST(BOOST_VMD_IS_VMD_TUPLE(AN_ARRAY));
  BOOST_TEST(BOOST_VMD_IS_VMD_TUPLE(A_LIST));

#if !BOOST_VMD_MSVC_V8

  BOOST_TEST(BOOST_VMD_IS_VMD_TUPLE(AN_EMPTY_TUPLE));
  BOOST_TEST(!BOOST_VMD_IS_VMD_TUPLE(AN_EMPTY_TUPLE_PLUS));

#endif

  BOOST_TEST(!BOOST_VMD_IS_VMD_TUPLE((y)2(x)));

#else

BOOST_ERROR("No variadic macro support");

#endif

  return boost::report_errors();

  }

ext: cxx


text:
#include "CADSoftwareEnvirUtils.h"
#include <cc_CommonUtilities.h>
#include <windows.h>
#include "cc_WindowsFunctions.h"
//#include <malloc.h>
#include <iostream>
//#include <vector>
//#include <tchar.h>
#include <algorithm>
#include <sstream>
#include <boost/filesystem.hpp>

// #define BUFFER 8192

namespace isis
{
	const int MAX_KEY_LENGTH =	256;
	//const int MAX_VALUE_NAME  = 16383;  // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms724872(v=vs.85).aspx
	const int MAX_VALUE_NAME  = 2048;   // Note - Set to 2048 because the values we are searching for in
										// HKEY_LOCAL_MACHINE\SOFTWARE\PTC\Creo Parametric\1.0\2011190
										// are typically just MAX_PATH.  Allocated the larger size for safety.

	// Return empty string if registry value was not found.
	// WARNING - Do not use this function for values larger that 2048.
	////////////////////////////////////////////////////////////////////////////////////////////////
	std::string RetrieveRegistryStringValue( const HKEY in_hkey, const std::string &in_Key )
	{
		std::string tempString = "";

		DWORD	pdwType;
		char	pvData[MAX_VALUE_NAME];
		DWORD	pcbData = MAX_VALUE_NAME;

		LSTATUS errorStatus =
			RegGetValueA(	in_hkey,		// A handle to an open registry key
							NULL,			// subkey of the key specified by the hkey parameter.
							in_Key.c_str(), // The name of the registry value.
							RRF_RT_REG_SZ,  // Flags that restrict the data type of value to be queried.  RRF_RT_REG_SZ only strings.
							&pdwType,		// A pointer to a variable that receives a code indicating the type of data stored
							pvData,			// A pointer to a buffer that receives the value's data.
							&pcbData  );		// A pointer to a variable that specifies the size of the buffer pointed to by the pvData

		if ( errorStatus == ERROR_SUCCESS ) tempString = (char *) pvData;

		return tempString;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////
	// out_SubKeys will be empty of no sub-keys were found
	void RetrieveRegistryListOfSubkeys( const HKEY in_hkey, std::vector<std::string> &out_SubKeys )
	{
		// The following code is from http://msdn.microsoft.com/en-us/library/ms724256(VS.85).aspx

		CHAR    achKey[MAX_KEY_LENGTH];   // buffer for subkey name
		DWORD    cbName;                   // size of name string
		CHAR     achClass[MAX_PATH] = "";  // buffer for class name
		DWORD    cchClassName = MAX_PATH;  // size of class string
		DWORD    cSubKeys=0;               // number of subkeys
		DWORD    cbMaxSubKey;              // longest subkey size
		DWORD    cchMaxClass;              // longest class string
		DWORD    cValues;                  // number of values for key
		DWORD    cchMaxValue;              // longest value name
		DWORD    cbMaxValueData;           // longest value data
		DWORD    cbSecurityDescriptor;     // size of security descriptor
		FILETIME ftLastWriteTime;          // last write time

		DWORD i, retCode;

		//WCHAR  achValue[MAX_VALUE_NAME];
		//DWORD cchValue = MAX_VALUE_NAME;

		// Get the class name and the value count.
		retCode = RegQueryInfoKeyA(
			in_hkey,                    // key handle
			achClass,                // buffer for class name
			&cchClassName,           // size of class string
			NULL,                    // reserved
			&cSubKeys,               // number of subkeys
			&cbMaxSubKey,            // longest subkey size
			&cchMaxClass,            // longest class string
			&cValues,                // number of values for this key
			&cchMaxValue,            // longest value name
			&cbMaxValueData,         // longest value data
			&cbSecurityDescriptor,   // security descriptor
			&ftLastWriteTime);       // last write time

		// Enumerate the subkeys, until RegEnumKeyEx fails.

		if (cSubKeys)
		{
			//printf( "\nNumber of subkeys: %d\n", cSubKeys);

			for (i=0; i<cSubKeys; i++)
			{
				cbName = MAX_KEY_LENGTH;
				retCode = RegEnumKeyExA(in_hkey, i,
						 achKey,
						 &cbName,
						 NULL,
						 NULL,
						 NULL,
						 &ftLastWriteTime);
				if (retCode == ERROR_SUCCESS)
				{
					//wprintf(TEXT("(%d) %s\n"), i+1, achKey);
					out_SubKeys.push_back(achKey);
				}
			}
		}

	}

	////////////////////////////////////////////////////////////////////////////////////////////////
	//  Registry Hierarchy
	//	HKEY_LOCAL_MACHINE
	//		SOFTWARE
	//			PTC
	//				Creo Parametric
	//					1.0    // Note - this could be 1.1, 1.2 should pick the highest 1.x number, Note 2.x not supported yet
	//						2011190  // Year build number,  this could be many numbers, should pick the highest number
	//							InstallDir // e.g. C:\Program Files\PTC\Creo 1.0\Parametric
	//							CommonFilesLocation // e.g. C:\Program Files\PTC\Creo 1.0\Common Files\F000
	//
	//	in_StartingKeyPath - Creo 1.0 = "SOFTWARE\\PTC\\Creo Parametric";
	//  in_StartingKeyPath - Creo 2.0 = "SOFTWARE\\Wow6432Node\\PTC\\Creo Parametric";
	//	in_SupportedVersionPrefixes -			1.  2.  // This means 1.x, 2.x, but only enter 1., 2.
	//  in_SupportedVersionString_ForErrorMsg - 1.x, 2.x, or 3.x  // currently only support 1.x
	//	out_CreoParametricInstallPath -			e.g. C:\Program Files\PTC\Creo 3.0\M060\Parametric\
	//	out_CreoParametricCommMsgExe -			This should be long name.   e.g. C:\Program Files\PTC\Creo 1.0\Common Files\F000\x86e_win64\obj\pro_comm_msg

	void RetrieveFromRegistryCreoInstallLocations(
								const std::string &in_StartingKeyPath,
								const std::vector<std::string> &in_SupportedVersionPrefixes,
								const std::string			   &in_SupportedVersionString_ForErrorMsg,
								std::string &out_CreoParametricInstallPath,
								std::string &out_CreoParametricCommMsgExe ) throw (isis::application_exception)
	{
		std::string key_Creo_Parametric = in_StartingKeyPath;

		std::string errorMsg_1 =  "Creo Parametric is not installed.  Please install Creo " + in_SupportedVersionString_ForErrorMsg + ". Note - Higher versions are currently not supported.  ";
		std::string errorMsg_2 =  "Creo Parametric is not installed or the registry information for the install is incorrect or the format of the registry information has changed.  If not installed, please install Creo " + in_SupportedVersionString_ForErrorMsg + ". Note - Higher versions are currently not supported.  ";

		std::string key_Temp;
		//////////////////////////////////////////////////////////////////////
		// Retrieve Key to in_StartingKeyPath (e.g. HKEY_LOCAL_MACHINE\SOFTWARE\PTC\Creo Parametric)
		/////////////////////////////////////////////////////////////////////
		HKEY hKey;
		if ( RegOpenKeyExA(HKEY_LOCAL_MACHINE,
			 key_Creo_Parametric.c_str(),
			 0, KEY_READ|KEY_WOW64_64KEY, &hKey) != ERROR_SUCCESS)
		{
			std::string TempError =   errorMsg_1 +
				std::string("Could not find registry key HKEY_LOCAL_MACHINE\\") + key_Creo_Parametric;
			throw isis::application_exception(TempError.c_str());
		}
		///////////////////////////////////////////////////////////////////
		// Get Subkeys of in_StartingKeyPath
		//////////////////////////////////////////////////////////////////
		std::vector<std::string> subKeys_vec;
		RetrieveRegistryListOfSubkeys( hKey, subKeys_vec );
		RegCloseKey(hKey);

		std::vector<std::string> versionNumber_vec;
		for ( std::vector<std::string>::const_iterator i(subKeys_vec.begin()); i != subKeys_vec.end(); ++i )
		{
			//std::cout << std::endl << *i;
			for ( std::vector<std::string>::const_iterator j(in_SupportedVersionPrefixes.begin()); j != in_SupportedVersionPrefixes.end(); ++ j )
			{
				if ( i->find(*j) != i->front())
				{
					versionNumber_vec.push_back(*i);
					break;
				}
			}
		}

		if ( versionNumber_vec.size() == 0 )
		{
			std::string TempError =   errorMsg_2 +
				std::string("Could not find registry keys subordinate to HKEY_LOCAL_MACHINE\\") + key_Creo_Parametric;
			throw isis::application_exception(TempError.c_str());
		}

		// Sort the keys, so that the highest key (highest Creo Version) could be selected.
		std::sort( versionNumber_vec.begin(), versionNumber_vec.end() );

		key_Temp = key_Creo_Parametric + "\\" + versionNumber_vec[versionNumber_vec.size() - 1 ];

		//std::cout << std::endl << key_Temp;

		////////////////////////////////////////////////////////////////////////////////
		// Retrieve next Key (e.g.in_StartingKeyPath\1.0 )
		////////////////////////////////////////////////////////////////////////////////
		if ( RegOpenKeyExA(HKEY_LOCAL_MACHINE,
			 key_Temp.c_str(),
			 0, KEY_READ|KEY_WOW64_64KEY, &hKey) != ERROR_SUCCESS)
		{
			std::string TempError =   errorMsg_2 +
				std::string("Could not find key HKEY_LOCAL_MACHINE\\") + key_Temp;
			throw isis::application_exception(TempError.c_str());
		}

		/////////////////////////////////////////////////////////////////////////////////////////////
		// Get Subkeys of in_StartingKeyPath\1.0   could be 1.1, 2.0 ...
		/////////////////////////////////////////////////////////////////////////////////////////////
		if (versionNumber_vec[versionNumber_vec.size() - 1 ].find("5.") == 0) {
			RegCloseKey(hKey);
		} else {
			subKeys_vec.clear();
			RetrieveRegistryListOfSubkeys( hKey, subKeys_vec );
			RegCloseKey(hKey);

			if ( subKeys_vec.size() == 0 )
			{
				std::string TempError =  errorMsg_2 +
					std::string("Could not find registry keys subordinate to HKEY_LOCAL_MACHINE\\") + key_Temp;
				throw isis::application_exception(TempError.c_str());
			}

			//for ( std::vector<std::string>::const_iterator i(subKeys_vec.begin()); i != subKeys_vec.end(); ++i )
			//{
			//	std::cout << std::endl << *i;
			//}

			// Sort the keys, so that the highest key (highest Creo Version) could be selected.
			std::sort( subKeys_vec.begin(), subKeys_vec.end() );

			key_Temp = key_Temp + "\\" + subKeys_vec[subKeys_vec.size() - 1 ];
		}
		//std::cout << std::endl << key_Temp;

		////////////////////////////////////////////////////////////////////////////////
		// Retrieve next Key (e.g. in_StartingKeyPath\2.0\2011109 )
		////////////////////////////////////////////////////////////////////////////////
		if ( RegOpenKeyExA(HKEY_LOCAL_MACHINE,
			 key_Temp.c_str(),
			 0, KEY_READ|KEY_WOW64_64KEY, &hKey) != ERROR_SUCCESS)
		{
			std::string TempError =   errorMsg_2 +
				std::string("Could not find key HKEY_LOCAL_MACHINE\\") + key_Temp;
			throw isis::application_exception(TempError.c_str());
		}

		////////////////////////////////
		// Get CommonFilesLocation
		////////////////////////////////
		std::string commonFilesLocation = RetrieveRegistryStringValue( hKey, "CommonFilesLocation" );

		if ( commonFilesLocation.size() == 0 )
		{
			std::string TempError =   errorMsg_2 +
				std::string("Could not find registry value \"CommonFilesLocation\" subordinate to HKEY_LOCAL_MACHINE\\") + key_Temp;
			throw isis::application_exception(TempError.c_str());
		}

		//std::cout << std::endl << "CommonFilesLocation: " <<  commonFilesLocation;

		////////////////////////////////
		// Get InstallDir
		////////////////////////////////
		std::string installDir = RetrieveRegistryStringValue( hKey, "InstallDir" );
		RegCloseKey(hKey);

		if ( commonFilesLocation.size() == 0 )
		{
			std::string TempError =   errorMsg_2 +
				std::string("Could not find registry value \"InstallDir\" subordinate to HKEY_LOCAL_MACHINE\\") + key_Temp;
			throw isis::application_exception(TempError.c_str());
		}

		//std::cout << std::endl << "InstallDir: " <<  installDir;

		out_CreoParametricInstallPath = installDir + "\\";

		out_CreoParametricCommMsgExe =  commonFilesLocation + "\\x86e_win64\\obj\\pro_comm_msg";

		//std::cout << std::endl << "out_CreoParametricInstallPath: " <<  out_CreoParametricInstallPath;
		//std::cout << std::endl << "out_CreoParametricCommMsgExe: " <<  out_CreoParametricCommMsgExe;
	}
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//
	//	Example return values
	//	if ( C:\PROGRA~1\PTC\CREO2~1.0\PARAME~1\bin\parametric.psf exists)
	//		C:\PROGRA~1\PTC\CREO2~1.0\PARAME~1\bin\parametric.exe -g:no_graphics -i:rpc_input C:\PROGRA~1\PTC\CREO2~1.0\PARAME~1\bin\parametric.psf %*
	//	else
	//		C:\PROGRA~1\PTC\CREO2~1.0\PARAME~1\bin\parametric.exe -g:no_graphics -i:rpc_input
	std::string BuildCreoStartCommand( bool					in_GraphicsModeOn,
									   bool					in_CreoExceptInputFromThisProgramAndCreoUI,
									   const std::string	&in_CreoParametricInstallPath )
	{
		std::string creoStartCommand				 =  "bin\\parametric.exe";
		std::string creoStartCommandSuffix			 = "";
		std::string creoStartCommand_psFile_FileName =  "bin\\parametric.psf";
		std::string creoStartCommandSuffix_psfFile	 =  " %*";

		if ( !in_GraphicsModeOn ) creoStartCommandSuffix += " -g:no_graphics";
		if ( !in_CreoExceptInputFromThisProgramAndCreoUI ) creoStartCommandSuffix += " -i:rpc_input";

		std::string creoStartCommand_psfFile_PathAndFileName = in_CreoParametricInstallPath + creoStartCommand_psFile_FileName ;

		// Check if \PROGRA~1\PTC\CREO2~1.0\PARAME~1\bin\parametric.psf exists
		if (::boost::filesystem::exists(creoStartCommand_psfFile_PathAndFileName) )
			//  C:\PROGRA~1\PTC\CREO2~1.0\PARAME~1\bin\parametric.exe -g:no_graphics -i:rpc_input C:\PROGRA~1\PTC\CREO2~1.0\PARAME~1\bin\parametric.psf %*
			return  "\"" + in_CreoParametricInstallPath +
			creoStartCommand + "\" " + creoStartCommandSuffix +
					" \"" + creoStartCommand_psfFile_PathAndFileName + "\" " +
					creoStartCommandSuffix_psfFile;
		else
			//  C:\PROGRA~1\PTC\CREO2~1.0\PARAME~1\bin\parametric.exe -g:no_graphics -i:rpc_input
			return in_CreoParametricInstallPath + creoStartCommand + " " + creoStartCommandSuffix;
	}
	//////////////////////////////////////////////////////////////////////////////////////////////////////////
	void SetupCreoEnvironmentVariables(
								bool				in_GraphicsModeOn,
								bool				in_CreoExceptInputFromThisProgramAndCreoUI,
								std::string &out_CreoStartCommand ) throw (isis::application_exception)
	{
		//std::cout << std::endl << "************* IN SetupCreoEnvironmentVariables";

		bool readEnvVariablesFromRegistry = true;

		char *EnvVariable_USE_ENVIR_VARS;
		EnvVariable_USE_ENVIR_VARS = getenv ("CREO_PARAMETRIC_USE_ENVIR_VARS");

		if ( EnvVariable_USE_ENVIR_VARS != NULL )
		{
			std::string tempString_1 = EnvVariable_USE_ENVIR_VARS;
			std::string tempString_2 = isis::ConvertToUpperCase(tempString_1);
			std::remove( tempString_2.begin(), tempString_2.end(), ' ');
			if ( tempString_2 == "TRUE" ) readEnvVariablesFromRegistry = false;
		}

		if ( readEnvVariablesFromRegistry )
		{
			std::string creoParametricInstallPath;
			std::string creoParametricCommMsgExe;

			try
			{
				std::string in_StartingKeyPath = "SOFTWARE\\Wow6432Node\\PTC\\PTC Creo Parametric";
				std::vector<std::string>	supportedVersionPrefixes;
				std::string					supportedVersionString_ForErrorMsg = "3, 4 or 5";  // e.g. 1.x, 2.x, or 3.x
				supportedVersionPrefixes.push_back("3.");
				supportedVersionPrefixes.push_back("4.");
				supportedVersionPrefixes.push_back("5.");
				isis::RetrieveFromRegistryCreoInstallLocations( in_StartingKeyPath,
																supportedVersionPrefixes,
																supportedVersionString_ForErrorMsg,
																creoParametricInstallPath,
																creoParametricCommMsgExe );
			}

			catch ( isis::application_exception& ex_Creo_3 )
			{
				std::stringstream errorString;
				errorString <<
						"Could not find an installation of Creo 3, 4 or 5.  Creo 2.0 and earlier are no longer supported, "
						<< std::endl << ex_Creo_3.what();
						throw isis::application_exception(errorString.str());
			}

			// 4/26/2016, We are now compiling with Creo 3, which means this program will only
			// work with Creo 3.0 or higher (e.g. Creo 4.0 when it is released)
			// Leave the following code commented out so that Creo 4.0 can easily be supported when available.

			/**
			catch ( isis::application_exception& ex_Creo_3 )
			{
				try // Creo 2.0
				{
				std::string in_StartingKeyPath = "SOFTWARE\\Wow6432Node\\PTC\\Creo Parametric";
				std::vector<std::string>	supportedVersionPrefixes;
				std::string					supportedVersionString_ForErrorMsg = "2.x";  // e.g. 1.x, 2.x, or 3.x
				supportedVersionPrefixes.push_back("2.");  //
				isis::RetrieveFromRegistryCreoInstallLocations( in_StartingKeyPath,
																supportedVersionPrefixes,
																supportedVersionString_ForErrorMsg,
																creoParametricInstallPath,
																creoParametricCommMsgExe );
				}
				catch ( isis::application_exception& ex_Creo_2 )
				{
					std::stringstream errorString;
					errorString <<
							"Could not find an installation of Creo 3.0 or Creo 2.0, "
							<< std::endl << ex_Creo_3.what()
							<< std::endl << ex_Creo_2.what();
							throw isis::application_exception(errorString.str());
				}
			}
			****/
			_putenv_s( "PRO_COMM_MSG_EXE", creoParametricCommMsgExe.c_str() );
			char *EnvVariableCOMM_MSG_EXE  = getenv ("PRO_COMM_MSG_EXE");
			//std::cout << std::endl << "************* PRO_COMM_MSG_EXE: " << creoParametricCommMsgExe;
			//std::cout << std::endl << "************* getenv (\"PRO_COMM_MSG_EXE\"): " << EnvVariableCOMM_MSG_EXE;

			out_CreoStartCommand = 	BuildCreoStartCommand(	in_GraphicsModeOn,
															in_CreoExceptInputFromThisProgramAndCreoUI,
															creoParametricInstallPath );
		}
		else  // For this case, the system environment variables must be set.
		{
			//std::cout << std::endl << "************* IN Make sure environment variables";

			// Make sure environment variables are set
			// (i.e. CREO_PARAMETRIC_INSTALL_PATH, CREO_PARAMETRIC_COMM_MSG_EXE	)
			char *EnvVariable_INSTALL_PATH, *EnvVariableCOMM_MSG_EXE;
			EnvVariable_INSTALL_PATH = getenv ("CREO_PARAMETRIC_INSTALL_PATH");
			EnvVariableCOMM_MSG_EXE  = getenv ("CREO_PARAMETRIC_COMM_MSG_EXE");
			if (EnvVariable_INSTALL_PATH == NULL || EnvVariableCOMM_MSG_EXE == NULL )
			{
				std::string TempError = "Environment variables CREO_PARAMETRIC_INSTALL_PATH and/or CREO_PARAMETRIC_COMM_MSG_EXE are not set properly.  Please see \"...\\0Readme - CreateAssembly.txt\" for instructions on how to properly configure your system." +
					std::string("This file is typically located at \"C:\\Program Files (x86)\\META\\bin\\CAD\\Creo\\bin\".");
				throw isis::application_exception(TempError.c_str());
			}
			_putenv_s( "PRO_COMM_MSG_EXE",  EnvVariableCOMM_MSG_EXE );

			std::string CreoParametricInstallPath = EnvVariable_INSTALL_PATH;
			if (CreoParametricInstallPath.back() != '\\') {
				CreoParametricInstallPath += "\\";
			}

			//out_CreoStartCommand = EnvVariable_INSTALL_PATH + creoStartCommandSuffix;
			out_CreoStartCommand = 	BuildCreoStartCommand(	in_GraphicsModeOn,
															in_CreoExceptInputFromThisProgramAndCreoUI,
															CreoParametricInstallPath);

		}

		//std::cout << std::endl << "SetupCreoEnvironmentVariables: out_CreoStartCommand:    " << out_CreoStartCommand);
		//std::cout << std::endl << "SetupCreoEnvironmentVariables: PRO_COMM_MSG_EXE:     "    << getenv ("PRO_COMM_MSG_EXE");
		//std::cout << std::endl << "SetupCreoEnvironmentVariables: out_CreoStartCommand: "    << out_CreoStartCommand;

	}  // End SetupCreoEnvironmentVariables()

} // end namespace isis

ext: cpp

max_stars_repo_name: lefevre-fraser/openmeta-mms

max_issues_repo_name: lefevre-fraser/openmeta-mms

max_forks_repo_name: lefevre-fraser/openmeta-mms


text:
/*************************************************************************
 * odil - Copyright (C) Universite de Strasbourg
 * Distributed under the terms of the CeCILL-B license, as published by
 * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
 * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
 * for details.
 ************************************************************************/

#include "odil/logging.h"

#include <boost/log/expressions.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/trivial.hpp>

#include "odil/odil.h"

namespace odil
{

namespace logging
{

bool configure()
{
    boost::log::core::get()->set_filter(
        boost::log::trivial::severity >= boost::log::trivial::warning);

    return true;

}

boost::log::sources::severity_logger<int> logger = {};

static bool const configured = configure();

}

}

max_stars_repo_name: genisysram/odil

max_issues_repo_name: genisysram/odil

max_forks_repo_name: genisysram/odil