diff --git a/include/bbp/sonata/common.h b/include/bbp/sonata/common.h index af1be923..61ce7b11 100644 --- a/include/bbp/sonata/common.h +++ b/include/bbp/sonata/common.h @@ -38,6 +38,7 @@ SONATA_API const std::string version(); using NodeID = uint64_t; using EdgeID = uint64_t; +using ElementID = uint32_t; class SONATA_API SonataError: public std::runtime_error { diff --git a/include/bbp/sonata/optional.hpp b/include/bbp/sonata/optional.hpp new file mode 100644 index 00000000..133673a9 --- /dev/null +++ b/include/bbp/sonata/optional.hpp @@ -0,0 +1,1715 @@ +// +// Copyright (c) 2014-2018 Martin Moene +// +// https://github.com/martinmoene/optional-lite +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#ifndef NONSTD_OPTIONAL_LITE_HPP +#define NONSTD_OPTIONAL_LITE_HPP + +#define optional_lite_MAJOR 3 +#define optional_lite_MINOR 2 +#define optional_lite_PATCH 0 + +#define optional_lite_VERSION optional_STRINGIFY(optional_lite_MAJOR) "." optional_STRINGIFY(optional_lite_MINOR) "." optional_STRINGIFY(optional_lite_PATCH) + +#define optional_STRINGIFY( x ) optional_STRINGIFY_( x ) +#define optional_STRINGIFY_( x ) #x + +// optional-lite configuration: + +#define optional_OPTIONAL_DEFAULT 0 +#define optional_OPTIONAL_NONSTD 1 +#define optional_OPTIONAL_STD 2 + +#if !defined( optional_CONFIG_SELECT_OPTIONAL ) +# define optional_CONFIG_SELECT_OPTIONAL ( optional_HAVE_STD_OPTIONAL ? optional_OPTIONAL_STD : optional_OPTIONAL_NONSTD ) +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef optional_CONFIG_NO_EXCEPTIONS +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) +# define optional_CONFIG_NO_EXCEPTIONS 0 +# else +# define optional_CONFIG_NO_EXCEPTIONS 1 +# endif +#endif + +// C++ language version detection (C++20 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef optional_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define optional_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define optional_CPLUSPLUS __cplusplus +# endif +#endif + +#define optional_CPP98_OR_GREATER ( optional_CPLUSPLUS >= 199711L ) +#define optional_CPP11_OR_GREATER ( optional_CPLUSPLUS >= 201103L ) +#define optional_CPP11_OR_GREATER_ ( optional_CPLUSPLUS >= 201103L ) +#define optional_CPP14_OR_GREATER ( optional_CPLUSPLUS >= 201402L ) +#define optional_CPP17_OR_GREATER ( optional_CPLUSPLUS >= 201703L ) +#define optional_CPP20_OR_GREATER ( optional_CPLUSPLUS >= 202000L ) + +// C++ language version (represent 98 as 3): + +#define optional_CPLUSPLUS_V ( optional_CPLUSPLUS / 100 - (optional_CPLUSPLUS > 200000 ? 2000 : 1994) ) + +// Use C++17 std::optional if available and requested: + +#if optional_CPP17_OR_GREATER && defined(__has_include ) +# if __has_include( ) +# define optional_HAVE_STD_OPTIONAL 1 +# else +# define optional_HAVE_STD_OPTIONAL 0 +# endif +#else +# define optional_HAVE_STD_OPTIONAL 0 +#endif + +#define optional_USES_STD_OPTIONAL ( (optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_STD) || ((optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_DEFAULT) && optional_HAVE_STD_OPTIONAL) ) + +// +// in_place: code duplicated in any-lite, expected-lite, optional-lite, value-ptr-lite, variant-lite: +// + +#ifndef nonstd_lite_HAVE_IN_PLACE_TYPES +#define nonstd_lite_HAVE_IN_PLACE_TYPES 1 + +// C++17 std::in_place in : + +#if optional_CPP17_OR_GREATER + +#include + +namespace nonstd { + +using std::in_place; +using std::in_place_type; +using std::in_place_index; +using std::in_place_t; +using std::in_place_type_t; +using std::in_place_index_t; + +#define nonstd_lite_in_place_t( T) std::in_place_t +#define nonstd_lite_in_place_type_t( T) std::in_place_type_t +#define nonstd_lite_in_place_index_t(K) std::in_place_index_t + +#define nonstd_lite_in_place( T) std::in_place_t{} +#define nonstd_lite_in_place_type( T) std::in_place_type_t{} +#define nonstd_lite_in_place_index(K) std::in_place_index_t{} + +} // namespace nonstd + +#else // optional_CPP17_OR_GREATER + +#include + +namespace nonstd { +namespace detail { + +template< class T > +struct in_place_type_tag {}; + +template< std::size_t K > +struct in_place_index_tag {}; + +} // namespace detail + +struct in_place_t {}; + +template< class T > +inline in_place_t in_place( detail::in_place_type_tag /*unused*/ = detail::in_place_type_tag() ) +{ + return in_place_t(); +} + +template< std::size_t K > +inline in_place_t in_place( detail::in_place_index_tag /*unused*/ = detail::in_place_index_tag() ) +{ + return in_place_t(); +} + +template< class T > +inline in_place_t in_place_type( detail::in_place_type_tag /*unused*/ = detail::in_place_type_tag() ) +{ + return in_place_t(); +} + +template< std::size_t K > +inline in_place_t in_place_index( detail::in_place_index_tag /*unused*/ = detail::in_place_index_tag() ) +{ + return in_place_t(); +} + +// mimic templated typedef: + +#define nonstd_lite_in_place_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag ) +#define nonstd_lite_in_place_type_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag ) +#define nonstd_lite_in_place_index_t(K) nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag ) + +#define nonstd_lite_in_place( T) nonstd::in_place_type +#define nonstd_lite_in_place_type( T) nonstd::in_place_type +#define nonstd_lite_in_place_index(K) nonstd::in_place_index + +} // namespace nonstd + +#endif // optional_CPP17_OR_GREATER +#endif // nonstd_lite_HAVE_IN_PLACE_TYPES + +// +// Using std::optional: +// + +#if optional_USES_STD_OPTIONAL + +#include + +namespace nonstd { + + using std::optional; + using std::bad_optional_access; + using std::hash; + + using std::nullopt; + using std::nullopt_t; + + using std::operator==; + using std::operator!=; + using std::operator<; + using std::operator<=; + using std::operator>; + using std::operator>=; + using std::make_optional; + using std::swap; +} + +#else // optional_USES_STD_OPTIONAL + +#include +#include + +// optional-lite alignment configuration: + +#ifndef optional_CONFIG_MAX_ALIGN_HACK +# define optional_CONFIG_MAX_ALIGN_HACK 0 +#endif + +#ifndef optional_CONFIG_ALIGN_AS +// no default, used in #if defined() +#endif + +#ifndef optional_CONFIG_ALIGN_AS_FALLBACK +# define optional_CONFIG_ALIGN_AS_FALLBACK double +#endif + +// Compiler warning suppression: + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wundef" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wundef" +#elif defined(_MSC_VER ) +# pragma warning( push ) +#endif + +// half-open range [lo..hi): +#define optional_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 optional_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 optional_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 optional_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 optional_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 optional_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 optional_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 optional_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 optional_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 optional_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 optional_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) +// MSVC++ 14.2 _MSC_VER >= 1920 optional_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) + +#if defined(_MSC_VER ) && !defined(__clang__) +# define optional_COMPILER_MSVC_VER (_MSC_VER ) +# define optional_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) +#else +# define optional_COMPILER_MSVC_VER 0 +# define optional_COMPILER_MSVC_VERSION 0 +#endif + +#define optional_COMPILER_VERSION( major, minor, patch ) ( 10 * (10 * (major) + (minor) ) + (patch) ) + +#if defined(__GNUC__) && !defined(__clang__) +# define optional_COMPILER_GNUC_VERSION optional_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +# define optional_COMPILER_GNUC_VERSION 0 +#endif + +#if defined(__clang__) +# define optional_COMPILER_CLANG_VERSION optional_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#else +# define optional_COMPILER_CLANG_VERSION 0 +#endif + +#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 140 ) +# pragma warning( disable: 4345 ) // initialization behavior changed +#endif + +#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 150 ) +# pragma warning( disable: 4814 ) // in C++14 'constexpr' will not imply 'const' +#endif + +// Presence of language and library features: + +#define optional_HAVE(FEATURE) ( optional_HAVE_##FEATURE ) + +#ifdef _HAS_CPP0X +# define optional_HAS_CPP0X _HAS_CPP0X +#else +# define optional_HAS_CPP0X 0 +#endif + +// Unless defined otherwise below, consider VC14 as C++11 for optional-lite: + +#if optional_COMPILER_MSVC_VER >= 1900 +# undef optional_CPP11_OR_GREATER +# define optional_CPP11_OR_GREATER 1 +#endif + +#define optional_CPP11_90 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1500) +#define optional_CPP11_100 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1600) +#define optional_CPP11_110 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1700) +#define optional_CPP11_120 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1800) +#define optional_CPP11_140 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1900) +#define optional_CPP11_141 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1910) + +#define optional_CPP11_140_490 ((optional_CPP11_OR_GREATER_ && optional_COMPILER_GNUC_VERSION >= 490) || (optional_COMPILER_MSVC_VER >= 1910)) + +#define optional_CPP14_000 (optional_CPP14_OR_GREATER) +#define optional_CPP17_000 (optional_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define optional_HAVE_CONSTEXPR_11 optional_CPP11_140 +#define optional_HAVE_IS_DEFAULT optional_CPP11_140 +#define optional_HAVE_NOEXCEPT optional_CPP11_140 +#define optional_HAVE_NULLPTR optional_CPP11_100 +#define optional_HAVE_REF_QUALIFIER optional_CPP11_140_490 +#define optional_HAVE_INITIALIZER_LIST optional_CPP11_140 + +// Presence of C++14 language features: + +#define optional_HAVE_CONSTEXPR_14 optional_CPP14_000 + +// Presence of C++17 language features: + +#define optional_HAVE_NODISCARD optional_CPP17_000 + +// Presence of C++ library features: + +#define optional_HAVE_CONDITIONAL optional_CPP11_120 +#define optional_HAVE_REMOVE_CV optional_CPP11_120 +#define optional_HAVE_TYPE_TRAITS optional_CPP11_90 + +#define optional_HAVE_TR1_TYPE_TRAITS (!! optional_COMPILER_GNUC_VERSION ) +#define optional_HAVE_TR1_ADD_POINTER (!! optional_COMPILER_GNUC_VERSION ) + +// C++ feature usage: + +#if optional_HAVE( CONSTEXPR_11 ) +# define optional_constexpr constexpr +#else +# define optional_constexpr /*constexpr*/ +#endif + +#if optional_HAVE( IS_DEFAULT ) +# define optional_is_default = default; +#else +# define optional_is_default {} +#endif + +#if optional_HAVE( CONSTEXPR_14 ) +# define optional_constexpr14 constexpr +#else +# define optional_constexpr14 /*constexpr*/ +#endif + +#if optional_HAVE( NODISCARD ) +# define optional_nodiscard [[nodiscard]] +#else +# define optional_nodiscard /*[[nodiscard]]*/ +#endif + +#if optional_HAVE( NOEXCEPT ) +# define optional_noexcept noexcept +#else +# define optional_noexcept /*noexcept*/ +#endif + +#if optional_HAVE( NULLPTR ) +# define optional_nullptr nullptr +#else +# define optional_nullptr NULL +#endif + +#if optional_HAVE( REF_QUALIFIER ) +// NOLINTNEXTLINE( bugprone-macro-parentheses ) +# define optional_ref_qual & +# define optional_refref_qual && +#else +# define optional_ref_qual /*&*/ +# define optional_refref_qual /*&&*/ +#endif + +// additional includes: + +#if optional_CONFIG_NO_EXCEPTIONS +// already included: +#else +# include +#endif + +#if optional_CPP11_OR_GREATER +# include +#endif + +#if optional_HAVE( INITIALIZER_LIST ) +# include +#endif + +#if optional_HAVE( TYPE_TRAITS ) +# include +#elif optional_HAVE( TR1_TYPE_TRAITS ) +# include +#endif + +// Method enabling + +#if optional_CPP11_OR_GREATER + +#define optional_REQUIRES_0(...) \ + template< bool B = (__VA_ARGS__), typename std::enable_if::type = 0 > + +#define optional_REQUIRES_T(...) \ + , typename std::enable_if< (__VA_ARGS__), int >::type = 0 + +#define optional_REQUIRES_R(R, ...) \ + typename std::enable_if< (__VA_ARGS__), R>::type + +#define optional_REQUIRES_A(...) \ + , typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr + +#endif + +// +// optional: +// + +namespace nonstd { namespace optional_lite { + +namespace std11 { + +#if optional_CPP11_OR_GREATER + using std::move; +#else + template< typename T > T & move( T & t ) { return t; } +#endif + +#if optional_HAVE( CONDITIONAL ) + using std::conditional; +#else + template< bool B, typename T, typename F > struct conditional { typedef T type; }; + template< typename T, typename F > struct conditional { typedef F type; }; +#endif // optional_HAVE_CONDITIONAL + +// gcc < 5: +#if optional_CPP11_OR_GREATER +#if optional_BETWEEN( optional_COMPILER_GNUC_VERSION, 1, 500 ) + template< typename T > struct is_trivially_copy_constructible : std::true_type{}; + template< typename T > struct is_trivially_move_constructible : std::true_type{}; +#else + using std::is_trivially_copy_constructible; + using std::is_trivially_move_constructible; +#endif +#endif +} // namespace std11 + +#if optional_CPP11_OR_GREATER + +/// type traits C++17: + +namespace std17 { + +#if optional_CPP17_OR_GREATER + +using std::is_swappable; +using std::is_nothrow_swappable; + +#elif optional_CPP11_OR_GREATER + +namespace detail { + +using std::swap; + +struct is_swappable +{ + template< typename T, typename = decltype( swap( std::declval(), std::declval() ) ) > + static std::true_type test( int /*unused*/ ); + + template< typename > + static std::false_type test(...); +}; + +struct is_nothrow_swappable +{ + // wrap noexcept(expr) in separate function as work-around for VC140 (VS2015): + + template< typename T > + static constexpr bool satisfies() + { + return noexcept( swap( std::declval(), std::declval() ) ); + } + + template< typename T > + static auto test( int /*unused*/ ) -> std::integral_constant()>{} + + template< typename > + static auto test(...) -> std::false_type; +}; + +} // namespace detail + +// is [nothow] swappable: + +template< typename T > +struct is_swappable : decltype( detail::is_swappable::test(0) ){}; + +template< typename T > +struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test(0) ){}; + +#endif // optional_CPP17_OR_GREATER + +} // namespace std17 + +/// type traits C++20: + +namespace std20 { + +template< typename T > +struct remove_cvref +{ + typedef typename std::remove_cv< typename std::remove_reference::type >::type type; +}; + +} // namespace std20 + +#endif // optional_CPP11_OR_GREATER + +/// class optional + +template< typename T > +class optional; + +namespace detail { + +// C++11 emulation: + +struct nulltype{}; + +template< typename Head, typename Tail > +struct typelist +{ + typedef Head head; + typedef Tail tail; +}; + +#if optional_CONFIG_MAX_ALIGN_HACK + +// Max align, use most restricted type for alignment: + +#define optional_UNIQUE( name ) optional_UNIQUE2( name, __LINE__ ) +#define optional_UNIQUE2( name, line ) optional_UNIQUE3( name, line ) +#define optional_UNIQUE3( name, line ) name ## line + +#define optional_ALIGN_TYPE( type ) \ + type optional_UNIQUE( _t ); struct_t< type > optional_UNIQUE( _st ) + +template< typename T > +struct struct_t { T _; }; + +union max_align_t +{ + optional_ALIGN_TYPE( char ); + optional_ALIGN_TYPE( short int ); + optional_ALIGN_TYPE( int ); + optional_ALIGN_TYPE( long int ); + optional_ALIGN_TYPE( float ); + optional_ALIGN_TYPE( double ); + optional_ALIGN_TYPE( long double ); + optional_ALIGN_TYPE( char * ); + optional_ALIGN_TYPE( short int * ); + optional_ALIGN_TYPE( int * ); + optional_ALIGN_TYPE( long int * ); + optional_ALIGN_TYPE( float * ); + optional_ALIGN_TYPE( double * ); + optional_ALIGN_TYPE( long double * ); + optional_ALIGN_TYPE( void * ); + +#ifdef HAVE_LONG_LONG + optional_ALIGN_TYPE( long long ); +#endif + + struct Unknown; + + Unknown ( * optional_UNIQUE(_) )( Unknown ); + Unknown * Unknown::* optional_UNIQUE(_); + Unknown ( Unknown::* optional_UNIQUE(_) )( Unknown ); + + struct_t< Unknown ( * )( Unknown) > optional_UNIQUE(_); + struct_t< Unknown * Unknown::* > optional_UNIQUE(_); + struct_t< Unknown ( Unknown::* )(Unknown) > optional_UNIQUE(_); +}; + +#undef optional_UNIQUE +#undef optional_UNIQUE2 +#undef optional_UNIQUE3 + +#undef optional_ALIGN_TYPE + +#elif defined( optional_CONFIG_ALIGN_AS ) // optional_CONFIG_MAX_ALIGN_HACK + +// Use user-specified type for alignment: + +#define optional_ALIGN_AS( unused ) \ + optional_CONFIG_ALIGN_AS + +#else // optional_CONFIG_MAX_ALIGN_HACK + +// Determine POD type to use for alignment: + +#define optional_ALIGN_AS( to_align ) \ + typename type_of_size< alignment_types, alignment_of< to_align >::value >::type + +template< typename T > +struct alignment_of; + +template< typename T > +struct alignment_of_hack +{ + char c; + T t; + alignment_of_hack(); +}; + +template< size_t A, size_t S > +struct alignment_logic +{ + enum { value = A < S ? A : S }; +}; + +template< typename T > +struct alignment_of +{ + enum { value = alignment_logic< + sizeof( alignment_of_hack ) - sizeof(T), sizeof(T) >::value }; +}; + +template< typename List, size_t N > +struct type_of_size +{ + typedef typename std11::conditional< + N == sizeof( typename List::head ), + typename List::head, + typename type_of_size::type >::type type; +}; + +template< size_t N > +struct type_of_size< nulltype, N > +{ + typedef optional_CONFIG_ALIGN_AS_FALLBACK type; +}; + +template< typename T> +struct struct_t { T _; }; + +#define optional_ALIGN_TYPE( type ) \ + typelist< type , typelist< struct_t< type > + +struct Unknown; + +typedef + optional_ALIGN_TYPE( char ), + optional_ALIGN_TYPE( short ), + optional_ALIGN_TYPE( int ), + optional_ALIGN_TYPE( long ), + optional_ALIGN_TYPE( float ), + optional_ALIGN_TYPE( double ), + optional_ALIGN_TYPE( long double ), + + optional_ALIGN_TYPE( char *), + optional_ALIGN_TYPE( short * ), + optional_ALIGN_TYPE( int * ), + optional_ALIGN_TYPE( long * ), + optional_ALIGN_TYPE( float * ), + optional_ALIGN_TYPE( double * ), + optional_ALIGN_TYPE( long double * ), + + optional_ALIGN_TYPE( Unknown ( * )( Unknown ) ), + optional_ALIGN_TYPE( Unknown * Unknown::* ), + optional_ALIGN_TYPE( Unknown ( Unknown::* )( Unknown ) ), + + nulltype + > > > > > > > > > > > > > > + > > > > > > > > > > > > > > + > > > > > > + alignment_types; + +#undef optional_ALIGN_TYPE + +#endif // optional_CONFIG_MAX_ALIGN_HACK + +/// C++03 constructed union to hold value. + +template< typename T > +union storage_t +{ +//private: +// template< typename > friend class optional; + + typedef T value_type; + + storage_t() optional_is_default + + explicit storage_t( value_type const & v ) + { + construct_value( v ); + } + + void construct_value( value_type const & v ) + { + ::new( value_ptr() ) value_type( v ); + } + +#if optional_CPP11_OR_GREATER + + explicit storage_t( value_type && v ) + { + construct_value( std::move( v ) ); + } + + void construct_value( value_type && v ) + { + ::new( value_ptr() ) value_type( std::move( v ) ); + } + + template< class... Args > + void emplace( Args&&... args ) + { + ::new( value_ptr() ) value_type( std::forward(args)... ); + } + + template< class U, class... Args > + void emplace( std::initializer_list il, Args&&... args ) + { + ::new( value_ptr() ) value_type( il, std::forward(args)... ); + } + +#endif + + void destruct_value() + { + value_ptr()->~T(); + } + + optional_nodiscard value_type const * value_ptr() const + { + return as(); + } + + value_type * value_ptr() + { + return as(); + } + + optional_nodiscard value_type const & value() const optional_ref_qual + { + return * value_ptr(); + } + + value_type & value() optional_ref_qual + { + return * value_ptr(); + } + +#if optional_HAVE( REF_QUALIFIER ) + + optional_nodiscard value_type const && value() const optional_refref_qual + { + return std::move( value() ); + } + + value_type && value() optional_refref_qual + { + return std::move( value() ); + } + +#endif + +#if optional_CPP11_OR_GREATER + + using aligned_storage_t = typename std::aligned_storage< sizeof(value_type), alignof(value_type) >::type; + aligned_storage_t data; + +#elif optional_CONFIG_MAX_ALIGN_HACK + + typedef struct { unsigned char data[ sizeof(value_type) ]; } aligned_storage_t; + + max_align_t hack; + aligned_storage_t data; + +#else + typedef optional_ALIGN_AS(value_type) align_as_type; + + typedef struct { align_as_type data[ 1 + ( sizeof(value_type) - 1 ) / sizeof(align_as_type) ]; } aligned_storage_t; + aligned_storage_t data; + +# undef optional_ALIGN_AS + +#endif // optional_CONFIG_MAX_ALIGN_HACK + + optional_nodiscard void * ptr() optional_noexcept + { + return &data; + } + + optional_nodiscard void const * ptr() const optional_noexcept + { + return &data; + } + + template + optional_nodiscard U * as() + { + return reinterpret_cast( ptr() ); + } + + template + optional_nodiscard U const * as() const + { + return reinterpret_cast( ptr() ); + } +}; + +} // namespace detail + +/// disengaged state tag + +struct nullopt_t +{ + struct init{}; + explicit optional_constexpr nullopt_t( init /*unused*/ ) optional_noexcept {} +}; + +#if optional_HAVE( CONSTEXPR_11 ) +constexpr nullopt_t nullopt{ nullopt_t::init{} }; +#else +// extra parenthesis to prevent the most vexing parse: +const nullopt_t nullopt(( nullopt_t::init() )); +#endif + +/// optional access error + +#if ! optional_CONFIG_NO_EXCEPTIONS + +class bad_optional_access : public std::logic_error +{ +public: + explicit bad_optional_access() + : logic_error( "bad optional access" ) {} +}; + +#endif //optional_CONFIG_NO_EXCEPTIONS + +/// optional + +template< typename T> +class optional +{ +private: + template< typename > friend class optional; + + typedef void (optional::*safe_bool)() const; + +public: + typedef T value_type; + + // x.x.3.1, constructors + + // 1a - default construct + optional_constexpr optional() optional_noexcept + : has_value_( false ) + , contained() + {} + + // 1b - construct explicitly empty + // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) + optional_constexpr optional( nullopt_t /*unused*/ ) optional_noexcept + : has_value_( false ) + , contained() + {} + + // 2 - copy-construct +#if optional_CPP11_OR_GREATER + // template< typename U = T + // optional_REQUIRES_T( + // std::is_copy_constructible::value + // || std11::is_trivially_copy_constructible::value + // ) + // > +#endif + optional_constexpr14 optional( optional const & other ) + : has_value_( other.has_value() ) + { + if ( other.has_value() ) + { + contained.construct_value( other.contained.value() ); + } + } + +#if optional_CPP11_OR_GREATER + + // 3 (C++11) - move-construct from optional + template< typename U = T + optional_REQUIRES_T( + std::is_move_constructible::value + || std11::is_trivially_move_constructible::value + ) + > + optional_constexpr14 optional( optional && other ) + // NOLINTNEXTLINE( performance-noexcept-move-constructor ) + noexcept( std::is_nothrow_move_constructible::value ) + : has_value_( other.has_value() ) + { + if ( other.has_value() ) + { + contained.construct_value( std::move( other.contained.value() ) ); + } + } + + // 4a (C++11) - explicit converting copy-construct from optional + template< typename U + optional_REQUIRES_T( + std::is_constructible::value + && !std::is_constructible & >::value + && !std::is_constructible && >::value + && !std::is_constructible const & >::value + && !std::is_constructible const && >::value + && !std::is_convertible< optional & , T>::value + && !std::is_convertible< optional && , T>::value + && !std::is_convertible< optional const & , T>::value + && !std::is_convertible< optional const &&, T>::value + && !std::is_convertible< U const & , T>::value /*=> explicit */ + ) + > + explicit optional( optional const & other ) + : has_value_( other.has_value() ) + { + if ( other.has_value() ) + { + contained.construct_value( T{ other.contained.value() } ); + } + } +#endif // optional_CPP11_OR_GREATER + + // 4b (C++98 and later) - non-explicit converting copy-construct from optional + template< typename U +#if optional_CPP11_OR_GREATER + optional_REQUIRES_T( + std::is_constructible::value + && !std::is_constructible & >::value + && !std::is_constructible && >::value + && !std::is_constructible const & >::value + && !std::is_constructible const && >::value + && !std::is_convertible< optional & , T>::value + && !std::is_convertible< optional && , T>::value + && !std::is_convertible< optional const & , T>::value + && !std::is_convertible< optional const &&, T>::value + && std::is_convertible< U const & , T>::value /*=> non-explicit */ + ) +#endif // optional_CPP11_OR_GREATER + > + // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) + /*non-explicit*/ optional( optional const & other ) + : has_value_( other.has_value() ) + { + if ( other.has_value() ) + { + contained.construct_value( other.contained.value() ); + } + } + +#if optional_CPP11_OR_GREATER + + // 5a (C++11) - explicit converting move-construct from optional + template< typename U + optional_REQUIRES_T( + std::is_constructible::value + && !std::is_constructible & >::value + && !std::is_constructible && >::value + && !std::is_constructible const & >::value + && !std::is_constructible const && >::value + && !std::is_convertible< optional & , T>::value + && !std::is_convertible< optional && , T>::value + && !std::is_convertible< optional const & , T>::value + && !std::is_convertible< optional const &&, T>::value + && !std::is_convertible< U &&, T>::value /*=> explicit */ + ) + > + explicit optional( optional && other + ) + : has_value_( other.has_value() ) + { + if ( other.has_value() ) + { + contained.construct_value( T{ std::move( other.contained.value() ) } ); + } + } + + // 5a (C++11) - non-explicit converting move-construct from optional + template< typename U + optional_REQUIRES_T( + std::is_constructible::value + && !std::is_constructible & >::value + && !std::is_constructible && >::value + && !std::is_constructible const & >::value + && !std::is_constructible const && >::value + && !std::is_convertible< optional & , T>::value + && !std::is_convertible< optional && , T>::value + && !std::is_convertible< optional const & , T>::value + && !std::is_convertible< optional const &&, T>::value + && std::is_convertible< U &&, T>::value /*=> non-explicit */ + ) + > + // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) + /*non-explicit*/ optional( optional && other ) + : has_value_( other.has_value() ) + { + if ( other.has_value() ) + { + contained.construct_value( std::move( other.contained.value() ) ); + } + } + + // 6 (C++11) - in-place construct + template< typename... Args + optional_REQUIRES_T( + std::is_constructible::value + ) + > + optional_constexpr explicit optional( nonstd_lite_in_place_t(T), Args&&... args ) + : has_value_( true ) + , contained( T( std::forward(args)...) ) + {} + + // 7 (C++11) - in-place construct, initializer-list + template< typename U, typename... Args + optional_REQUIRES_T( + std::is_constructible&, Args&&...>::value + ) + > + optional_constexpr explicit optional( nonstd_lite_in_place_t(T), std::initializer_list il, Args&&... args ) + : has_value_( true ) + , contained( T( il, std::forward(args)...) ) + {} + + // 8a (C++11) - explicit move construct from value + template< typename U = T + optional_REQUIRES_T( + std::is_constructible::value + && !std::is_same::type, nonstd_lite_in_place_t(U)>::value + && !std::is_same::type, optional>::value + && !std::is_convertible::value /*=> explicit */ + ) + > + optional_constexpr explicit optional( U && value ) + : has_value_( true ) + , contained( T{ std::forward( value ) } ) + {} + + // 8b (C++11) - non-explicit move construct from value + template< typename U = T + optional_REQUIRES_T( + std::is_constructible::value + && !std::is_same::type, nonstd_lite_in_place_t(U)>::value + && !std::is_same::type, optional>::value + && std::is_convertible::value /*=> non-explicit */ + ) + > + // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) + optional_constexpr /*non-explicit*/ optional( U && value ) + : has_value_( true ) + , contained( std::forward( value ) ) + {} + +#else // optional_CPP11_OR_GREATER + + // 8 (C++98) + optional( value_type const & value ) + : has_value_( true ) + , contained( value ) + {} + +#endif // optional_CPP11_OR_GREATER + + // x.x.3.2, destructor + + ~optional() + { + if ( has_value() ) + { + contained.destruct_value(); + } + } + + // x.x.3.3, assignment + + // 1 (C++98and later) - assign explicitly empty + optional & operator=( nullopt_t /*unused*/) optional_noexcept + { + reset(); + return *this; + } + + // 2 (C++98and later) - copy-assign from optional +#if optional_CPP11_OR_GREATER + // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator ) + optional_REQUIRES_R( + optional &, + true +// std::is_copy_constructible::value +// && std::is_copy_assignable::value + ) + operator=( optional const & other ) + noexcept( + std::is_nothrow_move_assignable::value + && std::is_nothrow_move_constructible::value + ) +#else + optional & operator=( optional const & other ) +#endif + { + if ( (has_value() == true ) && (other.has_value() == false) ) { reset(); } + else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( *other ); } + else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = *other; } + return *this; + } + +#if optional_CPP11_OR_GREATER + + // 3 (C++11) - move-assign from optional + // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator ) + optional_REQUIRES_R( + optional &, + true +// std::is_move_constructible::value +// && std::is_move_assignable::value + ) + operator=( optional && other ) noexcept + { + if ( (has_value() == true ) && (other.has_value() == false) ) { reset(); } + else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std::move( *other ) ); } + else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = std::move( *other ); } + return *this; + } + + // 4 (C++11) - move-assign from value + template< typename U = T > + // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator ) + optional_REQUIRES_R( + optional &, + std::is_constructible::value + && std::is_assignable::value + && !std::is_same::type, nonstd_lite_in_place_t(U)>::value + && !std::is_same::type, optional>::value + && !(std::is_scalar::value && std::is_same::type>::value) + ) + operator=( U && value ) + { + if ( has_value() ) + { + contained.value() = std::forward( value ); + } + else + { + initialize( T( std::forward( value ) ) ); + } + return *this; + } + +#else // optional_CPP11_OR_GREATER + + // 4 (C++98) - copy-assign from value + template< typename U /*= T*/ > + optional & operator=( U const & value ) + { + if ( has_value() ) contained.value() = value; + else initialize( T( value ) ); + return *this; + } + +#endif // optional_CPP11_OR_GREATER + + // 5 (C++98 and later) - converting copy-assign from optional + template< typename U > +#if optional_CPP11_OR_GREATER + // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator ) + optional_REQUIRES_R( + optional&, + std::is_constructible< T , U const &>::value + && std::is_assignable< T&, U const &>::value + && !std::is_constructible & >::value + && !std::is_constructible && >::value + && !std::is_constructible const & >::value + && !std::is_constructible const && >::value + && !std::is_convertible< optional & , T>::value + && !std::is_convertible< optional && , T>::value + && !std::is_convertible< optional const & , T>::value + && !std::is_convertible< optional const &&, T>::value + && !std::is_assignable< T&, optional & >::value + && !std::is_assignable< T&, optional && >::value + && !std::is_assignable< T&, optional const & >::value + && !std::is_assignable< T&, optional const && >::value + ) +#else + optional& +#endif // optional_CPP11_OR_GREATER + operator=( optional const & other ) + { + return *this = optional( other ); + } + +#if optional_CPP11_OR_GREATER + + // 6 (C++11) - converting move-assign from optional + template< typename U > + // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator ) + optional_REQUIRES_R( + optional&, + std::is_constructible< T , U>::value + && std::is_assignable< T&, U>::value + && !std::is_constructible & >::value + && !std::is_constructible && >::value + && !std::is_constructible const & >::value + && !std::is_constructible const && >::value + && !std::is_convertible< optional & , T>::value + && !std::is_convertible< optional && , T>::value + && !std::is_convertible< optional const & , T>::value + && !std::is_convertible< optional const &&, T>::value + && !std::is_assignable< T&, optional & >::value + && !std::is_assignable< T&, optional && >::value + && !std::is_assignable< T&, optional const & >::value + && !std::is_assignable< T&, optional const && >::value + ) + operator=( optional && other ) + { + return *this = optional( std::move( other ) ); + } + + // 7 (C++11) - emplace + template< typename... Args + optional_REQUIRES_T( + std::is_constructible::value + ) + > + T& emplace( Args&&... args ) + { + *this = nullopt; + contained.emplace( std::forward(args)... ); + has_value_ = true; + return contained.value(); + } + + // 8 (C++11) - emplace, initializer-list + template< typename U, typename... Args + optional_REQUIRES_T( + std::is_constructible&, Args&&...>::value + ) + > + T& emplace( std::initializer_list il, Args&&... args ) + { + *this = nullopt; + contained.emplace( il, std::forward(args)... ); + has_value_ = true; + return contained.value(); + } + +#endif // optional_CPP11_OR_GREATER + + // x.x.3.4, swap + + void swap( optional & other ) +#if optional_CPP11_OR_GREATER + noexcept( + std::is_nothrow_move_constructible::value + && std17::is_nothrow_swappable::value + ) +#endif + { + using std::swap; + if ( (has_value() == true ) && (other.has_value() == true ) ) { swap( **this, *other ); } + else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std11::move(*other) ); other.reset(); } + else if ( (has_value() == true ) && (other.has_value() == false) ) { other.initialize( std11::move(**this) ); reset(); } + } + + // x.x.3.5, observers + + optional_constexpr value_type const * operator ->() const + { + return assert( has_value() ), + contained.value_ptr(); + } + + optional_constexpr14 value_type * operator ->() + { + return assert( has_value() ), + contained.value_ptr(); + } + + optional_constexpr value_type const & operator *() const optional_ref_qual + { + return assert( has_value() ), + contained.value(); + } + + optional_constexpr14 value_type & operator *() optional_ref_qual + { + return assert( has_value() ), + contained.value(); + } + +#if optional_HAVE( REF_QUALIFIER ) + + optional_constexpr value_type const && operator *() const optional_refref_qual + { + return std::move( **this ); + } + + optional_constexpr14 value_type && operator *() optional_refref_qual + { + return std::move( **this ); + } + +#endif + +#if optional_CPP11_OR_GREATER + optional_constexpr explicit operator bool() const optional_noexcept + { + return has_value(); + } +#else + optional_constexpr operator safe_bool() const optional_noexcept + { + return has_value() ? &optional::this_type_does_not_support_comparisons : 0; + } +#endif + + // NOLINTNEXTLINE( modernize-use-nodiscard ) + /*optional_nodiscard*/ optional_constexpr bool has_value() const optional_noexcept + { + return has_value_; + } + + // NOLINTNEXTLINE( modernize-use-nodiscard ) + /*optional_nodiscard*/ optional_constexpr14 value_type const & value() const optional_ref_qual + { +#if optional_CONFIG_NO_EXCEPTIONS + assert( has_value() ); +#else + if ( ! has_value() ) + { + throw bad_optional_access(); + } +#endif + return contained.value(); + } + + optional_constexpr14 value_type & value() optional_ref_qual + { +#if optional_CONFIG_NO_EXCEPTIONS + assert( has_value() ); +#else + if ( ! has_value() ) + { + throw bad_optional_access(); + } +#endif + return contained.value(); + } + +#if optional_HAVE( REF_QUALIFIER ) && ( !optional_COMPILER_GNUC_VERSION || optional_COMPILER_GNUC_VERSION >= 490 ) + + // NOLINTNEXTLINE( modernize-use-nodiscard ) + /*optional_nodiscard*/ optional_constexpr value_type const && value() const optional_refref_qual + { + return std::move( value() ); + } + + optional_constexpr14 value_type && value() optional_refref_qual + { + return std::move( value() ); + } + +#endif + +#if optional_CPP11_OR_GREATER + + template< typename U > + optional_constexpr value_type value_or( U && v ) const optional_ref_qual + { + return has_value() ? contained.value() : static_cast(std::forward( v ) ); + } + + template< typename U > + optional_constexpr14 value_type value_or( U && v ) optional_refref_qual + { + return has_value() ? std::move( contained.value() ) : static_cast(std::forward( v ) ); + } + +#else + + template< typename U > + optional_constexpr value_type value_or( U const & v ) const + { + return has_value() ? contained.value() : static_cast( v ); + } + +#endif // optional_CPP11_OR_GREATER + + // x.x.3.6, modifiers + + void reset() optional_noexcept + { + if ( has_value() ) + { + contained.destruct_value(); + } + + has_value_ = false; + } + +private: + void this_type_does_not_support_comparisons() const {} + + template< typename V > + void initialize( V const & value ) + { + assert( ! has_value() ); + contained.construct_value( value ); + has_value_ = true; + } + +#if optional_CPP11_OR_GREATER + template< typename V > + void initialize( V && value ) + { + assert( ! has_value() ); + contained.construct_value( std::move( value ) ); + has_value_ = true; + } + +#endif + +private: + bool has_value_; + detail::storage_t< value_type > contained; + +}; + +// Relational operators + +template< typename T, typename U > +inline optional_constexpr bool operator==( optional const & x, optional const & y ) +{ + return bool(x) != bool(y) ? false : !bool( x ) ? true : *x == *y; +} + +template< typename T, typename U > +inline optional_constexpr bool operator!=( optional const & x, optional const & y ) +{ + return !(x == y); +} + +template< typename T, typename U > +inline optional_constexpr bool operator<( optional const & x, optional const & y ) +{ + return (!y) ? false : (!x) ? true : *x < *y; +} + +template< typename T, typename U > +inline optional_constexpr bool operator>( optional const & x, optional const & y ) +{ + return (y < x); +} + +template< typename T, typename U > +inline optional_constexpr bool operator<=( optional const & x, optional const & y ) +{ + return !(y < x); +} + +template< typename T, typename U > +inline optional_constexpr bool operator>=( optional const & x, optional const & y ) +{ + return !(x < y); +} + +// Comparison with nullopt + +template< typename T > +inline optional_constexpr bool operator==( optional const & x, nullopt_t /*unused*/ ) optional_noexcept +{ + return (!x); +} + +template< typename T > +inline optional_constexpr bool operator==( nullopt_t /*unused*/, optional const & x ) optional_noexcept +{ + return (!x); +} + +template< typename T > +inline optional_constexpr bool operator!=( optional const & x, nullopt_t /*unused*/ ) optional_noexcept +{ + return bool(x); +} + +template< typename T > +inline optional_constexpr bool operator!=( nullopt_t /*unused*/, optional const & x ) optional_noexcept +{ + return bool(x); +} + +template< typename T > +inline optional_constexpr bool operator<( optional const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept +{ + return false; +} + +template< typename T > +inline optional_constexpr bool operator<( nullopt_t /*unused*/, optional const & x ) optional_noexcept +{ + return bool(x); +} + +template< typename T > +inline optional_constexpr bool operator<=( optional const & x, nullopt_t /*unused*/ ) optional_noexcept +{ + return (!x); +} + +template< typename T > +inline optional_constexpr bool operator<=( nullopt_t /*unused*/, optional const & /*unused*/ ) optional_noexcept +{ + return true; +} + +template< typename T > +inline optional_constexpr bool operator>( optional const & x, nullopt_t /*unused*/ ) optional_noexcept +{ + return bool(x); +} + +template< typename T > +inline optional_constexpr bool operator>( nullopt_t /*unused*/, optional const & /*unused*/ ) optional_noexcept +{ + return false; +} + +template< typename T > +inline optional_constexpr bool operator>=( optional const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept +{ + return true; +} + +template< typename T > +inline optional_constexpr bool operator>=( nullopt_t /*unused*/, optional const & x ) optional_noexcept +{ + return (!x); +} + +// Comparison with T + +template< typename T, typename U > +inline optional_constexpr bool operator==( optional const & x, U const & v ) +{ + return bool(x) ? *x == v : false; +} + +template< typename T, typename U > +inline optional_constexpr bool operator==( U const & v, optional const & x ) +{ + return bool(x) ? v == *x : false; +} + +template< typename T, typename U > +inline optional_constexpr bool operator!=( optional const & x, U const & v ) +{ + return bool(x) ? *x != v : true; +} + +template< typename T, typename U > +inline optional_constexpr bool operator!=( U const & v, optional const & x ) +{ + return bool(x) ? v != *x : true; +} + +template< typename T, typename U > +inline optional_constexpr bool operator<( optional const & x, U const & v ) +{ + return bool(x) ? *x < v : true; +} + +template< typename T, typename U > +inline optional_constexpr bool operator<( U const & v, optional const & x ) +{ + return bool(x) ? v < *x : false; +} + +template< typename T, typename U > +inline optional_constexpr bool operator<=( optional const & x, U const & v ) +{ + return bool(x) ? *x <= v : true; +} + +template< typename T, typename U > +inline optional_constexpr bool operator<=( U const & v, optional const & x ) +{ + return bool(x) ? v <= *x : false; +} + +template< typename T, typename U > +inline optional_constexpr bool operator>( optional const & x, U const & v ) +{ + return bool(x) ? *x > v : false; +} + +template< typename T, typename U > +inline optional_constexpr bool operator>( U const & v, optional const & x ) +{ + return bool(x) ? v > *x : true; +} + +template< typename T, typename U > +inline optional_constexpr bool operator>=( optional const & x, U const & v ) +{ + return bool(x) ? *x >= v : false; +} + +template< typename T, typename U > +inline optional_constexpr bool operator>=( U const & v, optional const & x ) +{ + return bool(x) ? v >= *x : true; +} + +// Specialized algorithms + +template< typename T +#if optional_CPP11_OR_GREATER + optional_REQUIRES_T( + std::is_move_constructible::value + && std17::is_swappable::value ) +#endif +> +void swap( optional & x, optional & y ) +#if optional_CPP11_OR_GREATER + noexcept( noexcept( x.swap(y) ) ) +#endif +{ + x.swap( y ); +} + +#if optional_CPP11_OR_GREATER + +template< typename T > +optional_constexpr optional< typename std::decay::type > make_optional( T && value ) +{ + return optional< typename std::decay::type >( std::forward( value ) ); +} + +template< typename T, typename...Args > +optional_constexpr optional make_optional( Args&&... args ) +{ + return optional( nonstd_lite_in_place(T), std::forward(args)...); +} + +template< typename T, typename U, typename... Args > +optional_constexpr optional make_optional( std::initializer_list il, Args&&... args ) +{ + return optional( nonstd_lite_in_place(T), il, std::forward(args)...); +} + +#else + +template< typename T > +optional make_optional( T const & value ) +{ + return optional( value ); +} + +#endif // optional_CPP11_OR_GREATER + +} // namespace optional_lite + +using optional_lite::optional; +using optional_lite::nullopt_t; +using optional_lite::nullopt; + +#if ! optional_CONFIG_NO_EXCEPTIONS +using optional_lite::bad_optional_access; +#endif + +using optional_lite::make_optional; + +} // namespace nonstd + +#if optional_CPP11_OR_GREATER + +// specialize the std::hash algorithm: + +namespace std { + +template< class T > +struct hash< nonstd::optional > +{ +public: + std::size_t operator()( nonstd::optional const & v ) const optional_noexcept + { + return bool( v ) ? std::hash{}( *v ) : 0; + } +}; + +} //namespace std + +#endif // optional_CPP11_OR_GREATER + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER ) +# pragma warning( pop ) +#endif + +#endif // optional_USES_STD_OPTIONAL + +#endif // NONSTD_OPTIONAL_LITE_HPP diff --git a/include/bbp/sonata/report_reader.h b/include/bbp/sonata/report_reader.h index 0c9ff1af..e724a778 100644 --- a/include/bbp/sonata/report_reader.h +++ b/include/bbp/sonata/report_reader.h @@ -9,20 +9,21 @@ #include #include +#include namespace H5 = HighFive; namespace bbp { namespace sonata { -// KeyType will be NodeID for somas report and pair for elements report +// KeyType will be NodeID for somas report and pair for elements report template struct SONATA_API DataFrame { using DataType = std::vector; std::vector times; DataType ids; - // data[times][ids] - std::vector> data; + // data[times][ids], flattened. n_cols is ids.size() + std::vector data; }; using Spike = std::pair; @@ -53,9 +54,9 @@ class SONATA_API SpikeReader /** * Return reports for this population. */ - Spikes get(const Selection& node_ids = Selection({}), - double tstart = -1, - double tstop = -1) const; + Spikes get(const nonstd::optional& node_ids = nonstd::nullopt, + const nonstd::optional& tstart = nonstd::nullopt, + const nonstd::optional& tstop = nonstd::nullopt) const; /** * Return the way data are sorted ('none', 'by_id', 'by_time') @@ -81,7 +82,7 @@ class SONATA_API SpikeReader /** * Return a list of all population names. */ - std::vector getPopulationsNames() const; + std::vector getPopulationNames() const; const Population& openPopulation(const std::string& populationName) const; @@ -118,22 +119,25 @@ class SONATA_API ReportReader * Return true if the data is sorted. */ bool getSorted() const; + std::vector getNodeIds() const; /** * \param node_ids limit the report to the given selection. - * \param tstart return spikes occurring on or after tstart. tstart=-1 indicates no limit. - * \param tstop return spikes occurring on or before tstop. tstop=-1 indicates no limit. + * \param tstart return spikes occurring on or after tstart. tstart=nonstd::nullopt + * indicates no limit. \param tstop return spikes occurring on or before tstop. + * tstop=nonstd::nullopt indicates no limit. */ - DataFrame get(const Selection& nodes_ids = Selection({}), - double _tstart = -1, - double _tstop = -1) const; + DataFrame get(const nonstd::optional& node_ids = nonstd::nullopt, + const nonstd::optional& tstart = nonstd::nullopt, + const nonstd::optional& tstop = nonstd::nullopt) const; private: Population(const H5::File& file, const std::string& populationName); - std::pair getIndex(double tstart, double tstop) const; + std::pair getIndex(const nonstd::optional& tstart, const nonstd::optional& tstop) const; std::vector>> nodes_pointers_; H5::Group pop_group_; + std::vector nodes_ids_; double tstart_, tstop_, tstep_; std::vector> times_index_; std::string time_units_; @@ -148,7 +152,7 @@ class SONATA_API ReportReader /** * Return a list of all population names. */ - std::vector getPopulationsNames() const; + std::vector getPopulationNames() const; const Population& openPopulation(const std::string& populationName) const; @@ -160,7 +164,7 @@ class SONATA_API ReportReader }; using SomaReportReader = ReportReader; -using ElementReportReader = ReportReader>; +using ElementReportReader = ReportReader>; } // namespace sonata } // namespace bbp diff --git a/python/bindings.cpp b/python/bindings.cpp index a3bf1077..5acc9da5 100644 --- a/python/bindings.cpp +++ b/python/bindings.cpp @@ -52,6 +52,16 @@ py::array asArray(std::vector&& values) { } +// Return a new Numpy array with data owned by another python object +// This avoids copies, and enables correct reference counting for memory keep-alive +template +py::array managedMemoryArray(const DATA_T* data, const DIMS_T& dims, const OWNER_T& owner) { + const auto& tinfo = py::detail::get_type_info(typeid(OWNER_T)); + const auto& handle = py::detail::get_object_handle(&owner, tinfo); + return py::array(dims, data, handle); +} + + template py::object getAttribute(const Population& obj, const std::string& name, @@ -294,42 +304,52 @@ py::class_ bindStorageClass(py::module& m, const char* clsName, const c } } // unnamed namespace +namespace pybind11 { +namespace detail { +template +struct type_caster>: optional_caster> {}; + +template <> +struct type_caster: public void_caster {}; +} // namespace detail +} // namespace pybind11 + + template void bindReportReader(py::module& m, const std::string& prefix) { py::class_>(m, (prefix + "DataFrame").c_str(), - "Something easily convertible to pandas dataframe") + "A container of raw reporting data, compatible with Pandas") .def_readonly("ids", &DataFrame::ids) - .def_readonly("data", &DataFrame::data) - .def_readonly("times", &DataFrame::times); + + // .data and .time members are owned by this c++ object. We can't do std::move. + // To avoid copies we must declare the owner of the data is the current python + // object. Numpy will adjust owner reference count according to returned arrays + // clang-format off + .def_property_readonly("data", [](const DataFrame& dframe) { + std::array dims {0l, ssize_t(dframe.ids.size())}; + if (dims[1] > 0) { + dims[0] = dframe.data.size() / dims[1]; + } + return managedMemoryArray(dframe.data.data(), dims, dframe); + }) + // clang-format on + .def_property_readonly("times", [](DataFrame& dframe) { + return managedMemoryArray(dframe.times.data(), dframe.times.size(), dframe); + }); + py::class_(m, (prefix + "ReportPopulation").c_str(), "A population inside a ReportReader") - .def( - "get", - [](const typename ReportType::Population& self) { return self.get(); }, - "Return all reports") - .def( - "get", - [](const typename ReportType::Population& self, double tstart, double tstop) { - return self.get(Selection({}), tstart, tstop); - }, - "Return reports between 'tstart' and 'tstop'", - "tstart"_a, - "tstop"_a) - .def( - "get", - [](const typename ReportType::Population& self, Selection sel) { - return self.get(sel); - }, - "Return reports with all those node_ids", - "node_ids"_a) .def("get", &ReportType::Population::get, "Return reports with all those node_ids between 'tstart' and 'tstop'", - "node_ids"_a, - "tstart"_a, - "tstop"_a) + "node_ids"_a = nonstd::nullopt, + "tstart"_a = nonstd::nullopt, + "tstop"_a = nonstd::nullopt) + .def("get_node_ids", + &ReportType::Population::getNodeIds, + "Return the list of nodes ids for this population") .def_property_readonly("sorted", &ReportType::Population::getSorted, DOC_REPORTREADER_POP(getSorted)) @@ -344,9 +364,7 @@ void bindReportReader(py::module& m, const std::string& prefix) { DOC_REPORTREADER_POP(getDataUnits)); py::class_(m, (prefix + "ReportReader").c_str(), "Used to read somas files") .def(py::init()) - .def("get_populations_names", - &ReportType::getPopulationsNames, - "Get list of all populations") + .def("get_population_names", &ReportType::getPopulationNames, "Get list of all populations") .def("__getitem__", &ReportType::openPopulation); } @@ -370,7 +388,6 @@ PYBIND11_MODULE(_libsonata, m) { "__bool__", [](const Selection& obj) { return !obj.empty(); }, "True if Selection is not empty") - .def("__eq__", &bbp::sonata::operator==, "Compare selection contents are equal") .def("__ne__", &bbp::sonata::operator!=, "Compare selection contents are not equal") .def("__or__", &bbp::sonata::operator|, "Union of selections") @@ -487,29 +504,12 @@ PYBIND11_MODULE(_libsonata, m) { bindStorageClass(m, "EdgeStorage", "EdgePopulation"); py::class_(m, "SpikePopulation", "A population inside a SpikeReader") - .def( - "get", - [](const SpikeReader::Population& self) { return self.get(); }, - "Return all spikes") - .def( - "get", - [](const SpikeReader::Population& self, double tstart, double tstop) { - return self.get(Selection({}), tstart, tstop); - }, - "Return spikes between 'tstart' and 'tstop'", - "tstart"_a, - "tstop"_a) - .def( - "get", - [](const SpikeReader::Population& self, Selection sel) { return self.get(sel); }, - "Return spikes with all those node_ids", - "node_ids"_a) .def("get", &SpikeReader::Population::get, "Return spikes with all those node_ids between 'tstart' and 'tstop'", - "node_ids"_a, - "tstart"_a, - "tstop"_a) + "node_ids"_a = nonstd::nullopt, + "tstart"_a = nonstd::nullopt, + "tstop"_a = nonstd::nullopt) .def_property_readonly( "sorting", [](const SpikeReader::Population& self) { @@ -523,9 +523,9 @@ PYBIND11_MODULE(_libsonata, m) { DOC_SPIKEREADER_POP(getSorting)); py::class_(m, "SpikeReader", "Used to read spike files") .def(py::init()) - .def("get_populations_names", - &SpikeReader::getPopulationsNames, - DOC_SPIKEREADER(getPopulationsNames)) + .def("get_population_names", + &SpikeReader::getPopulationNames, + DOC_SPIKEREADER(getPopulationNames)) .def("__getitem__", &SpikeReader::openPopulation); bindReportReader(m, "Soma"); diff --git a/python/generated/docstrings.h b/python/generated/docstrings.h index 36552f34..0b25fe02 100644 --- a/python/generated/docstrings.h +++ b/python/generated/docstrings.h @@ -255,23 +255,27 @@ R"doc(Parameter ``node_ids``: limit the report to the given selection. Parameter ``tstart``: - return spikes occurring on or after tstart. tstart=-1 indicates no - limit. + return spikes occurring on or after tstart. tstart=nonstd::nullopt + indicates no limit. Parameter ``tstop``: - return spikes occurring on or before tstop. tstop=-1 indicates no - limit.)doc"; + return spikes occurring on or before tstop. tstop=nonstd::nullopt + indicates no limit.)doc"; static const char *__doc_bbp_sonata_ReportReader_Population_getDataUnits = R"doc(Return the unit of data.)doc"; static const char *__doc_bbp_sonata_ReportReader_Population_getIndex = R"doc()doc"; +static const char *__doc_bbp_sonata_ReportReader_Population_getNodeIds = R"doc()doc"; + static const char *__doc_bbp_sonata_ReportReader_Population_getSorted = R"doc(Return true if the data is sorted.)doc"; static const char *__doc_bbp_sonata_ReportReader_Population_getTimeUnits = R"doc(Return the unit of time)doc"; static const char *__doc_bbp_sonata_ReportReader_Population_getTimes = R"doc(Return (tstart, tstop, tstep) of the population)doc"; +static const char *__doc_bbp_sonata_ReportReader_Population_nodes_ids = R"doc()doc"; + static const char *__doc_bbp_sonata_ReportReader_Population_nodes_ids_sorted = R"doc()doc"; static const char *__doc_bbp_sonata_ReportReader_Population_nodes_pointers = R"doc()doc"; @@ -292,7 +296,7 @@ static const char *__doc_bbp_sonata_ReportReader_ReportReader = R"doc()doc"; static const char *__doc_bbp_sonata_ReportReader_file = R"doc()doc"; -static const char *__doc_bbp_sonata_ReportReader_getPopulationsNames = R"doc(Return a list of all population names.)doc"; +static const char *__doc_bbp_sonata_ReportReader_getPopulationNames = R"doc(Return a list of all population names.)doc"; static const char *__doc_bbp_sonata_ReportReader_openPopulation = R"doc()doc"; @@ -361,7 +365,7 @@ static const char *__doc_bbp_sonata_SpikeReader_SpikeReader = R"doc()doc"; static const char *__doc_bbp_sonata_SpikeReader_filename = R"doc()doc"; -static const char *__doc_bbp_sonata_SpikeReader_getPopulationsNames = R"doc(Return a list of all population names.)doc"; +static const char *__doc_bbp_sonata_SpikeReader_getPopulationNames = R"doc(Return a list of all population names.)doc"; static const char *__doc_bbp_sonata_SpikeReader_openPopulation = R"doc()doc"; diff --git a/python/tests/test.py b/python/tests/test.py index e36c607a..496501ec 100644 --- a/python/tests/test.py +++ b/python/tests/test.py @@ -232,7 +232,7 @@ def setUp(self): self.test_obj = SpikeReader(path) def test_get_all_populations(self): - self.assertEqual(self.test_obj.get_populations_names(), ['All', 'spikes1', 'spikes2']) + self.assertEqual(self.test_obj.get_population_names(), ['All', 'spikes1', 'spikes2']) def test_get_population(self): self.assertTrue(isinstance(self.test_obj['spikes1'], SpikePopulation)) @@ -242,8 +242,8 @@ def test_get_inexistant_population(self): def test_get_spikes_from_population(self): self.assertEqual(self.test_obj['All'].get(), [(5, 0.1), (2, 0.2), (3, 0.3), (2, 0.7), (3, 1.3)]) - self.assertEqual(self.test_obj['All'].get(0.2, 1.0), [(2, 0.2), (3, 0.3), (2, 0.7)]) - self.assertEqual(self.test_obj['spikes2'].get(0.2, 1.0), [(3, 0.3), (2, 0.2), (2, 0.7)]) + self.assertEqual(self.test_obj['All'].get(tstart=0.2, tstop=1.0), [(2, 0.2), (3, 0.3), (2, 0.7)]) + self.assertEqual(self.test_obj['spikes2'].get(tstart=0.2, tstop=1.0), [(3, 0.3), (2, 0.2), (2, 0.7)]) self.assertEqual(self.test_obj['spikes1'].get((3,)), [(3, 0.3), (3, 1.3)]) self.assertEqual(self.test_obj['spikes2'].get((3,)), [(3, 0.3), (3, 1.3)]) self.assertEqual(self.test_obj['spikes2'].get((10,)), []) @@ -254,13 +254,15 @@ def test_get_spikes_from_population(self): self.assertEqual(self.test_obj['spikes1'].sorting, "by_id") self.assertEqual(self.test_obj['spikes2'].sorting, "none") + self.assertEqual(len(self.test_obj['All'].get(node_ids=[])), 0) + class TestSomaReportPopulation(unittest.TestCase): def setUp(self): path = os.path.join(PATH, "somas.h5") self.test_obj = SomaReportReader(path) def test_get_all_population(self): - self.assertEqual(self.test_obj.get_populations_names(), ['All', 'soma1', 'soma2']) + self.assertEqual(self.test_obj.get_population_names(), ['All', 'soma1', 'soma2']) def test_get_population(self): self.assertTrue(isinstance(self.test_obj['All'], SomaReportPopulation)) @@ -286,7 +288,7 @@ def setUp(self): self.test_obj = ElementReportReader(path) def test_get_all_population(self): - self.assertEqual(self.test_obj.get_populations_names(), ['All', 'element1', 'element42']) + self.assertEqual(self.test_obj.get_population_names(), ['All', 'element1', 'element42']) def test_get_population(self): self.assertTrue(isinstance(self.test_obj['All'], ElementReportPopulation)) @@ -294,8 +296,14 @@ def test_get_population(self): def test_get_inexistant_population(self): self.assertRaises(RuntimeError, self.test_obj.__getitem__, 'foobar') + def test_get_node_ids(self): + self.assertEqual(self.test_obj['All'].get_node_ids(), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]) + def test_get_reports_from_population(self): self.assertEqual(self.test_obj['All'].times, (0., 4., 0.2)) + # check following calls succeed (no memory destroyed) + self.assertEqual(self.test_obj['All'].times, (0., 4., 0.2)) + self.assertEqual(self.test_obj['All'].time_units, 'ms') self.assertEqual(self.test_obj['All'].data_units, 'mV') self.assertTrue(self.test_obj['All'].sorted) @@ -307,12 +315,17 @@ def test_get_reports_from_population(self): keys.sort() self.assertEqual(keys, [(13, 30), (13, 30), (13, 31), (13, 31), (13, 32), (14, 32), (14, 33), (14, 33), (14, 34), (14, 34)]) - self.assertEqual(len(sel.times), 3) # Number of timestamp (0.8, 1.0 and 1.2) - sel = self.test_obj['All'].get(tstart=5., tstop=-1) # tstart out of range - self.assertEqual(len(sel.ids), 0) - sel = self.test_obj['All'].get(tstart=3., tstop=3.) - self.assertEqual(len(sel.ids), 100) + self.assertEqual(len(self.test_obj['All'].get(node_ids=[]).data), 0) + self.assertEqual(len(self.test_obj['All'].get(node_ids=[]).times), 0) + self.assertEqual(len(self.test_obj['All'].get(node_ids=[]).ids), 0) + self.assertEqual(len(sel.times), 3) # Number of timestamp (0.8, 1.0 and 1.2) + with self.assertRaises(SonataError): + self.test_obj['All'].get(tstart=5.) # tstart out of range + np.testing.assert_allclose(self.test_obj['All'].get(node_ids=[1, 2], tstart=3., tstop=3.).data[0], [150.0, 150.1, 150.2, 150.3, 150.4, 150.5, 150.6, 150.7, 150.8, 150.9]) # tstart should be <= tstop + # check following calls succeed (no memory destroyed) + np.testing.assert_allclose(self.test_obj['All'].get(node_ids=[1, 2], tstart=3., tstop=3.).data[0], [150.0, 150.1, 150.2, 150.3, 150.4, 150.5, 150.6, 150.7, 150.8, 150.9]) + np.testing.assert_allclose(self.test_obj['All'].get(node_ids=[3, 4], tstart=0.2, tstop=0.4).data[0], [11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9], 1e-6, 0) if __name__ == '__main__': unittest.main() diff --git a/src/report_reader.cpp b/src/report_reader.cpp index e8ce443a..91794086 100644 --- a/src/report_reader.cpp +++ b/src/report_reader.cpp @@ -14,34 +14,36 @@ HIGHFIVE_REGISTER_TYPE(bbp::sonata::SpikeReader::Population::Sorting, create_enu namespace { +using bbp::sonata::ElementID; using bbp::sonata::NodeID; using bbp::sonata::Selection; using bbp::sonata::Spike; using bbp::sonata::Spikes; void filterNodeIDUnsorted(Spikes& spikes, const Selection& node_ids) { - auto values = node_ids.flatten(); - auto new_end = std::remove_if(spikes.begin(), spikes.end(), [&values](const Spike& spike) { - return std::find(values.cbegin(), values.cend(), spike.first) == values.cend(); - }); + const auto values = node_ids.flatten(); + const auto new_end = + std::remove_if(spikes.begin(), spikes.end(), [&values](const Spike& spike) { + return std::find(values.cbegin(), values.cend(), spike.first) == values.cend(); + }); spikes.erase(new_end, spikes.end()); } void filterNodeIDSorted(Spikes& spikes, const Selection& node_ids) { Spikes _spikes; for (const auto& range : node_ids.ranges()) { - auto begin = std::lower_bound(spikes.begin(), - spikes.end(), - std::make_pair(range.first, 0.), - [](const Spike& spike1, const Spike& spike2) { - return spike1.first < spike2.first; - }); - auto end = std::upper_bound(spikes.begin(), - spikes.end(), - std::make_pair(range.second - 1, 0.), - [](const Spike& spike1, const Spike& spike2) { - return spike1.first < spike2.first; - }); + const auto begin = std::lower_bound(spikes.begin(), + spikes.end(), + std::make_pair(range.first, 0.), + [](const Spike& spike1, const Spike& spike2) { + return spike1.first < spike2.first; + }); + const auto end = std::upper_bound(spikes.begin(), + spikes.end(), + std::make_pair(range.second - 1, 0.), + [](const Spike& spike1, const Spike& spike2) { + return spike1.first < spike2.first; + }); std::move(begin, end, std::back_inserter(_spikes)); spikes.erase(begin, end); // have to erase, because otherwise it is no more sorted @@ -58,32 +60,32 @@ void filterTimestampUnsorted(Spikes& spikes, double tstart, double tstop) { } void filterTimestampSorted(Spikes& spikes, double tstart, double tstop) { - auto end = std::upper_bound(spikes.begin(), - spikes.end(), - std::make_pair(0UL, tstop + EPSILON), - [](const Spike& spike1, const Spike& spike2) { - return spike1.second < spike2.second; - }); + const auto end = std::upper_bound(spikes.begin(), + spikes.end(), + std::make_pair(0UL, tstop + EPSILON), + [](const Spike& spike1, const Spike& spike2) { + return spike1.second < spike2.second; + }); spikes.erase(end, spikes.end()); - auto begin = std::lower_bound(spikes.begin(), - spikes.end(), - std::make_pair(0UL, tstart - EPSILON), - [](const Spike& spike1, const Spike& spike2) { - return spike1.second < spike2.second; - }); + const auto begin = std::lower_bound(spikes.begin(), + spikes.end(), + std::make_pair(0UL, tstart - EPSILON), + [](const Spike& spike1, const Spike& spike2) { + return spike1.second < spike2.second; + }); spikes.erase(spikes.begin(), begin); } template -T make_key(NodeID node_id, uint32_t element_id); +T make_key(NodeID node_id, ElementID element_id); template <> -NodeID make_key(NodeID node_id, uint32_t /* element_id */) { +NodeID make_key(NodeID node_id, ElementID /* element_id */) { return node_id; } template <> -std::pair make_key(NodeID node_id, uint32_t element_id) { +std::pair make_key(NodeID node_id, ElementID element_id) { return {node_id, element_id}; } @@ -95,7 +97,7 @@ namespace sonata { SpikeReader::SpikeReader(const std::string& filename) : filename_(filename) {} -std::vector SpikeReader::getPopulationsNames() const { +std::vector SpikeReader::getPopulationNames() const { H5::File file(filename_, H5::File::ReadOnly); return file.getGroup("/spikes").listObjectNames(); } @@ -108,18 +110,29 @@ auto SpikeReader::openPopulation(const std::string& populationName) const -> con return populations_.at(populationName); } -Spikes SpikeReader::Population::get(const Selection& node_ids, double tstart, double tstop) const { - tstart = tstart < 0 ? tstart_ : tstart; - tstop = tstop < 0 ? tstop_ : tstop; - if (tstart > tstop_ + EPSILON || tstop < tstart_ - EPSILON || tstop < tstart) { +Spikes SpikeReader::Population::get(const nonstd::optional& node_ids, + const nonstd::optional& tstart, + const nonstd::optional& tstop) const { + const double start = tstart.value_or(tstart_); + const double stop = tstop.value_or(tstop_); + + if (start < 0 - EPSILON || stop < 0 - EPSILON) { + throw SonataError("Times cannot be negative"); + } + + if (start > stop) { + throw SonataError("tstart should be <= to tstop"); + } + + if (node_ids and node_ids->empty()) { return Spikes{}; } auto spikes = spikes_; - filterTimestamp(spikes, tstart, tstop); + filterTimestamp(spikes, start, stop); - if (!node_ids.empty()) { - filterNode(spikes, node_ids); + if (node_ids) { + filterNode(spikes, node_ids.value()); } return spikes; @@ -132,8 +145,8 @@ SpikeReader::Population::Sorting SpikeReader::Population::getSorting() const { SpikeReader::Population::Population(const std::string& filename, const std::string& populationName) { H5::File file(filename, H5::File::ReadOnly); - auto pop_path = std::string("/spikes/") + populationName; - auto pop = file.getGroup(pop_path); + const auto pop_path = std::string("/spikes/") + populationName; + const auto pop = file.getGroup(pop_path); std::vector node_ids; pop.getDataSet("node_ids").read(node_ids); @@ -142,7 +155,7 @@ SpikeReader::Population::Population(const std::string& filename, pop.getDataSet("timestamps").read(timestamps); if (node_ids.size() != timestamps.size()) { - throw std::runtime_error( + throw SonataError( "In spikes file, 'node_ids' and 'timestamps' does not have the same size."); } @@ -188,7 +201,7 @@ ReportReader::ReportReader(const std::string& filename) : file_(filename, H5::File::ReadOnly) {} template -std::vector ReportReader::getPopulationsNames() const { +std::vector ReportReader::getPopulationNames() const { return file_.getGroup("/report").listObjectNames(); } @@ -205,15 +218,14 @@ template ReportReader::Population::Population(const H5::File& file, const std::string& populationName) : pop_group_(file.getGroup(std::string("/report/") + populationName)) { { - auto mapping_group = pop_group_.getGroup("mapping"); - std::vector node_ids; - mapping_group.getDataSet("node_ids").read(node_ids); + const auto mapping_group = pop_group_.getGroup("mapping"); + mapping_group.getDataSet("node_ids").read(nodes_ids_); std::vector index_pointers; mapping_group.getDataSet("index_pointers").read(index_pointers); - for (size_t i = 0; i < node_ids.size(); ++i) { - nodes_pointers_.emplace_back(node_ids[i], + for (size_t i = 0; i < nodes_ids_.size(); ++i) { + nodes_pointers_.emplace_back(nodes_ids_[i], std::make_pair(index_pointers[i], index_pointers[i + 1])); } @@ -225,8 +237,8 @@ ReportReader::Population::Population(const H5::File& file, const std::string& tstep_ = times[2]; mapping_group.getDataSet("time").getAttribute("units").read(time_units_); size_t i = 0; - for (double t = tstart_; t < tstop_ - EPSILON; t += tstep_) { - times_index_.push_back({i++, t}); + for (double t = tstart_; t < tstop_ - EPSILON; t += tstep_, ++i) { + times_index_.emplace_back(i, t); } } @@ -261,57 +273,57 @@ bool ReportReader::Population::getSorted() const { } template -std::pair ReportReader::Population::getIndex(double tstart, double tstop) const { +std::vector ReportReader::Population::getNodeIds() const { + return nodes_ids_; +} + +template +std::pair ReportReader::Population::getIndex( + const nonstd::optional& tstart, const nonstd::optional& tstop) const { std::pair indexes; - if (tstart < 0 - EPSILON) { // Default value - indexes.first = times_index_.front().first; - } else if (tstart > times_index_.back().second + EPSILON) { // tstart is after end of range - indexes.first = 1; - indexes.second = 0; - return indexes; - } else { - for (const auto& time_index : times_index_) { - if (tstart < time_index.second + EPSILON) { - indexes.first = time_index.first; - break; - } - } - } - if (tstop < 0 - EPSILON) { // Default value - indexes.second = times_index_.back().first; - } else if (tstop < times_index_.front().second - EPSILON) { // tstop is before start of range - indexes.first = 1; - indexes.second = 0; - return indexes; - } else { - for (auto it = times_index_.crbegin(); it != times_index_.crend(); ++it) { - const auto& time_index = *it; - if (tstop > time_index.second - EPSILON) { - indexes.second = time_index.first; - break; - } - } + + const double start = tstart.value_or(tstart_); + const double stop = tstop.value_or(tstop_); + + if (start < 0 - EPSILON || stop < 0 - EPSILON) { + throw SonataError("Times cannot be negative"); } - if (indexes.first > indexes.second) { - indexes.second = indexes.first; + const auto it_start = std::find_if(times_index_.cbegin(), + times_index_.cend(), + [&](const std::pair& v) { + return start < v.second + EPSILON; + }); + if (it_start == times_index_.end()) { + throw SonataError("tstart is after the end of the range"); } + indexes.first = it_start->first; + + const auto it_stop = + std::find_if(times_index_.crbegin(), + times_index_.crend(), + [&](const std::pair& v) { return stop > v.second - EPSILON; }); + if (it_stop == times_index_.rend()) { + throw SonataError("tstop is before the beginning of the range"); + } + indexes.second = it_stop->first; return indexes; } + template -DataFrame ReportReader::Population::get(const Selection& selection, - double tstart, - double tstop) const { +DataFrame ReportReader::Population::get(const nonstd::optional& selection, + const nonstd::optional& tstart, + const nonstd::optional& tstop) const { DataFrame data_frame; size_t index_start = 0; size_t index_stop = 0; - std::tie(index_start, index_stop) = getIndex(tstart, tstop); + if (index_start > index_stop) { - return data_frame; + throw SonataError("tstart should be <= to tstop"); } for (size_t i = index_start; i <= index_stop; ++i) { @@ -321,25 +333,57 @@ DataFrame ReportReader::Population::get(const Selection& selection, // Simplify selection // We should remove duplicates // And when we can work with ranges let's sort them - // auto nodes_ids_ = Selection::fromValues(nodes_ids.flatten().sort()); + // auto nodes_ids_ = Selection::fromValues(node_ids.flatten().sort()); Selection::Values node_ids; - if (selection.empty()) { // Take all nodes in this case + if (!selection) { // Take all nodes in this case std::transform(nodes_pointers_.begin(), nodes_pointers_.end(), std::back_inserter(node_ids), [](const std::pair>& node_pointer) { return node_pointer.first; }); + } else if (selection->empty()) { + return DataFrame{{}, {}, {}}; } else { - node_ids = selection.flatten(); + node_ids = selection->flatten(); } - data_frame.data.resize(index_stop - index_start + 1); - // FIXME: It will be good to do it for ranges but if nodes_ids are not sorted it is not easy - // TODO: specialized this function for sorted nodes_ids? for (const auto& node_id : node_ids) { - auto it = std::find_if( + const auto it = std::find_if( + nodes_pointers_.begin(), + nodes_pointers_.end(), + [&node_id](const std::pair>& node_pointer) { + return node_pointer.first == node_id; + }); + if (it == nodes_pointers_.end()) { + continue; + } + + std::vector element_ids; + pop_group_.getGroup("mapping") + .getDataSet("element_ids") + .select({it->second.first}, {it->second.second - it->second.first}) + .read(element_ids); + for (const auto& elem : element_ids) { + data_frame.ids.push_back(make_key(node_id, elem)); + } + } + if (data_frame.ids.empty()) { // At the end no data available (wrong node_ids?) + return DataFrame{{}, {}, {}}; + } + + // Fill .data member + + auto n_time_entries = index_stop - index_start + 1; + auto n_ids = data_frame.ids.size(); + data_frame.data.resize(n_time_entries * n_ids); + + // FIXME: It will be good to do it for ranges but if node_ids are not sorted it is not easy + // TODO: specialized this function for sorted node_ids? + int ids_index = 0; + for (const auto& node_id : node_ids) { + const auto it = std::find_if( nodes_pointers_.begin(), nodes_pointers_.end(), [&node_id](const std::pair>& node_pointer) { @@ -355,28 +399,23 @@ DataFrame ReportReader::Population::get(const Selection& selection, .select({index_start, it->second.first}, {index_stop - index_start + 1, it->second.second - it->second.first}) .read(data); + int timer_index = 0; + for (const std::vector& datum : data) { - for (float d : datum) { - data_frame.data[timer_index].push_back(d); - } + std::copy(datum.data(), + datum.data() + datum.size(), + &data_frame.data[timer_index * n_ids + ids_index]); ++timer_index; } - - std::vector element_ids; - pop_group_.getGroup("mapping") - .getDataSet("element_ids") - .select({it->second.first}, {it->second.second - it->second.first}) - .read(element_ids); - for (size_t i = 0; i < element_ids.size(); ++i) { - data_frame.ids.push_back(make_key(node_id, element_ids[i])); - } + ids_index += data[0].size(); } + return data_frame; } template class ReportReader; -template class ReportReader>; +template class ReportReader>; } // namespace sonata } // namespace bbp diff --git a/tests/test_report_reader.cpp b/tests/test_report_reader.cpp index 45da0041..74eb70a9 100644 --- a/tests/test_report_reader.cpp +++ b/tests/test_report_reader.cpp @@ -4,10 +4,17 @@ using namespace bbp::sonata; +void testTimes(const std::vector& vec, double start, double step, int size) { + REQUIRE(size == vec.size()); + for (int i = 0; i < vec.size(); ++i) { + REQUIRE(std::abs(vec[i] - (start + step * i)) < std::numeric_limits::epsilon()); + } +} + TEST_CASE("SpikeReader", "[base]") { const SpikeReader reader("./data/spikes.h5"); - REQUIRE(reader.getPopulationsNames() == std::vector{"All", "spikes1", "spikes2"}); + REQUIRE(reader.getPopulationNames() == std::vector{"All", "spikes1", "spikes2"}); REQUIRE(reader.openPopulation("All").get(Selection({{3, 4}})) == std::vector>{{3UL, 0.3}, {3UL, 1.3}}); @@ -21,25 +28,113 @@ TEST_CASE("SpikeReader", "[base]") { SpikeReader::Population::Sorting::by_id); REQUIRE(reader.openPopulation("spikes2").getSorting() == SpikeReader::Population::Sorting::none); + + REQUIRE(reader.openPopulation("All").get(Selection({{5, 6}}), 0.1, 0.1) == + std::vector>{{5, 0.1}}); } -TEST_CASE("ReportReader", "[base]") { +TEST_CASE("SomaReportReader limits", "[base]") { const SomaReportReader reader("./data/somas.h5"); - REQUIRE(reader.getPopulationsNames() == std::vector{"All", "soma1", "soma2"}); + auto pop = reader.openPopulation("All"); + + // ids out of range + REQUIRE(pop.get(Selection({{100, 101}})).ids == DataFrame::DataType{}); + + // Inverted id + REQUIRE_THROWS(pop.get(Selection({{2, 1}}))); + + // Negative ids + REQUIRE_THROWS(pop.get(Selection({{-1, 1}}))); + + // Times out of range + REQUIRE_THROWS(pop.get(Selection({{1, 2}}), 100., 101.)); + + // Inverted times + REQUIRE_THROWS(pop.get(Selection({{1, 2}}), 0.2, 0.1)); + + // Negatives times + REQUIRE_THROWS(pop.get(Selection({{1, 2}}), -1., -2.)); +} + +TEST_CASE("SomaReportReader", "[base]") { + const SomaReportReader reader("./data/somas.h5"); + + REQUIRE(reader.getPopulationNames() == std::vector{"All", "soma1", "soma2"}); + + auto pop = reader.openPopulation("All"); + + REQUIRE(pop.getTimes() == std::make_tuple(0., 1., 0.1)); + + REQUIRE(pop.getTimeUnits() == "ms"); + + REQUIRE(pop.getDataUnits() == "mV"); + + REQUIRE(pop.getSorted()); + + REQUIRE(pop.getNodeIds() == std::vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}); + + auto data = pop.get(Selection({{3, 5}}), 0.2, 0.5); + REQUIRE(data.ids == DataFrame::DataType{{3, 4}}); + testTimes(data.times, 0.2, 0.1, 4); + REQUIRE(data.data == std::vector{3.2f, 4.2f, 3.3f, 4.3f, 3.4f, 4.4f, 3.5f, 4.5f}); +} + +TEST_CASE("ElementReportReader limits", "[base]") { + const ElementReportReader reader("./data/elements.h5"); + + auto pop = reader.openPopulation("All"); + + // ids out of range + REQUIRE(pop.get(Selection({{100, 101}})).ids == + DataFrame>::DataType{}); + + // Inverted id + REQUIRE_THROWS(pop.get(Selection({{2, 1}}))); + + // Negative ids + REQUIRE_THROWS(pop.get(Selection({{-1, 1}}))); + + // Times out of range + REQUIRE_THROWS(pop.get(Selection({{1, 2}}), 100., 101.)); + + // Inverted times + REQUIRE_THROWS(pop.get(Selection({{1, 2}}), 0.2, 0.1)); + + // Negatives times + REQUIRE_THROWS(pop.get(Selection({{1, 2}}), -1., -2.)); +} + +TEST_CASE("ElementReportReader", "[base]") { + const ElementReportReader reader("./data/elements.h5"); + + REQUIRE(reader.getPopulationNames() == + std::vector{"All", "element1", "element42"}); + + auto pop = reader.openPopulation("All"); + + REQUIRE(pop.getTimes() == std::make_tuple(0., 4., 0.2)); - REQUIRE(reader.openPopulation("All").getTimes() == std::make_tuple(0., 1., 0.1)); + REQUIRE(pop.getTimeUnits() == "ms"); - REQUIRE(reader.openPopulation("All").getTimeUnits() == "ms"); + REQUIRE(pop.getDataUnits() == "mV"); - REQUIRE(reader.openPopulation("All").getDataUnits() == "mV"); + REQUIRE(pop.getSorted()); - REQUIRE(reader.openPopulation("All").getSorted()); + REQUIRE(pop.getNodeIds() == std::vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}); - REQUIRE(reader.openPopulation("All").get(Selection({{3, 5}}), 0.2, 0.5).ids == - DataFrame::DataType{{3, 4}}); + auto data = pop.get(Selection({{3, 5}}), 0.2, 0.5); + REQUIRE(data.ids == + DataFrame>::DataType{ + {{3, 5}, {3, 5}, {3, 6}, {3, 6}, {3, 7}, {4, 7}, {4, 8}, {4, 8}, {4, 9}, {4, 9}}}); + testTimes(data.times, 0.2, 0.2, 2); + REQUIRE(data.data == std::vector{11.0f, 11.1f, 11.2f, 11.3f, 11.4f, 11.5f, 11.6f, + 11.7f, 11.8f, 11.9f, 21.0f, 21.1f, 21.2f, 21.3f, + 21.4f, 21.5f, 21.6f, 21.7f, 21.8f, 21.9f}); - REQUIRE( - reader.openPopulation("All").get(Selection({{3, 5}}), 0.2, 0.5).data == - std::vector>{{{3.2f, 4.2f}, {3.3f, 4.3f}, {3.4f, 4.4f}, {3.5f, 4.5f}}}); + // Select only one time + REQUIRE(pop.get(Selection({{1, 2}}), 0.6, 0.6).data == + std::vector{30.0f, 30.1f, 30.2f, 30.3f, 30.4f}); }