@ -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/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 | |||
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 | |||
#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{}); |