Browse Source

Prototype of flat_tree

master
Ludovic 'Archivist' Lagouardette 2 years ago
parent
commit
da2ac4c8ff
9 changed files with 645 additions and 245 deletions
  1. +47
    -1
      .vscode/settings.json
  2. +25
    -0
      include/gp/algorithms/sort.hpp
  3. +172
    -7
      include/gp/containers/flat_tree.hpp
  4. +3
    -237
      include/gp/utils/iterator.hpp
  5. +129
    -0
      include/gp/utils/iterators/flat_tree_iterator.hpp
  6. +16
    -0
      include/gp/utils/iterators/iterator_properties.hpp
  7. +230
    -0
      include/gp/utils/iterators/pointer_iterator.hpp
  8. +1
    -0
      include/gp_config.hpp
  9. +22
    -0
      tests/flat_tree_test.cpp

+ 47
- 1
.vscode/settings.json View File

@ -19,6 +19,52 @@
"functional": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp"
"utility": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"set": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"string": "cpp",
"string_view": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"limits": "cpp",
"new": "cpp",
"numbers": "cpp",
"ranges": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"typeinfo": "cpp"
}
}

+ 25
- 0
include/gp/algorithms/sort.hpp View File

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

+ 172
- 7
include/gp/containers/flat_tree.hpp View File

@ -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();
}
};
}

+ 3
- 237
include/gp/utils/iterator.hpp View File

@ -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>

+ 129
- 0
include/gp/utils/iterators/flat_tree_iterator.hpp View File

@ -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();
}
};
}

+ 16
- 0
include/gp/utils/iterators/iterator_properties.hpp View File

@ -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 */
};
}

+ 230
- 0
include/gp/utils/iterators/pointer_iterator.hpp View File

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

+ 1
- 0
include/gp_config.hpp View File

@ -99,6 +99,7 @@ namespace gp_config{
namespace memory_module{
constexpr bool is_ok = true;
constexpr bool prefer_constant_memory = true;
}
typedef uint32_t file_descriptor_t;

+ 22
- 0
tests/flat_tree_test.cpp View File

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

Loading…
Cancel
Save