- Fixed some missing and erroneous testing logic for containers
- Lots of bug-fixing for containers - Performance optimization for containers
This commit is contained in:
@@ -42,7 +42,7 @@ DOXYFILE_ENCODING = UTF-8
|
|||||||
# title of most generated pages and in a few other places.
|
# title of most generated pages and in a few other places.
|
||||||
# The default value is: My Project.
|
# The default value is: My Project.
|
||||||
|
|
||||||
PROJECT_NAME = fennec
|
PROJECT_NAME = Workbook
|
||||||
|
|
||||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
|
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
|
||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
@@ -68,7 +68,7 @@ PROJECT_LOGO =
|
|||||||
# entered, it will be relative to the location where doxygen was started. If
|
# entered, it will be relative to the location where doxygen was started. If
|
||||||
# left blank the current directory will be used.
|
# left blank the current directory will be used.
|
||||||
|
|
||||||
OUTPUT_DIRECTORY = /home/medusa/Documents/Work/Personal/fennec/docs
|
OUTPUT_DIRECTORY = /home/medusa/Documents/Work/Personal/Workbook/external/fennec/docs
|
||||||
|
|
||||||
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
|
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096
|
||||||
# sub-directories (in 2 levels) under the output directory of each output format
|
# sub-directories (in 2 levels) under the output directory of each output format
|
||||||
@@ -815,7 +815,7 @@ FILE_VERSION_FILTER =
|
|||||||
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
|
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
|
||||||
# tag is left empty.
|
# tag is left empty.
|
||||||
|
|
||||||
LAYOUT_FILE = /home/medusa/Documents/Work/Personal/fennec/doxy/DoxyLayout.xml
|
LAYOUT_FILE = /home/medusa/Documents/Work/Personal/Workbook/external/fennec/doxy/DoxyLayout.xml
|
||||||
|
|
||||||
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
|
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
|
||||||
# the reference definitions. This must be a list of .bib files. The .bib
|
# the reference definitions. This must be a list of .bib files. The .bib
|
||||||
@@ -943,9 +943,9 @@ WARN_LOGFILE =
|
|||||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||||
# Note: If this tag is empty the current directory is searched.
|
# Note: If this tag is empty the current directory is searched.
|
||||||
|
|
||||||
INPUT = "/home/medusa/Documents/Work/Personal/fennec/include" \
|
INPUT = "/home/medusa/Documents/Work/Personal/Workbook/external/fennec/include" \
|
||||||
"/home/medusa/Documents/Work/Personal/fennec/source" \
|
"/home/medusa/Documents/Work/Personal/Workbook/external/fennec/source" \
|
||||||
"/home/medusa/Documents/Work/Personal/fennec/README.md"
|
"/home/medusa/Documents/Work/Personal/Workbook/external/fennec/README.md"
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||||
@@ -1079,7 +1079,7 @@ EXCLUDE_SYMBOLS =
|
|||||||
# that contain example code fragments that are included (see the \include
|
# that contain example code fragments that are included (see the \include
|
||||||
# command).
|
# command).
|
||||||
|
|
||||||
EXAMPLE_PATH = "/home/medusa/Documents/Work/Personal/fennec/examples"
|
EXAMPLE_PATH = "/home/medusa/Documents/Work/Personal/Workbook/external/fennec/examples"
|
||||||
|
|
||||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||||
@@ -1099,7 +1099,7 @@ EXAMPLE_RECURSIVE = NO
|
|||||||
# that contain images that are to be included in the documentation (see the
|
# that contain images that are to be included in the documentation (see the
|
||||||
# \image command).
|
# \image command).
|
||||||
|
|
||||||
IMAGE_PATH = "/home/medusa/Documents/Work/Personal/fennec/doxy/static"
|
IMAGE_PATH = "/home/medusa/Documents/Work/Personal/Workbook/external/fennec/doxy/static"
|
||||||
|
|
||||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||||
@@ -1160,7 +1160,7 @@ FILTER_SOURCE_PATTERNS =
|
|||||||
# (index.html). This can be useful if you have a project on for instance GitHub
|
# (index.html). This can be useful if you have a project on for instance GitHub
|
||||||
# and want to reuse the introduction page also for the doxygen output.
|
# and want to reuse the introduction page also for the doxygen output.
|
||||||
|
|
||||||
USE_MDFILE_AS_MAINPAGE = "/home/medusa/Documents/Work/Personal/fennec/README.md"
|
USE_MDFILE_AS_MAINPAGE = "/home/medusa/Documents/Work/Personal/Workbook/external/fennec/README.md"
|
||||||
|
|
||||||
# The Fortran standard specifies that for fixed formatted Fortran code all
|
# The Fortran standard specifies that for fixed formatted Fortran code all
|
||||||
# characters from position 72 are to be considered as comment. A common
|
# characters from position 72 are to be considered as comment. A common
|
||||||
@@ -1359,7 +1359,7 @@ HTML_FILE_EXTENSION = .html
|
|||||||
# of the possible markers and block names see the documentation.
|
# of the possible markers and block names see the documentation.
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
HTML_HEADER = /home/medusa/Documents/Work/Personal/fennec/doxy/header.html
|
HTML_HEADER = /home/medusa/Documents/Work/Personal/Workbook/external/fennec/doxy/header.html
|
||||||
|
|
||||||
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
|
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
|
||||||
# generated HTML page. If the tag is left blank doxygen will generate a standard
|
# generated HTML page. If the tag is left blank doxygen will generate a standard
|
||||||
@@ -1369,7 +1369,7 @@ HTML_HEADER = /home/medusa/Documents/Work/Personal/fennec/doxy/header
|
|||||||
# that doxygen normally uses.
|
# that doxygen normally uses.
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
HTML_FOOTER = /home/medusa/Documents/Work/Personal/fennec/doxy/footer.html
|
HTML_FOOTER = /home/medusa/Documents/Work/Personal/Workbook/external/fennec/doxy/footer.html
|
||||||
|
|
||||||
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
|
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
|
||||||
# sheet that is used by each HTML page. It can be used to fine-tune the look of
|
# sheet that is used by each HTML page. It can be used to fine-tune the look of
|
||||||
@@ -1381,7 +1381,7 @@ HTML_FOOTER = /home/medusa/Documents/Work/Personal/fennec/doxy/footer
|
|||||||
# obsolete.
|
# obsolete.
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
HTML_STYLESHEET = /home/medusa/Documents/Work/Personal/fennec/doxy/style.css
|
HTML_STYLESHEET = /home/medusa/Documents/Work/Personal/Workbook/external/fennec/doxy/style.css
|
||||||
|
|
||||||
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
|
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
|
||||||
# cascading style sheets that are included after the standard style sheets
|
# cascading style sheets that are included after the standard style sheets
|
||||||
@@ -1399,10 +1399,10 @@ HTML_STYLESHEET = /home/medusa/Documents/Work/Personal/fennec/doxy/style.
|
|||||||
# documentation.
|
# documentation.
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
HTML_EXTRA_STYLESHEET = /home/medusa/Documents/Work/Personal/fennec/doxy/doxygen-awesome.css \
|
HTML_EXTRA_STYLESHEET = /home/medusa/Documents/Work/Personal/Workbook/external/fennec/doxy/doxygen-awesome.css \
|
||||||
/home/medusa/Documents/Work/Personal/fennec/doxy/doxygen-awesome-sidebar-only.css \
|
/home/medusa/Documents/Work/Personal/Workbook/external/fennec/doxy/doxygen-awesome-sidebar-only.css \
|
||||||
/home/medusa/Documents/Work/Personal/fennec/doxy/doxygen-awesome-sidebar-only-darkmode-toggle.css \
|
/home/medusa/Documents/Work/Personal/Workbook/external/fennec/doxy/doxygen-awesome-sidebar-only-darkmode-toggle.css \
|
||||||
/home/medusa/Documents/Work/Personal/fennec/doxy/custom.css
|
/home/medusa/Documents/Work/Personal/Workbook/external/fennec/doxy/custom.css
|
||||||
|
|
||||||
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
|
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
|
||||||
# other source files which should be copied to the HTML output directory. Note
|
# other source files which should be copied to the HTML output directory. Note
|
||||||
@@ -1412,7 +1412,7 @@ HTML_EXTRA_STYLESHEET = /home/medusa/Documents/Work/Personal/fennec/doxy/doxyge
|
|||||||
# files will be copied as-is; there are no commands or markers available.
|
# files will be copied as-is; there are no commands or markers available.
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
HTML_EXTRA_FILES = "/home/medusa/Documents/Work/Personal/fennec/doxy/doxygen-awesome-darkmode-toggle.js"
|
HTML_EXTRA_FILES = "/home/medusa/Documents/Work/Personal/Workbook/external/fennec/doxy/doxygen-awesome-darkmode-toggle.js"
|
||||||
|
|
||||||
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
|
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
|
||||||
# should be rendered with a dark or light theme.
|
# should be rendered with a dark or light theme.
|
||||||
|
|||||||
67
examples/assert.cpp
Normal file
67
examples/assert.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// =====================================================================================================================
|
||||||
|
// I release this example code into the public domain
|
||||||
|
// =====================================================================================================================
|
||||||
|
|
||||||
|
// This file contains code that tests the efficiency of the assert macro scheme in
|
||||||
|
// fennec engine. This is purely looking at the branching aspect and not the private
|
||||||
|
// assert definition. The code only uses passing values to see any performance overhead
|
||||||
|
// from the conditional
|
||||||
|
|
||||||
|
// This code is based on the example code that cppreference provides at
|
||||||
|
// https://en.cppreference.com/w/cpp/language/attributes/likely
|
||||||
|
|
||||||
|
// To my surprise, the difference between them is negligible.
|
||||||
|
// Even when n is a crazy number like one billion, there isn't a conclusive difference.
|
||||||
|
// I checked that it isn't the lambdas, they are optimized out.
|
||||||
|
|
||||||
|
// In debug mode, the results are to be expected; release < experimental < control
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#define assert_c(expression) \
|
||||||
|
if(not(expression)) { \
|
||||||
|
std::abort(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define assert_e(expression) \
|
||||||
|
if(not(expression)) [[unlikely]] { \
|
||||||
|
std::abort(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define assert_r(expression) (static_cast<void> (0))
|
||||||
|
|
||||||
|
volatile int sink{}; // ensures a side effect
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto benchmark = [](auto fun, auto rem) {
|
||||||
|
srand(0);
|
||||||
|
|
||||||
|
const auto start = std::chrono::high_resolution_clock::now();
|
||||||
|
for (auto size{1ULL}; size != 10'000'000ULL; ++size)
|
||||||
|
sink = fun(rand());
|
||||||
|
const std::chrono::duration<double> diff =
|
||||||
|
std::chrono::high_resolution_clock::now() - start;
|
||||||
|
|
||||||
|
std::cout << "Time: " << std::fixed << std::setprecision(6) << diff.count()
|
||||||
|
<< " sec " << rem << std::endl;
|
||||||
|
};
|
||||||
|
|
||||||
|
benchmark([](int x) {
|
||||||
|
assert_c(0 <= x && x <= RAND_MAX);
|
||||||
|
return x;
|
||||||
|
}, "control");
|
||||||
|
|
||||||
|
benchmark([](int x) {
|
||||||
|
assert_r(0 <= x && x <= RAND_MAX);
|
||||||
|
return x;
|
||||||
|
}, "release");
|
||||||
|
|
||||||
|
benchmark([](int x) {
|
||||||
|
assert_e(0 <= x && x <= RAND_MAX);
|
||||||
|
return x;
|
||||||
|
}, "experimental");
|
||||||
|
}
|
||||||
@@ -389,6 +389,79 @@ class RDTreePrinter:
|
|||||||
return self.Iterator(self.tree, 0, self.capacity)
|
return self.Iterator(self.tree, 0, self.capacity)
|
||||||
|
|
||||||
|
|
||||||
|
# PRIORITY QUEUE =======================================================================================================
|
||||||
|
|
||||||
|
class PriorityQueuePrinter:
|
||||||
|
"""Print a fennec::rdtree"""
|
||||||
|
|
||||||
|
class Iterator:
|
||||||
|
def __init__(self, tree, node, capacity):
|
||||||
|
self.tree = tree
|
||||||
|
self.capacity = capacity
|
||||||
|
self.visit = deque()
|
||||||
|
self.skip = True
|
||||||
|
|
||||||
|
self.visit.append((node, 0, 0, node))
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
if len(self.visit) == 0:
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
node = self.visit[0][0]
|
||||||
|
i = self.visit[0][1]
|
||||||
|
depth = self.visit[0][2]
|
||||||
|
start = self.visit[0][3]
|
||||||
|
self.visit.popleft()
|
||||||
|
|
||||||
|
if node == start and not self.skip:
|
||||||
|
return self.__next__()
|
||||||
|
|
||||||
|
self.skip = False
|
||||||
|
|
||||||
|
value = self.tree[node]['_val']['key']
|
||||||
|
|
||||||
|
nnext = self.tree[node]['_val']['next']
|
||||||
|
child = self.tree[node]['_val']['child']
|
||||||
|
|
||||||
|
index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
|
||||||
|
|
||||||
|
if nnext < self.capacity:
|
||||||
|
self.visit.appendleft((nnext, i + 1, depth, start))
|
||||||
|
|
||||||
|
if child < self.capacity:
|
||||||
|
self.visit.appendleft((child, 0, depth + 1, child))
|
||||||
|
self.skip = True
|
||||||
|
|
||||||
|
# ┌ ─ ├ └
|
||||||
|
|
||||||
|
if nnext != 18446744073709551615:
|
||||||
|
index += '├'
|
||||||
|
else:
|
||||||
|
index += '└'
|
||||||
|
|
||||||
|
index += '─'
|
||||||
|
index += '[{}]'.format(node)
|
||||||
|
return index, value
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, val):
|
||||||
|
self.tree = val['_table']['_table']['_alloc']['_data']
|
||||||
|
self.size = val['_table']['_size']
|
||||||
|
self.capacity = val['_table']['_table']['_alloc']['_capacity']
|
||||||
|
self.min = val['_min']
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
if self.size == 0:
|
||||||
|
return "{ empty }"
|
||||||
|
return "{ size = " + str(self.size) + " }"
|
||||||
|
|
||||||
|
def children(self):
|
||||||
|
return self.Iterator(self.tree, self.min, self.capacity)
|
||||||
|
|
||||||
|
|
||||||
# BINTREE ==============================================================================================================
|
# BINTREE ==============================================================================================================
|
||||||
|
|
||||||
class BinTreePrinter:
|
class BinTreePrinter:
|
||||||
@@ -400,6 +473,7 @@ class BinTreePrinter:
|
|||||||
self.capacity = capacity
|
self.capacity = capacity
|
||||||
self.visit = deque()
|
self.visit = deque()
|
||||||
|
|
||||||
|
if capacity > 0:
|
||||||
self.visit.append((node, 0, 0))
|
self.visit.append((node, 0, 0))
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
@@ -412,6 +486,7 @@ class BinTreePrinter:
|
|||||||
node = self.visit[0][0]
|
node = self.visit[0][0]
|
||||||
i = self.visit[0][1]
|
i = self.visit[0][1]
|
||||||
depth = self.visit[0][2]
|
depth = self.visit[0][2]
|
||||||
|
parent = self.tree[node]['parent']
|
||||||
self.visit.popleft()
|
self.visit.popleft()
|
||||||
|
|
||||||
value = self.tree[node]['value']
|
value = self.tree[node]['value']
|
||||||
@@ -424,7 +499,7 @@ class BinTreePrinter:
|
|||||||
self.visit.appendleft((left, 0, depth + 1))
|
self.visit.appendleft((left, 0, depth + 1))
|
||||||
|
|
||||||
index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
|
index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
|
||||||
if i == 0:
|
if i == 0 and parent != 18446744073709551615 and self.tree[parent]['right'] != 18446744073709551615:
|
||||||
index += '├'
|
index += '├'
|
||||||
else:
|
else:
|
||||||
index += '└'
|
index += '└'
|
||||||
@@ -537,6 +612,7 @@ def register_printers():
|
|||||||
pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter)
|
pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter)
|
||||||
pp.add_printer('fennec::bintree', '^fennec::bintree<.*>$', BinTreePrinter)
|
pp.add_printer('fennec::bintree', '^fennec::bintree<.*>$', BinTreePrinter)
|
||||||
pp.add_printer('fennec::sequence', '^fennec::sequence<.*>$', BinTreePrinter)
|
pp.add_printer('fennec::sequence', '^fennec::sequence<.*>$', BinTreePrinter)
|
||||||
|
pp.add_printer('fennec::priority_queue', '^fennec::priority_queue<.*>$', PriorityQueuePrinter)
|
||||||
pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
|
pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
|
||||||
return pp
|
return pp
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ public:
|
|||||||
using value_t = TypeT;
|
using value_t = TypeT;
|
||||||
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
|
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
|
||||||
static constexpr size_t npos = -1;
|
static constexpr size_t npos = -1;
|
||||||
inline static size_t sink = npos;
|
|
||||||
|
|
||||||
friend class iterator;
|
friend class iterator;
|
||||||
friend class const_iterator;
|
friend class const_iterator;
|
||||||
@@ -189,7 +188,7 @@ public:
|
|||||||
/// \param i The node id
|
/// \param i The node id
|
||||||
/// \returns The parent of node `i`
|
/// \returns The parent of node `i`
|
||||||
constexpr size_t parent(size_t i) const {
|
constexpr size_t parent(size_t i) const {
|
||||||
return i >= _table.size() ? npos : _table[i].parent;
|
return i == npos ? npos : _table[i].parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -205,7 +204,7 @@ public:
|
|||||||
/// \param i The node id
|
/// \param i The node id
|
||||||
/// \returns The left child of node `i`
|
/// \returns The left child of node `i`
|
||||||
constexpr size_t left(size_t i) const {
|
constexpr size_t left(size_t i) const {
|
||||||
return i >= _table.size() ? npos : _table[i].left;
|
return i == npos ? npos : _table[i].left;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -213,7 +212,7 @@ public:
|
|||||||
/// \param i The node id
|
/// \param i The node id
|
||||||
/// \returns The right child of node `i`
|
/// \returns The right child of node `i`
|
||||||
constexpr size_t right(size_t i) const {
|
constexpr size_t right(size_t i) const {
|
||||||
return i >= _table.size() ? npos : _table[i].right;
|
return i == npos ? npos : _table[i].right;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -230,11 +229,7 @@ public:
|
|||||||
/// \param i The node id
|
/// \param i The node id
|
||||||
/// \returns `true` if `i` is the right node of `parent(i)`, `false` otherwise
|
/// \returns `true` if `i` is the right node of `parent(i)`, `false` otherwise
|
||||||
constexpr bool direction(size_t i) const {
|
constexpr bool direction(size_t i) const {
|
||||||
size_t p = parent(i);
|
return i == npos ? false : i == right(parent(i));
|
||||||
if (p >= _table.capacity()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return i == right(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -245,15 +240,7 @@ public:
|
|||||||
size_t p = parent(i);
|
size_t p = parent(i);
|
||||||
size_t l = left(p);
|
size_t l = left(p);
|
||||||
size_t r = right(p);
|
size_t r = right(p);
|
||||||
return i == l ? l : r;
|
return i == l ? r : l;
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// \brief Short for "Parent Sibling," \f$O(1)\f$
|
|
||||||
/// \param i The id of the node
|
|
||||||
/// \returns The id of the parents' sibling of `i`
|
|
||||||
constexpr size_t parsib(size_t i) const {
|
|
||||||
return sibling(parent(i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -305,22 +292,18 @@ public:
|
|||||||
/// \details \f$O(1)\f$
|
/// \details \f$O(1)\f$
|
||||||
/// \param i The node id
|
/// \param i The node id
|
||||||
/// \returns `nullptr` if node `i` does not exist, otherwise, a pointer to the value of node `i`
|
/// \returns `nullptr` if node `i` does not exist, otherwise, a pointer to the value of node `i`
|
||||||
constexpr value_t* operator[](size_t i) {
|
constexpr value_t& operator[](size_t i) {
|
||||||
if (i >= _table.size()) {
|
assertd(i < _table.size(), "Index out of bounds.");
|
||||||
return nullptr;
|
return _table[i].value;
|
||||||
}
|
|
||||||
return _table[i] ? &*_table[i] : nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \details Const Access, \f$O(1)\f$
|
/// \details Const Access, \f$O(1)\f$
|
||||||
/// \param i The node id
|
/// \param i The node id
|
||||||
/// \returns `nullptr` if node `i` does not exist, otherwise, a pointer to the value of node `i`
|
/// \returns `nullptr` if node `i` does not exist, otherwise, a pointer to the value of node `i`
|
||||||
constexpr const value_t* operator[](size_t i) const {
|
constexpr const value_t& operator[](size_t i) const {
|
||||||
if (i >= _table.size()) {
|
assertd(i < _table.size(), "Index out of bounds.");
|
||||||
return nullptr;
|
return _table[i].value;
|
||||||
}
|
|
||||||
return _table[i] ? &*_table[i] : nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
@@ -458,14 +441,16 @@ public:
|
|||||||
size_t new_root = child(sub, not dir);
|
size_t new_root = child(sub, not dir);
|
||||||
size_t new_child = child(new_root, dir);
|
size_t new_child = child(new_root, dir);
|
||||||
|
|
||||||
child(sub, not dir) = new_child;
|
_child(sub, not dir) = new_child;
|
||||||
parent(new_child) = sub;
|
if (new_child != npos) {
|
||||||
child(new_root, dir) = sub;
|
_parent(new_child) = sub;
|
||||||
|
}
|
||||||
|
_child(new_root, dir) = sub;
|
||||||
|
|
||||||
parent(new_root) = sub_parent;
|
_parent(new_root) = sub_parent;
|
||||||
parent(sub) = new_root;
|
_parent(sub) = new_root;
|
||||||
if (sub_parent != npos) {
|
if (sub_parent != npos) {
|
||||||
child(sub_parent, sub == right(sub_parent)) = new_root;
|
_child(sub_parent, sub == right(sub_parent)) = new_root;
|
||||||
} else {
|
} else {
|
||||||
_root = new_root;
|
_root = new_root;
|
||||||
}
|
}
|
||||||
@@ -516,13 +501,13 @@ public:
|
|||||||
/// \param visit The visiting object
|
/// \param visit The visiting object
|
||||||
/// \param i The node to start at
|
/// \param i The node to start at
|
||||||
template<typename OrderT, typename VisitorT>
|
template<typename OrderT, typename VisitorT>
|
||||||
constexpr void traverse(VisitorT&& visit, size_t i = root) {
|
constexpr void traverse(VisitorT&& visit, size_t i) {
|
||||||
OrderT order;
|
OrderT order;
|
||||||
i = order(*this, i);
|
i = order(*this, i);
|
||||||
while (i != npos) {
|
while (i != npos) {
|
||||||
uint8_t mode = traversal_control_continue;
|
uint8_t mode = traversal_control_continue;
|
||||||
if (_table[i].value) {
|
if (_table[i].value) {
|
||||||
mode = visit(*_table[i].value, i);
|
mode = visit(_table[i].value, i);
|
||||||
}
|
}
|
||||||
if (mode == traversal_control_break) {
|
if (mode == traversal_control_break) {
|
||||||
break;
|
break;
|
||||||
@@ -581,7 +566,7 @@ public:
|
|||||||
return npos;
|
return npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t nxt = tree.sibling(node);
|
size_t nxt = tree.right(tree.parent(node));
|
||||||
size_t chd = tree.left(node);
|
size_t chd = tree.left(node);
|
||||||
nxt = node == nxt ? npos : nxt;
|
nxt = node == nxt ? npos : nxt;
|
||||||
|
|
||||||
@@ -618,19 +603,16 @@ public:
|
|||||||
return npos;
|
return npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t prnt = tree.parent(node);
|
size_t parent = tree.parent(node);
|
||||||
size_t next = tree.sibling(node);
|
size_t pright = tree.right(parent);
|
||||||
next = node == next ? npos : next;
|
size_t next = tree.left_most(tree.right(node));
|
||||||
|
|
||||||
|
if (node != pright && parent != npos) {
|
||||||
|
visit.push_front(parent);
|
||||||
|
}
|
||||||
|
|
||||||
if (node != head) {
|
|
||||||
if (tree.left(prnt) == node) {
|
|
||||||
visit.push_back(prnt);
|
|
||||||
if (next != npos) {
|
if (next != npos) {
|
||||||
visit.push_back(tree.left_most(next));
|
visit.push_front(next);
|
||||||
}
|
|
||||||
} else if (next != npos) {
|
|
||||||
visit.push_front(tree.left_most(next));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not visit.empty()) {
|
if (not visit.empty()) {
|
||||||
@@ -648,9 +630,23 @@ public:
|
|||||||
list<size_t> visit;
|
list<size_t> visit;
|
||||||
size_t head;
|
size_t head;
|
||||||
|
|
||||||
|
constexpr size_t successor(const bintree& tree, size_t n) {
|
||||||
|
size_t s = tree.left_most(n);
|
||||||
|
while (n == s) {
|
||||||
|
size_t r = tree.right(n);
|
||||||
|
if (r != npos) {
|
||||||
|
n = r;
|
||||||
|
s = tree.left_most(n);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s == npos ? n : s;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr size_t operator()(const bintree& tree, size_t start) {
|
constexpr size_t operator()(const bintree& tree, size_t start) {
|
||||||
head = start;
|
head = start;
|
||||||
return tree.left_most(start);
|
return this->successor(tree, start);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
|
constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
|
||||||
@@ -658,16 +654,15 @@ public:
|
|||||||
return npos;
|
return npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t prnt = tree.parent(node);
|
size_t parent = tree.parent(node);
|
||||||
size_t next = tree.sibling(node);
|
size_t pright = tree.right(parent);
|
||||||
next = node == next ? npos : next;
|
|
||||||
|
|
||||||
if (node != head) {
|
if (node == pright) {
|
||||||
if (next != npos) {
|
if (parent != npos) {
|
||||||
visit.push_front(tree.left_most(next));
|
visit.push_front(parent);
|
||||||
} else {
|
|
||||||
visit.push_front(prnt);
|
|
||||||
}
|
}
|
||||||
|
} else if (pright != npos) {
|
||||||
|
visit.push_front(this->successor(tree, pright));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not visit.empty()) {
|
if (not visit.empty()) {
|
||||||
@@ -677,6 +672,7 @@ public:
|
|||||||
node = npos;
|
node = npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -690,6 +686,12 @@ public:
|
|||||||
size_t _n;
|
size_t _n;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
constexpr iterator(bintree* tree, size_t root)
|
||||||
|
: _tree(tree)
|
||||||
|
, _order()
|
||||||
|
, _n(_order(*tree, root)) {
|
||||||
|
}
|
||||||
|
|
||||||
constexpr iterator(bintree* tree, size_t root, size_t node)
|
constexpr iterator(bintree* tree, size_t root, size_t node)
|
||||||
: _tree(tree)
|
: _tree(tree)
|
||||||
, _order()
|
, _order()
|
||||||
@@ -706,19 +708,19 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
value_t& operator*() {
|
value_t& operator*() {
|
||||||
return _tree[_n];
|
return (*_tree)[_n];
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t* operator->() {
|
value_t* operator->() {
|
||||||
return &_tree[_n];
|
return &(*_tree)[_n];
|
||||||
}
|
}
|
||||||
|
|
||||||
const value_t& operator*() const {
|
const value_t& operator*() const {
|
||||||
return _tree[_n];
|
return (*_tree)[_n];
|
||||||
}
|
}
|
||||||
|
|
||||||
const value_t* operator->() const {
|
const value_t* operator->() const {
|
||||||
return &_tree[_n];
|
return &(*_tree)[_n];
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool operator==(const iterator& it) {
|
constexpr bool operator==(const iterator& it) {
|
||||||
@@ -754,7 +756,8 @@ protected:
|
|||||||
|
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr size_t _insert_left(size_t p, ArgsT&&...args) {
|
constexpr size_t _insert_left(size_t p, ArgsT&&...args) {
|
||||||
size_t i = p == npos ? _root : left(p);
|
size_t i = p >= capacity() ? npos : p;
|
||||||
|
i = i == npos ? _root : _left(i);
|
||||||
if (i != npos) {
|
if (i != npos) {
|
||||||
_table[i].value = value_t(fennec::forward<ArgsT>(args)...);
|
_table[i].value = value_t(fennec::forward<ArgsT>(args)...);
|
||||||
} else {
|
} else {
|
||||||
@@ -764,6 +767,9 @@ protected:
|
|||||||
d = depth(p) + 1;
|
d = depth(p) + 1;
|
||||||
_table[p].left = i;
|
_table[p].left = i;
|
||||||
}
|
}
|
||||||
|
if (_root == npos) {
|
||||||
|
_root = i;
|
||||||
|
}
|
||||||
fennec::construct(&_table[i], p, npos, npos, d, fennec::forward<ArgsT>(args)...);
|
fennec::construct(&_table[i], p, npos, npos, d, fennec::forward<ArgsT>(args)...);
|
||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
@@ -771,7 +777,7 @@ protected:
|
|||||||
|
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr size_t _insert_right(size_t p, ArgsT&&...args) {
|
constexpr size_t _insert_right(size_t p, ArgsT&&...args) {
|
||||||
size_t i = p == npos ? _root : right(p);
|
size_t i = p == npos ? _root : _right(p);
|
||||||
if (i != npos) {
|
if (i != npos) {
|
||||||
_table[i].value = value_t(fennec::forward<ArgsT>(args)...);
|
_table[i].value = value_t(fennec::forward<ArgsT>(args)...);
|
||||||
if (p == npos || _root == npos) {
|
if (p == npos || _root == npos) {
|
||||||
@@ -784,42 +790,41 @@ protected:
|
|||||||
d = depth(p) + 1;
|
d = depth(p) + 1;
|
||||||
_table[p].right = i;
|
_table[p].right = i;
|
||||||
}
|
}
|
||||||
|
if (_root == npos) {
|
||||||
|
_root = i;
|
||||||
|
}
|
||||||
fennec::construct(&_table[i], p, npos, npos, d, fennec::forward<ArgsT>(args)...);
|
fennec::construct(&_table[i], p, npos, npos, d, fennec::forward<ArgsT>(args)...);
|
||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
constexpr size_t& parent(size_t i) {
|
constexpr size_t& _parent(size_t i) {
|
||||||
return i >= _table.size() ? sink : _table[i].parent;
|
return _table[i].parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t& grandparent(size_t i) {
|
constexpr size_t& _grandparent(size_t i) {
|
||||||
return parent(parent(i));
|
return _parent(parent(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t& left(size_t i) {
|
constexpr size_t& _left(size_t i) {
|
||||||
return i >= _table.size() ? sink : _table[i].left;
|
return _table[i].left;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t& right(size_t i) {
|
constexpr size_t& _right(size_t i) {
|
||||||
return i >= _table.size() ? sink : _table[i].right;
|
return _table[i].right;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t& child(size_t i, bool dir) {
|
constexpr size_t& _child(size_t i, bool dir) {
|
||||||
return dir ? right(i) : left(i);
|
return dir ? _right(i) : _left(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t& sibling(size_t i) {
|
constexpr size_t& _sibling(size_t i) {
|
||||||
size_t p = parent(i);
|
size_t p = parent(i);
|
||||||
size_t& l = left(p);
|
size_t& l = _left(p);
|
||||||
size_t& r = right(p);
|
size_t& r = _right(p);
|
||||||
return i == l ? l : r;
|
return i == l ? l : r;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t& parsib(size_t i) {
|
|
||||||
return sibling(parent(i));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,7 +156,9 @@ public:
|
|||||||
|
|
||||||
// This constructor should not be invokable since moving is a single object operation and will cause undefined
|
// This constructor should not be invokable since moving is a single object operation and will cause undefined
|
||||||
// behaviour when moving to multiple elements
|
// behaviour when moving to multiple elements
|
||||||
constexpr dynarray(size_t n, TypeT&&) = delete;
|
constexpr dynarray(size_t n, TypeT&& val)
|
||||||
|
: dynarray(n, fennec::copy(val)) {
|
||||||
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Emplace Constructor
|
/// \brief Emplace Constructor
|
||||||
@@ -432,6 +434,18 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Resize the dynarray, invoking the copy constructor for all new elements
|
||||||
|
/// \param n The new size in elements
|
||||||
|
/// \param val The value to fill with
|
||||||
|
constexpr void resize(size_t n, const TypeT& val) {
|
||||||
|
_alloc.creallocate(n);
|
||||||
|
|
||||||
|
while (_size < n) {
|
||||||
|
emplace_back(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Clears the contents of the dynarray, destructing all elements and releasing the allocation.
|
/// \brief Clears the contents of the dynarray, destructing all elements and releasing the allocation.
|
||||||
constexpr void clear() {
|
constexpr void clear() {
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ struct object_pool {
|
|||||||
// Definitions =========================================================================================================
|
// Definitions =========================================================================================================
|
||||||
public:
|
public:
|
||||||
using value_t = TypeT;
|
using value_t = TypeT;
|
||||||
using elem_t = optional<TypeT>;
|
using table_t = allocation<value_t, AllocT>;
|
||||||
using table_t = dynarray<elem_t, AllocT>;
|
using freed_t = list<size_t, AllocT>;
|
||||||
|
|
||||||
|
|
||||||
// Constructors & Destructor ===========================================================================================
|
// Constructors & Destructor ===========================================================================================
|
||||||
@@ -119,8 +119,7 @@ public:
|
|||||||
/// \returns a reference to the object with id `i`
|
/// \returns a reference to the object with id `i`
|
||||||
constexpr value_t& operator[](size_t i) {
|
constexpr value_t& operator[](size_t i) {
|
||||||
assert(i < capacity(), "Index out of Bounds!");
|
assert(i < capacity(), "Index out of Bounds!");
|
||||||
assert(_table[i], "Attempted to access Null Object.");
|
return _table[i];
|
||||||
return *_table[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -129,8 +128,7 @@ public:
|
|||||||
/// \returns a const-qualified reference to the object with id `i`
|
/// \returns a const-qualified reference to the object with id `i`
|
||||||
constexpr const value_t& operator[](size_t i) const {
|
constexpr const value_t& operator[](size_t i) const {
|
||||||
assert(i < capacity(), "Index out of Bounds!");
|
assert(i < capacity(), "Index out of Bounds!");
|
||||||
assert(_table[i], "Attempted to access Null Object.");
|
return _table[i];
|
||||||
return *_table[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
@@ -171,16 +169,23 @@ public:
|
|||||||
/// \brief Erase an object from the pool
|
/// \brief Erase an object from the pool
|
||||||
/// \param i The id of the object
|
/// \param i The id of the object
|
||||||
constexpr void erase(size_t i) {
|
constexpr void erase(size_t i) {
|
||||||
_table[i] = nullopt;
|
fennec::destruct(&_table[i]);
|
||||||
_freed.push_back(i);
|
_freed.push_back(i);
|
||||||
--_size;
|
--_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Clear the object pool
|
||||||
|
constexpr void clear() {
|
||||||
|
dynarray<bool> free(capacity(), false);
|
||||||
|
_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
dynarray<elem_t, AllocT> _table;
|
table_t _table;
|
||||||
list<size_t> _freed;
|
freed_t _freed;
|
||||||
size_t _size;
|
size_t _size;
|
||||||
|
|
||||||
size_t _next_free() {
|
size_t _next_free() {
|
||||||
@@ -196,10 +201,10 @@ private:
|
|||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
size_t _insert(ArgsT&&...args) {
|
size_t _insert(ArgsT&&...args) {
|
||||||
size_t i = _next_free();
|
size_t i = _next_free();
|
||||||
if (i >= _table.size()) {
|
if (i >= _table.capacity()) {
|
||||||
_table.emplace_back();
|
_table.creallocate(fennec::max(_table.size() * 2, size_t(8)));
|
||||||
}
|
}
|
||||||
_table[i].emplace(fennec::forward<ArgsT>(args)...);
|
fennec::construct(&_table[i], fennec::forward<ArgsT>(args)...);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,28 +36,155 @@
|
|||||||
#include <fennec/lang/types.h>
|
#include <fennec/lang/types.h>
|
||||||
#include <fennec/memory/allocator.h>
|
#include <fennec/memory/allocator.h>
|
||||||
|
|
||||||
|
// Binary heaps are just kinda busted.
|
||||||
|
// In-array binary heaps are one of the most efficient data structures for computers
|
||||||
|
// -> Cache Locality
|
||||||
|
// -> log(n) runtime
|
||||||
|
// -> No auxiliary structures or constant runtimes
|
||||||
|
// -> Only needs an extra byte for color
|
||||||
|
//
|
||||||
|
// I tried just about every heap under the sun
|
||||||
|
// -> strict fibonacci heap, got blown out of the water by std::priority_queue
|
||||||
|
// -> fibonacci heap, got blown out of the water by std::priority_queue
|
||||||
|
// -> binomial heap, on-par with std::set, blown out of the water by std::priority_queue
|
||||||
|
//
|
||||||
|
// Then I relented and fell back to ye old binary heap
|
||||||
|
// This implementation roughly matches gcc's std::priority_queue
|
||||||
|
|
||||||
namespace fennec
|
namespace fennec
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename ValueT, class CompareT = less<ValueT>, class AllocT = allocator<ValueT>>
|
template<typename ValueT, class CompareT = less<ValueT>, class AllocT = allocator<ValueT>>
|
||||||
struct priority_queue {
|
struct priority_queue {
|
||||||
|
|
||||||
|
// Definitions & Constants =============================================================================================
|
||||||
public:
|
public:
|
||||||
using value_t = ValueT;
|
using value_t = ValueT;
|
||||||
using compare_t = CompareT;
|
using compare_t = CompareT;
|
||||||
using alloc_t = AllocT;
|
using alloc_t = allocation<value_t, AllocT>;
|
||||||
|
|
||||||
|
static constexpr size_t npos = -1;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct node {
|
constexpr size_t left(size_t n) const {
|
||||||
size_t parent, child;
|
n = n * 2 + 1;
|
||||||
size_t left, right;
|
return n >= _size ? npos : n;
|
||||||
int degree;
|
}
|
||||||
value_t key;
|
|
||||||
};
|
|
||||||
|
|
||||||
using table_t = object_pool<node>;
|
constexpr size_t right(size_t n) const {
|
||||||
|
n = n * 2 + 2;
|
||||||
|
return n >= _size ? npos : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t parent(size_t n) const {
|
||||||
|
return n == 0 ? npos : (n - 1) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Constructors & Destructor ===========================================================================================
|
||||||
public:
|
public:
|
||||||
table_t _table;
|
constexpr priority_queue()
|
||||||
|
: _size(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ~priority_queue() {
|
||||||
|
while (_size > 0) {
|
||||||
|
--_size;
|
||||||
|
fennec::destruct(&_table[_size]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Properties ==========================================================================================================
|
||||||
|
|
||||||
|
constexpr size_t size() const {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t capacity() const {
|
||||||
|
return _table.capacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool empty() const {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Access ==============================================================================================================
|
||||||
|
|
||||||
|
constexpr const value_t& front() const {
|
||||||
|
return _table[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Modifiers ===========================================================================================================
|
||||||
|
|
||||||
|
constexpr void push(const value_t& key) {
|
||||||
|
this->_insert(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void push(value_t&& key) {
|
||||||
|
this->_insert(fennec::forward<value_t>(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename...ArgsT>
|
||||||
|
constexpr void emplace(ArgsT&&...args) {
|
||||||
|
this->_insert(fennec::forward<ArgsT>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void pop() {
|
||||||
|
fennec::swap(_table[0], _table[--_size]);
|
||||||
|
fennec::destruct(&_table[_size]);
|
||||||
|
_fix_erase(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Members =============================================================================================================
|
||||||
|
private:
|
||||||
|
compare_t _compare;
|
||||||
|
alloc_t _table;
|
||||||
|
size_t _size;
|
||||||
|
|
||||||
|
// Helpers =============================================================================================================
|
||||||
|
|
||||||
|
template<typename...ArgsT>
|
||||||
|
constexpr void _insert(ArgsT&&...args) {
|
||||||
|
if (_size == _table.capacity()) {
|
||||||
|
_expand();
|
||||||
|
}
|
||||||
|
fennec::construct(&_table[_size], fennec::forward<ArgsT>(args)...);
|
||||||
|
_fix_insert(_size++);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void _expand() {
|
||||||
|
_table.reallocate((_table.capacity() + 1) * 2 - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t _min(size_t a, size_t b) {
|
||||||
|
if (a == npos) { return b; }
|
||||||
|
if (b == npos) { return a; }
|
||||||
|
return _compare(_table[a], _table[b]) ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _fix_insert(size_t n) {
|
||||||
|
size_t p = parent(n);
|
||||||
|
while (p != npos && _compare(_table[n], _table[p])) {
|
||||||
|
fennec::swap(_table[n], _table[p]);
|
||||||
|
n = p;
|
||||||
|
p = parent(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _fix_erase(size_t n) {
|
||||||
|
size_t c = _min(left(n), right(n));
|
||||||
|
while (n != npos && c != npos && _compare(_table[c], _table[n])) {
|
||||||
|
fennec::swap(_table[c], _table[n]);
|
||||||
|
n = c;
|
||||||
|
c = _min(left(n), right(n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,21 @@
|
|||||||
#include <fennec/lang/compare.h>
|
#include <fennec/lang/compare.h>
|
||||||
#include <fennec/memory/allocator.h>
|
#include <fennec/memory/allocator.h>
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
|
||||||
|
// https://www.geeksforgeeks.org/dsa/insertion-in-red-black-tree/
|
||||||
|
|
||||||
|
// Uncertain how I managed to do this, but this data structure has
|
||||||
|
// A 50%-100% performance increase over std::set when running Dijkstra's
|
||||||
|
//
|
||||||
|
// Guesses:
|
||||||
|
// -> I likely make some assumptions that std::set doesn't
|
||||||
|
// -> Cache locality
|
||||||
|
// -> Simplified rotation and coloring logic
|
||||||
|
//
|
||||||
|
// Some of the implementations I have seen have multiple levels
|
||||||
|
// of if statements based on directionality which causes branching.
|
||||||
|
// I use const-expressions that reduce down to cmov instructions
|
||||||
|
|
||||||
namespace fennec
|
namespace fennec
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -97,7 +112,6 @@ protected:
|
|||||||
using base_t::parent;
|
using base_t::parent;
|
||||||
using base_t::grandparent;
|
using base_t::grandparent;
|
||||||
using base_t::sibling;
|
using base_t::sibling;
|
||||||
using base_t::parsib;
|
|
||||||
|
|
||||||
using base_t::left_most;
|
using base_t::left_most;
|
||||||
using base_t::right_most;
|
using base_t::right_most;
|
||||||
@@ -224,7 +238,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
constexpr void erase(const value_t& val) {
|
constexpr void erase(const value_t& val) {
|
||||||
_erase_bst(val);
|
_erase(find(val).index());
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -246,38 +260,39 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \returns An iterator at the smallest element in the sequence
|
/// \returns An iterator at the smallest element in the sequence
|
||||||
constexpr sequence::iterator begin() {
|
constexpr iterator begin() {
|
||||||
return sequence::iterator(this, _root, _root);
|
return sequence::iterator(this, _root);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns An iterator after the largest element in the sequence
|
/// \returns An iterator after the largest element in the sequence
|
||||||
constexpr sequence::iterator end() {
|
constexpr iterator end() {
|
||||||
return sequence::iterator(this, _root, npos);
|
return sequence::iterator(this, _root, npos);
|
||||||
}
|
}
|
||||||
|
|
||||||
class iterator : public base_t::iterator {
|
class iterator : public base_t::iterator {
|
||||||
protected:
|
protected:
|
||||||
using base_t::iterator::_n;
|
using base_t::iterator::_n;
|
||||||
|
using base_t::iterator::_tree;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using base_t::iterator::iterator;
|
using base_t::iterator::iterator;
|
||||||
|
|
||||||
value_t& operator*() {
|
value_t& operator*() {
|
||||||
return _table[_n].value.second;
|
return base_t::iterator::operator*().first;
|
||||||
}
|
}
|
||||||
|
|
||||||
const value_t& operator*() const {
|
const value_t& operator*() const {
|
||||||
return _table[_n].value.second;
|
return base_t::iterator::operator*().first;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t* operator->() {
|
value_t* operator->() {
|
||||||
return &_table[_n].value.second;
|
return &base_t::iterator::operator*().first;
|
||||||
}
|
}
|
||||||
|
|
||||||
const value_t* operator->() const {
|
const value_t* operator->() const {
|
||||||
return &_table[_n].value.second;
|
return &base_t::iterator::operator*().firstf;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -290,26 +305,46 @@ protected:
|
|||||||
|
|
||||||
// Helpers =============================================================================================================
|
// Helpers =============================================================================================================
|
||||||
protected:
|
protected:
|
||||||
|
using base_t::_left;
|
||||||
|
using base_t::_right;
|
||||||
|
using base_t::_parent;
|
||||||
|
using base_t::_sibling;
|
||||||
|
using base_t::_child;
|
||||||
|
|
||||||
constexpr value_t& _value(size_t i) {
|
constexpr value_t& _value(size_t i) {
|
||||||
return i >= _table.capacity() ? value_sink : _table[i].value.first;
|
return _table[i].value.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr const value_t& _value(size_t i) const {
|
constexpr const value_t& _value(size_t i) const {
|
||||||
return i >= _table.capacity() ? value_sink : _table[i].value.first;
|
return _table[i].value.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool& _color(size_t i) {
|
constexpr bool& _color(size_t i) {
|
||||||
return i >= _table.capacity() ? color_sink = false : _table[i].value.second;
|
return _table[i].value.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool _color(size_t i) const {
|
constexpr bool color(size_t i) const {
|
||||||
return i >= _table.capacity() ? color_sink = false : _table[i].value.second;
|
return i == npos ? false : _table[i].value.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr void _recolor(size_t n) {
|
||||||
|
bool c = !color(n);
|
||||||
|
if (n == _root) { // Only recolor if not the root node
|
||||||
|
_color(n) = c;
|
||||||
|
}
|
||||||
|
_color(left(n)) = !_color(left(n)) ;
|
||||||
|
_color(right(n)) = !_color(right(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
// run-of-the-mill bst insert
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr size_t _insert_bst(ArgsT&&...args) {
|
constexpr size_t _insert_bst(ArgsT&&...args) {
|
||||||
value_t val(fennec::forward<ArgsT>(args)...);
|
value_t val(fennec::forward<ArgsT>(args)...);
|
||||||
|
|
||||||
|
if (_root == npos) {
|
||||||
|
return _root = insert_left(npos, node_t(fennec::move(val), red));
|
||||||
|
}
|
||||||
|
|
||||||
size_t i = _root;
|
size_t i = _root;
|
||||||
size_t p = npos;
|
size_t p = npos;
|
||||||
while (i != npos) {
|
while (i != npos) {
|
||||||
@@ -324,10 +359,6 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_root == npos) {
|
|
||||||
return _root = insert_left(npos, node_t(fennec::move(val), red));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_compare(val, _value(p))) {
|
if (_compare(val, _value(p))) {
|
||||||
return insert_left(p, node_t(fennec::move(val), red));
|
return insert_left(p, node_t(fennec::move(val), red));
|
||||||
} else {
|
} else {
|
||||||
@@ -335,118 +366,232 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void _fix_insert(size_t x) {
|
// This makes some cheats given that the structure is modified only by internal functions
|
||||||
while (x != _root && _color(parent(x)) == red) {
|
// If such is the case, ONLY LL, LR, RL, and RR will show up
|
||||||
if (_color(parsib(x)) == red) {
|
// Then we just need to handle splitting a 4-node
|
||||||
_color(parent(x)) = black;
|
constexpr void _fix_insert(size_t n) {
|
||||||
_color(parsib(x)) = black;
|
size_t p = parent(n);
|
||||||
_color(grandparent(x)) = red;
|
while (color(p) != black) {
|
||||||
x = grandparent(x);
|
size_t g = parent(p);
|
||||||
} else if (parent(x) == left(grandparent(x))) {
|
size_t u = sibling(p);
|
||||||
if (x == right(parent(x))) {
|
size_t d = direction(n);
|
||||||
x = parent(x);
|
size_t r = direction(p);
|
||||||
rotate_left(x);
|
|
||||||
}
|
// Split 4 node
|
||||||
_color(parent(x)) = black;
|
if (color(u) == red) {
|
||||||
_color(grandparent(x)) = red;
|
_recolor(g);
|
||||||
rotate_right(grandparent(x));
|
n = p;
|
||||||
} else {
|
p = g;
|
||||||
if (x == left(parent(x))) {
|
continue;
|
||||||
x = parent(x);
|
|
||||||
rotate_right(x);
|
|
||||||
}
|
|
||||||
_color(parent(x)) = black;
|
|
||||||
_color(grandparent(x)) = red;
|
|
||||||
rotate_left(grandparent(x));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_color(_root) = black;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void _shift(size_t u, size_t v) {
|
// LR & RL case
|
||||||
if (parent(u) == npos) {
|
if (d != r) {
|
||||||
|
rotate(p, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LL & RR case
|
||||||
|
rotate(g, not r);
|
||||||
|
n = parent(n);
|
||||||
|
p = parent(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void _transplant(size_t u, size_t v) {
|
||||||
|
size_t p = parent(u);
|
||||||
|
if (p == npos) {
|
||||||
_root = v;
|
_root = v;
|
||||||
|
} else if (u == left(p)) {
|
||||||
|
_left(p) = v;
|
||||||
} else {
|
} else {
|
||||||
child(parent(u), direction(u)) = v;
|
_right(p) = v;
|
||||||
}
|
|
||||||
if (v != npos) {
|
|
||||||
parent(v) = parent(u);
|
|
||||||
}
|
}
|
||||||
|
_parent(v) = _parent(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void _erase_bst(const value_t& val) {
|
constexpr void _swap_val(size_t a, size_t b) {
|
||||||
size_t z = find(val).index();
|
fennec::swap(_value(a), _value(b));
|
||||||
size_t y = z;
|
}
|
||||||
size_t x = npos;
|
|
||||||
bool c = _color(y);
|
|
||||||
size_t p = npos;
|
|
||||||
|
|
||||||
if (left(z) == npos) {
|
constexpr size_t _replace(size_t x) {
|
||||||
x = right(z);
|
size_t l = left(x);
|
||||||
p = parent(z);
|
size_t r = right(x);
|
||||||
_shift(z, x);
|
|
||||||
} else if (right(z) == npos) {
|
// Both are null
|
||||||
x = left(z);
|
if (l == r) {
|
||||||
p = parent(z);
|
return npos;
|
||||||
_shift(z, x);
|
}
|
||||||
|
|
||||||
|
if (l == npos) {
|
||||||
|
return r;
|
||||||
|
} else if (r == npos) {
|
||||||
|
return l;
|
||||||
} else {
|
} else {
|
||||||
y = left_most(right(z));
|
return left_most(right(x));
|
||||||
c = _color(y);
|
|
||||||
x = right(y);
|
|
||||||
p = (parent(y) == z) ? y : parent(y);
|
|
||||||
if (parent(y) != z) {
|
|
||||||
_shift(y, right(y));
|
|
||||||
right(y) = right(z);
|
|
||||||
parent(right(y)) = y;
|
|
||||||
}
|
|
||||||
_shift(z, y);
|
|
||||||
left(y) = left(z);
|
|
||||||
if (left(y))
|
|
||||||
parent(left(y)) = y;
|
|
||||||
_color(y) = _color(z);
|
|
||||||
}
|
|
||||||
|
|
||||||
fennec::destruct(&_table[z]);
|
|
||||||
--_size;
|
|
||||||
|
|
||||||
if (c == black) {
|
|
||||||
_fix_erase(x, p);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void _fix_erase(size_t x, size_t p) {
|
constexpr size_t _red_child(size_t x) {
|
||||||
while (x != _root && _color(x) == black) {
|
size_t l = left(x);
|
||||||
bool dir = direction(x);
|
size_t r = right(x);
|
||||||
size_t w = child(p, not dir);
|
|
||||||
|
|
||||||
if (_color(w) == red) {
|
if (color(l) == red) {
|
||||||
_color(w) = black;
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color(r) == red) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is an implementation based on the C code in
|
||||||
|
// the wikipedia article adapted to this framework
|
||||||
|
constexpr void _fix_erase(size_t n) {
|
||||||
|
size_t p = parent(n);
|
||||||
|
size_t s, sc, sf;
|
||||||
|
bool d = n == right(p);
|
||||||
|
|
||||||
|
_child(p, d) = npos;
|
||||||
|
|
||||||
|
goto start_balance;
|
||||||
|
|
||||||
|
do {
|
||||||
|
d = n == right(p);
|
||||||
|
start_balance:
|
||||||
|
s = child(p, !d);
|
||||||
|
sf = child(s, !d);
|
||||||
|
sc = child(s, d);
|
||||||
|
|
||||||
|
if (color(s) == red) {
|
||||||
|
// Case 3
|
||||||
|
rotate(p, d);
|
||||||
_color(p) = red;
|
_color(p) = red;
|
||||||
w = rotate(p, dir);
|
_color(s) = black;
|
||||||
|
|
||||||
|
// Fix pointers
|
||||||
|
s = sc;
|
||||||
|
sf = child(s, !d);
|
||||||
|
sc = child(s, d);
|
||||||
|
|
||||||
|
if (color(sf) == red) {
|
||||||
|
goto case_6;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (w == npos || (_color(left(w)) == black && _color(right(w)) == black)) {
|
if (color(sc) == red) {
|
||||||
_color(w) = red;
|
goto case_5;
|
||||||
x = p;
|
|
||||||
p = parent(x);
|
|
||||||
} else {
|
|
||||||
if (_color(child(w, not dir)) == black) {
|
|
||||||
_color(child(w, dir)) = black;
|
|
||||||
_color(w) = red;
|
|
||||||
rotate(w, not dir);
|
|
||||||
w = child(p, not dir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_color(w) = _color(p);
|
// Case 4
|
||||||
|
if (color(p) == red) {
|
||||||
|
if (s != npos) {
|
||||||
|
_color(s) = red;
|
||||||
|
}
|
||||||
_color(p) = black;
|
_color(p) = black;
|
||||||
_color(child(w, not dir)) = black;
|
return;
|
||||||
rotate(p, dir);
|
|
||||||
x = _root;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_color(x) = black;
|
if (color(sf) == red) {
|
||||||
|
goto case_6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color(sc) == red) {
|
||||||
|
goto case_5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 4
|
||||||
|
if (color(p) == red) {
|
||||||
|
if (s != npos) {
|
||||||
|
_color(s) = red;
|
||||||
|
}
|
||||||
|
_color(p) = black;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 1
|
||||||
|
if (p == npos) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 2
|
||||||
|
if (s != npos) {
|
||||||
|
_color(s) = red;
|
||||||
|
}
|
||||||
|
n = p;
|
||||||
|
} while ((p = parent(n)) != npos);
|
||||||
|
|
||||||
|
return; //
|
||||||
|
|
||||||
|
case_5:
|
||||||
|
rotate(s, !d);
|
||||||
|
_color(s) = red;
|
||||||
|
_color(sc) = black;
|
||||||
|
sf = s;
|
||||||
|
s = sc;
|
||||||
|
|
||||||
|
case_6:
|
||||||
|
rotate(p, d);
|
||||||
|
_color(s) = color(p);
|
||||||
|
_color(p) = black;
|
||||||
|
_color(sf) = black;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void _erase(size_t n) {
|
||||||
|
if (n == npos) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t l = left(n);
|
||||||
|
size_t r = right(n);
|
||||||
|
|
||||||
|
// 2 children
|
||||||
|
if (l != npos && r != npos) {
|
||||||
|
size_t s = left_most(r);
|
||||||
|
_swap_val(n, s);
|
||||||
|
n = s;
|
||||||
|
l = left(n);
|
||||||
|
r = right(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t p = parent(n);
|
||||||
|
bool d = n == right(p);
|
||||||
|
size_t c = l != npos ? l : r;
|
||||||
|
|
||||||
|
// Single child
|
||||||
|
if (c != npos) {
|
||||||
|
_parent(c) = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles root cases
|
||||||
|
if (p == npos) {
|
||||||
|
_root = c;
|
||||||
|
if (c == npos) {
|
||||||
|
fennec::destruct(&_table[n]);
|
||||||
|
_freed.push_back(n);
|
||||||
|
--_size;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
_color(c) = black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single Child, Red, and Root cases
|
||||||
|
if (p == npos || c != npos || color(n) == red) {
|
||||||
|
if (p != npos) {
|
||||||
|
_child(p, d) = c;
|
||||||
|
}
|
||||||
|
fennec::destruct(&_table[n]);
|
||||||
|
_freed.push_back(n);
|
||||||
|
--_size;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fix_erase(n);
|
||||||
|
fennec::destruct(&_table[n]);
|
||||||
|
_freed.push_back(n);
|
||||||
|
--_size;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -51,25 +51,25 @@
|
|||||||
#undef ULLONG_MIN
|
#undef ULLONG_MIN
|
||||||
#undef ULLONG_MAX
|
#undef ULLONG_MAX
|
||||||
|
|
||||||
#define CHAR_IS_SIGNED false
|
#define CHAR_IS_SIGNED true
|
||||||
#define CHAR_ROUNDS 0x0
|
#define CHAR_ROUNDS 0x0
|
||||||
#define CHAR_RADIX_DIG 0x8
|
#define CHAR_RADIX_DIG 0x7
|
||||||
#define CHAR_DIG 0x2
|
#define CHAR_DIG 0x2
|
||||||
#define CHAR_DECIMAL_DIG 0x0
|
#define CHAR_DECIMAL_DIG 0x0
|
||||||
#define CHAR_RADIX 0x2
|
#define CHAR_RADIX 0x2
|
||||||
#define CHAR_TRAPS 0xtrue
|
#define CHAR_TRAPS 0xtrue
|
||||||
#define CHAR_MIN 0x0
|
#define CHAR_MIN 0x80
|
||||||
#define CHAR_MAX 0xff
|
#define CHAR_MAX 0x7f
|
||||||
|
|
||||||
#define WCHAR_IS_SIGNED false
|
#define WCHAR_IS_SIGNED true
|
||||||
#define WCHAR_ROUNDS 0x0
|
#define WCHAR_ROUNDS 0x0
|
||||||
#define WCHAR_RADIX_DIG 0x20
|
#define WCHAR_RADIX_DIG 0x1f
|
||||||
#define WCHAR_DIG 0x9
|
#define WCHAR_DIG 0x9
|
||||||
#define WCHAR_DECIMAL_DIG 0x0
|
#define WCHAR_DECIMAL_DIG 0x0
|
||||||
#define WCHAR_RADIX 0x2
|
#define WCHAR_RADIX 0x2
|
||||||
#define WCHAR_TRAPS 0xtrue
|
#define WCHAR_TRAPS 0xtrue
|
||||||
#define WCHAR_MIN 0x0
|
#define WCHAR_MIN 0x80000000
|
||||||
#define WCHAR_MAX 0xffffffff
|
#define WCHAR_MAX 0x7fffffff
|
||||||
|
|
||||||
#define SCHAR_ROUNDS 0x0
|
#define SCHAR_ROUNDS 0x0
|
||||||
#define SCHAR_RADIX_DIG 0x7
|
#define SCHAR_RADIX_DIG 0x7
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ add_executable(fennec-test
|
|||||||
tests/containers/performance/test_iterator_visitor.h
|
tests/containers/performance/test_iterator_visitor.h
|
||||||
tests/containers/test_sequence.h
|
tests/containers/test_sequence.h
|
||||||
tests/langproc/test_format.h
|
tests/langproc/test_format.h
|
||||||
|
tests/containers/test_priority_queue.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}"
|
target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}"
|
||||||
|
|||||||
109
test/tests/containers/test_bintree.h
Normal file
109
test/tests/containers/test_bintree.h
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// =====================================================================================================================
|
||||||
|
// fennec, a free and open source game engine
|
||||||
|
// Copyright © 2025 Medusa Slockbower
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
// =====================================================================================================================
|
||||||
|
|
||||||
|
#ifndef FENNEC_TEST_CONTAINERS_BINTREE_H
|
||||||
|
#define FENNEC_TEST_CONTAINERS_BINTREE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "../../test.h"
|
||||||
|
|
||||||
|
#include <fennec/containers/bintree.h>
|
||||||
|
|
||||||
|
namespace fennec
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace test
|
||||||
|
{
|
||||||
|
|
||||||
|
inline void fennec_test_containers_bintree() {
|
||||||
|
using tree_t = bintree<size_t>;
|
||||||
|
|
||||||
|
tree_t test;
|
||||||
|
constexpr size_t npos = tree_t::npos;
|
||||||
|
constexpr size_t pre_order [] = { 1, 2, 4, 5, 3, 6 };
|
||||||
|
constexpr size_t in_order [] = { 4, 2, 5, 1, 3, 6 };
|
||||||
|
constexpr size_t post_order[] = { 4, 5, 2, 6, 3, 1 };
|
||||||
|
|
||||||
|
const size_t n = 50;
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
const size_t parent = rand() % max(test.size(), size_t(1));
|
||||||
|
const bool side = rand() % 2;
|
||||||
|
size_t l = side ? test.insert_right(parent, i) : test.insert_left(parent, i);
|
||||||
|
assertf(test.parent(l) == parent, "Tree Construct Test Failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
test.clear();
|
||||||
|
|
||||||
|
fennec_test_run(test.empty(), true);
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
size_t n1 = test.insert_left(npos, 1);
|
||||||
|
size_t n2 = test.insert_left(n1, 2);
|
||||||
|
size_t n3 = test.insert_right(n1, 3);
|
||||||
|
size_t n4 = test.insert_left(n2, 4);
|
||||||
|
size_t n5 = test.insert_right(n2, 5);
|
||||||
|
size_t n6 = test.insert_right(n3, 6);
|
||||||
|
fennec_test_run(n1 != npos, true);
|
||||||
|
fennec_test_run(n2 != npos, true);
|
||||||
|
fennec_test_run(n3 != npos, true);
|
||||||
|
fennec_test_run(n4 != npos, true);
|
||||||
|
fennec_test_run(n5 != npos, true);
|
||||||
|
fennec_test_run(n6 != npos, true);
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
test.traverse<tree_t::pre_order>([&](size_t x, size_t) -> uint8_t {
|
||||||
|
fennec_test_run(x, pre_order[i++]);
|
||||||
|
return traversal_control_continue;
|
||||||
|
}, test.root());
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
test.traverse<tree_t::in_order>([&](size_t x, size_t) -> uint8_t {
|
||||||
|
fennec_test_run(x, in_order[i++]);
|
||||||
|
return traversal_control_continue;
|
||||||
|
}, test.root());
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
test.traverse<tree_t::post_order>([&](size_t x, size_t) -> uint8_t {
|
||||||
|
fennec_test_run(x, post_order[i++]);
|
||||||
|
return traversal_control_continue;
|
||||||
|
}, test.root());
|
||||||
|
|
||||||
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
|
test.clear();
|
||||||
|
|
||||||
|
fennec_test_run(test.empty(), true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FENNEC_TEST_CONTAINERS_RDTREE_H
|
||||||
56
test/tests/containers/test_priority_queue.h
Normal file
56
test/tests/containers/test_priority_queue.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// =====================================================================================================================
|
||||||
|
// fennec, a free and open source game engine
|
||||||
|
// Copyright © 2025 Medusa Slockbower
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
// =====================================================================================================================
|
||||||
|
|
||||||
|
#ifndef FENNEC_TEST_CONTAINERS_PRIORITY_QUEUE_H
|
||||||
|
#define FENNEC_TEST_CONTAINERS_PRIORITY_QUEUE_H
|
||||||
|
|
||||||
|
#include "../../test.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fennec/containers/priority_queue.h>
|
||||||
|
#include <fennec/containers/sequence.h>
|
||||||
|
|
||||||
|
namespace fennec::test
|
||||||
|
{
|
||||||
|
|
||||||
|
inline void fennec_test_containers_priority_queue() {
|
||||||
|
|
||||||
|
using type_t = decltype(rand());
|
||||||
|
sequence<type_t> ref;
|
||||||
|
priority_queue<type_t> test;
|
||||||
|
|
||||||
|
size_t n = 50;
|
||||||
|
for (size_t i = 0; i < n; ++i) {
|
||||||
|
type_t v = rand();
|
||||||
|
test.push(v);
|
||||||
|
ref.insert(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
fennec_test_run(test.size(), n);
|
||||||
|
|
||||||
|
for (type_t x : ref) {
|
||||||
|
assert(x == test.front(), "Failed Priority Queue Test!");
|
||||||
|
test.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "passed" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FENNEC_TEST_CONTAINERS_PRIORITY_QUEUE_H
|
||||||
@@ -55,11 +55,6 @@ inline void fennec_test_containers_rdtree() {
|
|||||||
|
|
||||||
fennec_test_spacer(1);
|
fennec_test_spacer(1);
|
||||||
|
|
||||||
test.traverse<tree_t::pre_order>([](size_t i, size_t n) -> uint8_t {
|
|
||||||
assertf(i + 1 == n, "Tree Traverse Test Failed");
|
|
||||||
return traversal_control_continue;
|
|
||||||
});
|
|
||||||
|
|
||||||
test.erase(0);
|
test.erase(0);
|
||||||
|
|
||||||
fennec_test_run(test.empty(), true);
|
fennec_test_run(test.empty(), true);
|
||||||
|
|||||||
@@ -39,17 +39,29 @@ namespace test
|
|||||||
{
|
{
|
||||||
|
|
||||||
inline void fennec_test_containers_sequence() {
|
inline void fennec_test_containers_sequence() {
|
||||||
dynarray<size_t> ref;
|
using type_t = decltype(rand());
|
||||||
sequence<size_t> test;
|
dynarray<type_t> ref;
|
||||||
|
sequence<type_t> test;
|
||||||
const size_t n = 50;
|
const size_t n = 50;
|
||||||
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
size_t v = rand();
|
type_t v = rand();
|
||||||
ref.push_back(v);
|
ref.push_back(v);
|
||||||
test.insert(v);
|
test.insert(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t v : ref) {
|
fennec_test_run(test.size(), n);
|
||||||
|
|
||||||
|
type_t p = -1;
|
||||||
|
size_t c = 0;
|
||||||
|
for (type_t x : test) {
|
||||||
|
assertf(x > p, "Failed Sequence Test!");
|
||||||
|
p = x;
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
fennec_test_run(c, n);
|
||||||
|
|
||||||
|
for (type_t v : ref) {
|
||||||
assertf(test.contains(v), "Failed Sequence Test!");
|
assertf(test.contains(v), "Failed Sequence Test!");
|
||||||
test.erase(v);
|
test.erase(v);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#define FENNEC_TEST_CONTAINERS_H
|
#define FENNEC_TEST_CONTAINERS_H
|
||||||
|
|
||||||
#include "containers/test_array.h"
|
#include "containers/test_array.h"
|
||||||
|
#include "containers/test_bintree.h"
|
||||||
#include "containers/test_deque.h"
|
#include "containers/test_deque.h"
|
||||||
#include "containers/test_dynarray.h"
|
#include "containers/test_dynarray.h"
|
||||||
#include "containers/test_graph.h"
|
#include "containers/test_graph.h"
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
#include "containers/test_map.h"
|
#include "containers/test_map.h"
|
||||||
#include "containers/test_object_pool.h"
|
#include "containers/test_object_pool.h"
|
||||||
#include "containers/test_optional.h"
|
#include "containers/test_optional.h"
|
||||||
|
#include "containers/test_priority_queue.h"
|
||||||
#include "containers/test_rdtree.h"
|
#include "containers/test_rdtree.h"
|
||||||
#include "containers/test_sequence.h"
|
#include "containers/test_sequence.h"
|
||||||
#include "containers/test_set.h"
|
#include "containers/test_set.h"
|
||||||
@@ -72,11 +74,21 @@ namespace fennec::test
|
|||||||
fennec_test_containers_set();
|
fennec_test_containers_set();
|
||||||
fennec_test_spacer(3);
|
fennec_test_spacer(3);
|
||||||
|
|
||||||
|
fennec_test_subheader("bintree tests");
|
||||||
|
fennec_test_spacer(2);
|
||||||
|
fennec_test_containers_bintree();
|
||||||
|
fennec_test_spacer(3);
|
||||||
|
|
||||||
fennec_test_subheader("sequence tests");
|
fennec_test_subheader("sequence tests");
|
||||||
fennec_test_spacer(2);
|
fennec_test_spacer(2);
|
||||||
fennec_test_containers_sequence();
|
fennec_test_containers_sequence();
|
||||||
fennec_test_spacer(3);
|
fennec_test_spacer(3);
|
||||||
|
|
||||||
|
fennec_test_subheader("priority queue tests");
|
||||||
|
fennec_test_spacer(2);
|
||||||
|
fennec_test_containers_priority_queue();
|
||||||
|
fennec_test_spacer(3);
|
||||||
|
|
||||||
fennec_test_subheader("map tests");
|
fennec_test_subheader("map tests");
|
||||||
fennec_test_spacer(2);
|
fennec_test_spacer(2);
|
||||||
fennec_test_containers_map();
|
fennec_test_containers_map();
|
||||||
|
|||||||
Reference in New Issue
Block a user