@ -0,0 +1,25 @@ | |||||
#pragma once | |||||
#include <gp/algorithms/move.hpp> | |||||
namespace gp { | |||||
template<typename it_t, typename pred> | |||||
void selection_sort(it_t first, it_t last, pred predicate = pred{}) { | |||||
while(first != last) { | |||||
auto traveler = first; | |||||
auto it = first; | |||||
it++; | |||||
for(;it!=last;it++) { | |||||
if(predicate(*it, *traveler)) traveler = it; | |||||
} | |||||
gp::swap(*first, *traveler); | |||||
first++; | |||||
} | |||||
} | |||||
template<typename it_t, typename pred> | |||||
void sort(it_t first, it_t last, pred predicate = pred{}) { | |||||
return selection_sort(first, last, predicate); | |||||
} | |||||
} |
@ -1,12 +1,177 @@ | |||||
#pragma one | |||||
#pragma once | |||||
#include <gp/algorithms/sort.hpp> | |||||
#include <gp/containers/array.hpp> | #include <gp/containers/array.hpp> | ||||
#include <gp/integer_math.hpp> | |||||
#include <gp/iterator.hpp> | |||||
#include <gp/math/integral.hpp> | |||||
#include <gp/utils/iterator.hpp> | |||||
#include <gp/functional/optional.hpp> | |||||
// UNIMPLEMENTED: see filename | // UNIMPLEMENTED: see filename | ||||
template<typename T, size_t depth> | |||||
class flat_tree { | |||||
gp::array<T, (1 << depth) - 1> data_; | |||||
namespace gp { | |||||
}; | |||||
namespace ordinators { | |||||
struct less { | |||||
template<typename T> | |||||
bool operator() (const T& lhs, const T& rhs){ | |||||
return lhs < rhs; | |||||
} | |||||
}; | |||||
} | |||||
/** | |||||
* @brief | |||||
* | |||||
* @tparam T The type to store, must be either final or primitive | |||||
* @tparam capacity The number of elements that can be stored in the structure | |||||
*/ | |||||
template<typename T, size_t capacity, typename ordinator = gp::ordinators::less> | |||||
class flat_tree { | |||||
using node_t = gp::optional<T>; | |||||
gp::array<node_t, capacity> data_; | |||||
static void ordain(auto destination_space, auto value_workspace, index_to_1 idx) { | |||||
if(!value_workspace.size()) return; | |||||
size_t pivot; | |||||
if(value_workspace.size()%2) { | |||||
pivot = value_workspace.size()/2 + 1; | |||||
} else { | |||||
pivot = value_workspace.size()/2; | |||||
} | |||||
destination_space[idx] = gp::move(value_workspace[pivot]); | |||||
auto left = idx; | |||||
left.left(); | |||||
ordain(destination_space, value_workspace.slice_start(pivot), left); | |||||
auto right = idx; | |||||
right.right(); | |||||
ordain(destination_space, value_workspace.trim_start(pivot+1), left); | |||||
}; | |||||
public: | |||||
using iterator = gp::flat_tree_iterator<flat_tree, T, 1>; | |||||
using riterator = gp::flat_tree_iterator<flat_tree, T, -1>; | |||||
friend iterator; | |||||
friend riterator; | |||||
flat_tree(auto data) { | |||||
gp_config::assertion(data.size() <= data_.size(), "Couldn't construct flat tree from C array"); | |||||
for(auto v : data) { | |||||
insert(v); | |||||
} | |||||
} | |||||
bool insert(T& value) { | |||||
index_to_1 idx(0); | |||||
while(idx < data_.size() && data()[idx].has_value()) { | |||||
if(ordinator()(data()[idx].value(), value)) { | |||||
idx.left(); | |||||
} else { | |||||
idx.right(); | |||||
} | |||||
} | |||||
if (idx >= data_.size()) [[unlikely]] { | |||||
if(rebalance_with_value(value)) return true; | |||||
if constexpr (gp_config::has_exceptions) { | |||||
} | |||||
return false; | |||||
} | |||||
data()[idx] = value; | |||||
return true; | |||||
} | |||||
/** | |||||
* @brief Rebalance the tree | |||||
* | |||||
* @param value The value to try to push along with the rebalance | |||||
* | |||||
* @return true if the operation suceeded | |||||
* @return false if the operation failed | |||||
*/ | |||||
bool rebalance_with_value(T& value) { | |||||
// TODO: Add constant memory shenanigans | |||||
/* | |||||
auto workspace = data_.as_buffer(); | |||||
bool ok = false; | |||||
for(auto& elem : workspace) { | |||||
if(!elem.has_value()) { | |||||
ok = true; | |||||
break; | |||||
} | |||||
} | |||||
if(!ok) return false; | |||||
gp::sort(workspace.begin(), workspace.end(), [](const node_t& lhs, const node_t& rhs){ return lhs.has_value() > rhs.has_value(); }); | |||||
size_t cnt = 0; | |||||
for(auto& elem : workspace) { | |||||
if(elem.has_value()) break; | |||||
++cnt; | |||||
} | |||||
--cnt; | |||||
workspace[cnt] = value; | |||||
auto value_workspace = workspace.trim_start(cnt); | |||||
gp::sort(value_workspace.begin(), value_workspace.end(), [](const node_t& lhs, const node_t& rhs){ return ordinator(lhs.value(), rhs.value()); }); | |||||
auto destination_space = workspace.slice_start(value_workspace.size()); | |||||
for(auto ) | |||||
*/ | |||||
auto oth = gp::move(data_); | |||||
auto workspace = oth.as_buffer(); | |||||
bool ok = false; | |||||
for(auto& elem : workspace) { | |||||
if(!elem.has_value()) { | |||||
ok = true; | |||||
break; | |||||
} | |||||
} | |||||
if(!ok) return false; | |||||
gp::sort(workspace.begin(), workspace.end(), [](const node_t& lhs, const node_t& rhs){ return lhs.has_value() > rhs.has_value(); }); | |||||
size_t cnt = 0; | |||||
for(auto& elem : workspace) { | |||||
if(elem.has_value()) break; | |||||
++cnt; | |||||
} | |||||
--cnt; | |||||
workspace[cnt] = value; | |||||
auto value_workspace = workspace.trim_start(cnt); | |||||
gp::sort(value_workspace.begin(), value_workspace.end(), [](const node_t& lhs, const node_t& rhs){ return ordinator()(lhs.value(), rhs.value()); }); | |||||
auto destination_space = workspace.slice_start(value_workspace.size()); | |||||
ordain(destination_space, value_workspace, 0); | |||||
return true; | |||||
} | |||||
iterator begin() { | |||||
return ++iterator{.tree = *this, .index = (size_t)-1}; | |||||
} | |||||
iterator end() { | |||||
return iterator{.tree = *this, .index = (size_t)-1}; | |||||
} | |||||
auto data() { | |||||
return data_.as_buffer(); | |||||
} | |||||
}; | |||||
} |
@ -1,239 +1,5 @@ | |||||
#pragma once | #pragma once | ||||
#include <cstddef> | |||||
#include <cstdint> | |||||
// TODO: Specify the concept of an iterator | |||||
namespace gp { | |||||
/** | |||||
* @brief An enumeration that may be used to determine iterator categories | |||||
* | |||||
*/ | |||||
enum class iterator_type_t{ | |||||
contiguous_iterator, /**< Defines an iterator for something that is continuous and random access */ | |||||
non_contiguous_iterator, /**< Defines an iterator for a non contiguous datastructure, for example an iterator over a hashmap or a tree*/ | |||||
lazy_iterator /**< Defines an iterator for which the actual data layout and availability are still unknown*/ | |||||
}; | |||||
/** | |||||
* @brief An abstraction of a pointer to iterate against, in both normal and reverse order | |||||
* | |||||
* @tparam T The type of data pointed by the iterator | |||||
* @tparam sign the direction in which data is scrutinized, should be either 1 or -1, behaviour for other value is left undefined | |||||
*/ | |||||
template<typename T, int sign = 1> | |||||
struct pointer_iterator final | |||||
{ | |||||
T* data; /**< the only data field of the class */ | |||||
typedef T value_type; /**< The type of which a reference will be returned on dereferencing */ | |||||
typedef std::size_t difference_type; /**< The type of the substraction of two pointers */ | |||||
static constexpr iterator_type_t iterator_type = iterator_type_t::contiguous_iterator; /**< @see iterator_type_t */ | |||||
/** | |||||
* @brief Generates an empty iterator | |||||
*/ | |||||
constexpr pointer_iterator() | |||||
: data{nullptr} | |||||
{} | |||||
constexpr pointer_iterator(const pointer_iterator& oth) | |||||
: data{oth.data} | |||||
{} | |||||
/** | |||||
* @brief Generates an iterator from any pointer | |||||
*/ | |||||
constexpr pointer_iterator(T* ptr) | |||||
: data{ptr} | |||||
{} | |||||
/** | |||||
* @brief Dereference unary operator | |||||
* | |||||
* @return constexpr T& returns a reference to the pointed value | |||||
*/ | |||||
constexpr T& operator*() const | |||||
{ | |||||
return *data; | |||||
} | |||||
constexpr pointer_iterator& operator++() | |||||
{ | |||||
data += sign; | |||||
return *this; | |||||
} | |||||
constexpr pointer_iterator operator++(int) | |||||
{ | |||||
auto p = *this; | |||||
data += sign; | |||||
return p; | |||||
} | |||||
constexpr pointer_iterator& operator--() | |||||
{ | |||||
data -= sign; | |||||
return *this; | |||||
} | |||||
constexpr pointer_iterator operator--(int) | |||||
{ | |||||
auto p = *this; | |||||
data -= sign; | |||||
return p; | |||||
} | |||||
constexpr pointer_iterator operator+(const std::size_t offset) const | |||||
{ | |||||
return pointer_iterator{data+sign*offset}; | |||||
} | |||||
constexpr pointer_iterator operator+(const int offset) const | |||||
{ | |||||
return pointer_iterator{data+sign*offset}; | |||||
} | |||||
constexpr pointer_iterator operator-(const std::size_t offset) const | |||||
{ | |||||
return pointer_iterator{data-sign*offset}; | |||||
} | |||||
constexpr pointer_iterator operator-(const int offset) const | |||||
{ | |||||
return pointer_iterator{data-sign*offset}; | |||||
} | |||||
constexpr difference_type operator-(const pointer_iterator& oth) const | |||||
{ | |||||
return ((T*)data-(T*)oth.data)*sign; | |||||
} | |||||
constexpr bool operator==(const pointer_iterator oth) const | |||||
{ | |||||
return data==oth.data; | |||||
} | |||||
constexpr bool operator!=(pointer_iterator oth) const | |||||
{ | |||||
return data!=oth.data; | |||||
} | |||||
constexpr bool before_or_equal(const pointer_iterator oth) const | |||||
{ | |||||
return reinterpret_cast<std::intptr_t>(data) <= reinterpret_cast<std::intptr_t>(oth.data); | |||||
} | |||||
constexpr bool operator<=(const pointer_iterator oth) const | |||||
{ | |||||
return before_or_equal(oth); | |||||
} | |||||
}; | |||||
/** | |||||
* @brief An identical twin to the pointer_iterator, but which dereference to a const reference | |||||
* | |||||
* @see pointer_iterator | |||||
* @tparam T @see pointer_iterator | |||||
* @tparam sign @see pointer_iterator | |||||
*/ | |||||
template<typename T, int sign = 1> | |||||
struct const_pointer_iterator final | |||||
{ | |||||
const T* data; /**< @see pointer_iterator */ | |||||
typedef T value_type; /**< @see pointer_iterator */ | |||||
typedef std::size_t difference_type; /**< @see pointer_iterator */ | |||||
static constexpr iterator_type_t iterator_type = iterator_type_t::contiguous_iterator; /**< @see pointer_iterator */ | |||||
constexpr const_pointer_iterator(const const_pointer_iterator& oth) | |||||
: data{oth.data} | |||||
{} | |||||
/** | |||||
* @brief @see pointer_iterator | |||||
*/ | |||||
constexpr const_pointer_iterator(const T* ptr) | |||||
: data{ptr} | |||||
{} | |||||
/** | |||||
* @brief Dereferencing returns a const version of what a pointer_iterator would return | |||||
*/ | |||||
constexpr const T& operator*() const | |||||
{ | |||||
return *data; | |||||
} | |||||
constexpr const_pointer_iterator& operator++() | |||||
{ | |||||
data += sign; | |||||
return *this; | |||||
} | |||||
constexpr const_pointer_iterator operator++(int) | |||||
{ | |||||
auto p = data; | |||||
data += sign; | |||||
return const_pointer_iterator{p}; | |||||
} | |||||
constexpr const_pointer_iterator& operator--() | |||||
{ | |||||
data -= sign; | |||||
return *this; | |||||
} | |||||
constexpr const_pointer_iterator operator--(int) | |||||
{ | |||||
auto p = data; | |||||
data -= sign; | |||||
return const_pointer_iterator{p}; | |||||
} | |||||
constexpr const_pointer_iterator operator+(const std::size_t offset) const | |||||
{ | |||||
return const_pointer_iterator{data+sign*offset}; | |||||
} | |||||
constexpr const_pointer_iterator operator+(const int offset) const | |||||
{ | |||||
return const_pointer_iterator{data+sign*offset}; | |||||
} | |||||
constexpr const_pointer_iterator operator-(const std::size_t offset) const | |||||
{ | |||||
return const_pointer_iterator{data-sign*offset}; | |||||
} | |||||
constexpr const_pointer_iterator operator-(const int offset) | |||||
{ | |||||
return const_pointer_iterator{data-sign*offset}; | |||||
} | |||||
constexpr difference_type operator-(const const_pointer_iterator& oth) const | |||||
{ | |||||
return ((T*)data-(T*)oth.data)*sign; | |||||
} | |||||
constexpr bool operator==(const const_pointer_iterator oth) const | |||||
{ | |||||
return data==oth.data; | |||||
} | |||||
constexpr bool operator!=(const_pointer_iterator oth) const | |||||
{ | |||||
return data!=oth.data; | |||||
} | |||||
constexpr bool before_or_equal(const const_pointer_iterator oth) const | |||||
{ | |||||
return reinterpret_cast<std::intptr_t>(data) <= reinterpret_cast<std::intptr_t>(oth.data); | |||||
} | |||||
constexpr bool operator<=(const const_pointer_iterator oth) const | |||||
{ | |||||
return before_or_equal(oth); | |||||
} | |||||
}; | |||||
} | |||||
#include <gp/utils/iterators/iterator_properties.hpp> | |||||
#include <gp/utils/iterators/pointer_iterator.hpp> | |||||
#include <gp/utils/iterators/flat_tree_iterator.hpp> |
@ -0,0 +1,129 @@ | |||||
#pragma once | |||||
#include <gp/utils/iterators/iterator_properties.hpp> | |||||
#include <cstddef> | |||||
#include <cstdint> | |||||
namespace gp { | |||||
struct index_to_1 { | |||||
size_t value; | |||||
index_to_1(size_t v) : value{v + 1} | |||||
{} | |||||
void left() { | |||||
value <<= 1; | |||||
} | |||||
void right() { | |||||
value = (value << 1) + 1; | |||||
} | |||||
void up() { | |||||
value >>= 1; | |||||
} | |||||
bool is_right() const { | |||||
return value & 1; | |||||
} | |||||
bool is_left() const { | |||||
return !(value & 1); | |||||
} | |||||
bool is_root() const { | |||||
return value == 1; | |||||
} | |||||
operator size_t() { | |||||
return value - 1; | |||||
} | |||||
}; | |||||
template<typename reflect, typename T, int sign = 1> | |||||
struct flat_tree_iterator { | |||||
reflect& tree; | |||||
size_t index; | |||||
bool has_right(index_to_1 v) const { | |||||
v.right(); | |||||
return (v < tree.data().size()) && (tree.data()[v].has_value()); | |||||
} | |||||
bool has_left(index_to_1 v) const { | |||||
v.left(); | |||||
return (v < tree.data().size()) && (tree.data()[v].has_value()); | |||||
} | |||||
flat_tree_iterator& operator++() { | |||||
index = next(); | |||||
return *this; | |||||
} | |||||
flat_tree_iterator operator++(int) { | |||||
auto cpy = *this; | |||||
index = next(); | |||||
return cpy; | |||||
} | |||||
size_t next() const { | |||||
index_to_1 it{index}; | |||||
enum class cases { | |||||
right_climber, | |||||
left_climber, | |||||
descender, | |||||
starter | |||||
}; | |||||
const cases current_case = [&](){ | |||||
if(index == (size_t)-1) { | |||||
return cases::starter; | |||||
} else if(has_right(it)) { | |||||
return cases::descender; | |||||
} else if(it.is_left()) { | |||||
return cases::left_climber; | |||||
} else { | |||||
return cases::right_climber; | |||||
} | |||||
}(); | |||||
switch(current_case) { | |||||
case cases::starter: { | |||||
it = index_to_1{0}; | |||||
while(has_left(it)) { | |||||
it.left(); | |||||
} | |||||
} break; | |||||
case cases::descender: { | |||||
it.right(); | |||||
while(has_left(it)) { | |||||
it.left(); | |||||
} | |||||
} break; | |||||
case cases::left_climber: { | |||||
it.up(); | |||||
} break; | |||||
case cases::right_climber: { | |||||
while(it.is_right()) { | |||||
it.up(); | |||||
} | |||||
if(it.is_root()) { | |||||
it.value = -1; | |||||
} | |||||
} break; | |||||
} | |||||
return it; | |||||
} | |||||
bool operator!=(flat_tree_iterator rhs) { | |||||
return index != rhs.index; | |||||
} | |||||
T& operator*() { | |||||
return tree.data_[index].value(); | |||||
} | |||||
}; | |||||
} |
@ -0,0 +1,16 @@ | |||||
#pragma once | |||||
// TODO: Specify the concept of an iterator | |||||
namespace gp { | |||||
/** | |||||
* @brief An enumeration that may be used to determine iterator categories | |||||
* | |||||
*/ | |||||
enum class iterator_type_t{ | |||||
contiguous_iterator, /**< Defines an iterator for something that is continuous and random access */ | |||||
non_contiguous_iterator, /**< Defines an iterator for a non contiguous datastructure, for example an iterator over a hashmap or a tree */ | |||||
lazy_iterator /**< Defines an iterator for which the actual data layout and availability are still unknown */ | |||||
}; | |||||
} |
@ -0,0 +1,230 @@ | |||||
#pragma once | |||||
#include <gp/utils/iterators/iterator_properties.hpp> | |||||
#include <cstddef> | |||||
#include <cstdint> | |||||
// TODO: Specify the concept of an iterator | |||||
namespace gp { | |||||
/** | |||||
* @brief An abstraction of a pointer to iterate against, in both normal and reverse order | |||||
* | |||||
* @tparam T The type of data pointed by the iterator | |||||
* @tparam sign the direction in which data is scrutinized, should be either 1 or -1, behaviour for other value is left undefined | |||||
*/ | |||||
template<typename T, int sign = 1> | |||||
struct pointer_iterator final | |||||
{ | |||||
T* data; /**< the only data field of the class */ | |||||
typedef T value_type; /**< The type of which a reference will be returned on dereferencing */ | |||||
typedef std::size_t difference_type; /**< The type of the substraction of two pointers */ | |||||
static constexpr iterator_type_t iterator_type = iterator_type_t::contiguous_iterator; /**< @see iterator_type_t */ | |||||
/** | |||||
* @brief Generates an empty iterator | |||||
*/ | |||||
constexpr pointer_iterator() | |||||
: data{nullptr} | |||||
{} | |||||
constexpr pointer_iterator(const pointer_iterator& oth) | |||||
: data{oth.data} | |||||
{} | |||||
/** | |||||
* @brief Generates an iterator from any pointer | |||||
*/ | |||||
constexpr pointer_iterator(T* ptr) | |||||
: data{ptr} | |||||
{} | |||||
/** | |||||
* @brief Dereference unary operator | |||||
* | |||||
* @return constexpr T& returns a reference to the pointed value | |||||
*/ | |||||
constexpr T& operator*() const | |||||
{ | |||||
return *data; | |||||
} | |||||
constexpr pointer_iterator& operator++() | |||||
{ | |||||
data += sign; | |||||
return *this; | |||||
} | |||||
constexpr pointer_iterator operator++(int) | |||||
{ | |||||
auto p = *this; | |||||
data += sign; | |||||
return p; | |||||
} | |||||
constexpr pointer_iterator& operator--() | |||||
{ | |||||
data -= sign; | |||||
return *this; | |||||
} | |||||
constexpr pointer_iterator operator--(int) | |||||
{ | |||||
auto p = *this; | |||||
data -= sign; | |||||
return p; | |||||
} | |||||
constexpr pointer_iterator operator+(const std::size_t offset) const | |||||
{ | |||||
return pointer_iterator{data+sign*offset}; | |||||
} | |||||
constexpr pointer_iterator operator+(const int offset) const | |||||
{ | |||||
return pointer_iterator{data+sign*offset}; | |||||
} | |||||
constexpr pointer_iterator operator-(const std::size_t offset) const | |||||
{ | |||||
return pointer_iterator{data-sign*offset}; | |||||
} | |||||
constexpr pointer_iterator operator-(const int offset) const | |||||
{ | |||||
return pointer_iterator{data-sign*offset}; | |||||
} | |||||
constexpr difference_type operator-(const pointer_iterator& oth) const | |||||
{ | |||||
return ((T*)data-(T*)oth.data)*sign; | |||||
} | |||||
constexpr bool operator==(const pointer_iterator oth) const | |||||
{ | |||||
return data==oth.data; | |||||
} | |||||
constexpr bool operator!=(pointer_iterator oth) const | |||||
{ | |||||
return data!=oth.data; | |||||
} | |||||
constexpr bool before_or_equal(const pointer_iterator oth) const | |||||
{ | |||||
return reinterpret_cast<std::intptr_t>(data) <= reinterpret_cast<std::intptr_t>(oth.data); | |||||
} | |||||
constexpr bool operator<=(const pointer_iterator oth) const | |||||
{ | |||||
return before_or_equal(oth); | |||||
} | |||||
}; | |||||
/** | |||||
* @brief An identical twin to the pointer_iterator, but which dereference to a const reference | |||||
* | |||||
* @see pointer_iterator | |||||
* @tparam T @see pointer_iterator | |||||
* @tparam sign @see pointer_iterator | |||||
*/ | |||||
template<typename T, int sign = 1> | |||||
struct const_pointer_iterator final | |||||
{ | |||||
const T* data; /**< @see pointer_iterator */ | |||||
typedef T value_type; /**< @see pointer_iterator */ | |||||
typedef std::size_t difference_type; /**< @see pointer_iterator */ | |||||
static constexpr iterator_type_t iterator_type = iterator_type_t::contiguous_iterator; /**< @see pointer_iterator */ | |||||
constexpr const_pointer_iterator(const const_pointer_iterator& oth) | |||||
: data{oth.data} | |||||
{} | |||||
/** | |||||
* @brief @see pointer_iterator | |||||
*/ | |||||
constexpr const_pointer_iterator(const T* ptr) | |||||
: data{ptr} | |||||
{} | |||||
/** | |||||
* @brief Dereferencing returns a const version of what a pointer_iterator would return | |||||
*/ | |||||
constexpr const T& operator*() const | |||||
{ | |||||
return *data; | |||||
} | |||||
constexpr const_pointer_iterator& operator++() | |||||
{ | |||||
data += sign; | |||||
return *this; | |||||
} | |||||
constexpr const_pointer_iterator operator++(int) | |||||
{ | |||||
auto p = data; | |||||
data += sign; | |||||
return const_pointer_iterator{p}; | |||||
} | |||||
constexpr const_pointer_iterator& operator--() | |||||
{ | |||||
data -= sign; | |||||
return *this; | |||||
} | |||||
constexpr const_pointer_iterator operator--(int) | |||||
{ | |||||
auto p = data; | |||||
data -= sign; | |||||
return const_pointer_iterator{p}; | |||||
} | |||||
constexpr const_pointer_iterator operator+(const std::size_t offset) const | |||||
{ | |||||
return const_pointer_iterator{data+sign*offset}; | |||||
} | |||||
constexpr const_pointer_iterator operator+(const int offset) const | |||||
{ | |||||
return const_pointer_iterator{data+sign*offset}; | |||||
} | |||||
constexpr const_pointer_iterator operator-(const std::size_t offset) const | |||||
{ | |||||
return const_pointer_iterator{data-sign*offset}; | |||||
} | |||||
constexpr const_pointer_iterator operator-(const int offset) | |||||
{ | |||||
return const_pointer_iterator{data-sign*offset}; | |||||
} | |||||
constexpr difference_type operator-(const const_pointer_iterator& oth) const | |||||
{ | |||||
return ((T*)data-(T*)oth.data)*sign; | |||||
} | |||||
constexpr bool operator==(const const_pointer_iterator oth) const | |||||
{ | |||||
return data==oth.data; | |||||
} | |||||
constexpr bool operator!=(const_pointer_iterator oth) const | |||||
{ | |||||
return data!=oth.data; | |||||
} | |||||
constexpr bool before_or_equal(const const_pointer_iterator oth) const | |||||
{ | |||||
return reinterpret_cast<std::intptr_t>(data) <= reinterpret_cast<std::intptr_t>(oth.data); | |||||
} | |||||
constexpr bool operator<=(const const_pointer_iterator oth) const | |||||
{ | |||||
return before_or_equal(oth); | |||||
} | |||||
}; | |||||
} |
@ -0,0 +1,22 @@ | |||||
#include "test_scaffold.h" | |||||
#include <gp/containers/flat_tree.hpp> | |||||
#include <iostream> | |||||
struct flat_tree_test : public test_scaffold { | |||||
flat_tree_test() { | |||||
name = __FILE__ ":1"; | |||||
} | |||||
virtual int run() { | |||||
gp::array<int,7> v = {1,2,3,4,5,6,7}; | |||||
gp::flat_tree<int, 7> hello(v); | |||||
for(auto elem : hello) { | |||||
std::cout << elem << std::endl; | |||||
} | |||||
return 0; | |||||
} | |||||
}; | |||||
append_test dummy_sdfjlm6543(new flat_tree_test{}); |