diff --git a/src/cxml/word2vec.h b/src/cxml/word2vec.h index c3d254c..20994b5 100644 --- a/src/cxml/word2vec.h +++ b/src/cxml/word2vec.h @@ -75,7 +75,7 @@ class Word2Vec { auto target = mat::unit_matrix(1, vocab_len, 0, i); net.train(in[i], target, epochs); } - in.print(); + in.bits_print(); } vec predict_next(int vocab_index) { mat in(1, vocab_len); diff --git a/src/cxstructs/PriorityQueue.h b/src/cxstructs/PriorityQueue.h index 6b57f27..fe4a583 100644 --- a/src/cxstructs/PriorityQueue.h +++ b/src/cxstructs/PriorityQueue.h @@ -22,9 +22,11 @@ #define CXSTRUCTS_SRC_CXSTRUCTS_PRIORITYQUEUE_H_ #include "../cxconfig.h" +#include // For std::allocator #ifdef CX_INCLUDE_TESTS #include #endif + namespace cxstructs { /** @@ -103,7 +105,7 @@ class PriorityQueue { public: /** - * Per default is a min heap. Pass std::greater<> as comparator to get a max-heap + * Per default is a min heap. Pass std::greater<> as comparator to bits_get a max-heap * @param len initial length and expansion factor */ inline explicit PriorityQueue(uint_32_cx len = 32) @@ -241,16 +243,7 @@ class PriorityQueue { CX_WARNING(len_ > size_ * 1.5, "calling shrink_to_fit for no reason"); shrink(); } - friend std::ostream& operator<<(std::ostream& os, const PriorityQueue& q) { - if (q.size() == 0) { - return os << "[]"; - } - os << "[" << q.arr_[0]; - for (uint_32_cx i = 1; i < q.size_; i++) { - os << "," << q.arr_[i]; - } - return os << "]"; - } + class Iterator { T* ptr; diff --git a/src/cxstructs/QuadTree.h b/src/cxstructs/QuadTree.h index ac0871b..1303f94 100644 --- a/src/cxstructs/QuadTree.h +++ b/src/cxstructs/QuadTree.h @@ -21,11 +21,11 @@ #ifndef CXSTRUCTS_SRC_DATASTRUCTURES_QUADTREE_H_ #define CXSTRUCTS_SRC_DATASTRUCTURES_QUADTREE_H_ -#include #include "../cxconfig.h" #include "Geometry.h" #include "vec.h" +#include //used in kNN 2D @@ -279,7 +279,7 @@ class QuadTree { }; /** * Actively iterates down the tree summing all subtree sizes

- * This can get slow on large trees + * This can bits_get slow on large trees * @return the max depth of the tree */ [[nodiscard]] inline uint_32_cx size() const { @@ -289,7 +289,7 @@ class QuadTree { } /** * Actively iterates down the tree for its max depth

- * This can get slow on large trees + * This can bits_get slow on large trees * @return the max depth of the tree */ [[nodiscard]] inline uint_16_cx depth() const { diff --git a/src/cxstructs/Queue.h b/src/cxstructs/Queue.h index dba5ec7..51c8cbc 100644 --- a/src/cxstructs/Queue.h +++ b/src/cxstructs/Queue.h @@ -22,6 +22,7 @@ #define CXSTRUCTS_QUEUE_H #include "../cxconfig.h" +#include //For std::allocator #ifdef CX_INCLUDE_TESTS #include #endif @@ -279,7 +280,7 @@ class Queue { * Clears the queue of all elements */ inline void clear() { - if (!std::is_trivial_v) { + if constexpr (!std::is_trivial_v) { for (uint_32_cx i = 0; i < size_; i++) { std::allocator_traits::destroy(alloc, &arr_[i]); } @@ -301,16 +302,6 @@ class Queue { * @return */ [[nodiscard]] inline uint_32_cx capacity() const { return capacity_; } - friend std::ostream& operator<<(std::ostream& os, const Queue& q) { - if (q.size() == 0) { - return os << "[]"; - } - os << "[" << q.arr_[0]; - for (uint_32_cx i = q.front_ + 1; i < q.size_; i++) { - os << "," << q.arr_[i]; - } - return os << "]"; - } class Iterator { T* ptr; uint_32_cx current; diff --git a/src/cxstructs/Stack.h b/src/cxstructs/Stack.h index 6c24ff9..93e06f4 100644 --- a/src/cxstructs/Stack.h +++ b/src/cxstructs/Stack.h @@ -49,6 +49,7 @@ class Stack { using Allocator = typename std::conditional, std::allocator>::type; + Allocator alloc; T* arr_; uint_32_cx size_; diff --git a/src/cxstructs/StackArray.h b/src/cxstructs/StackArray.h index 3051290..b1ccf5a 100644 --- a/src/cxstructs/StackArray.h +++ b/src/cxstructs/StackArray.h @@ -24,10 +24,9 @@ #include "../cxconfig.h" #include // For std::forward_iterator_tag -template +namespace cxstructs { +template class StackArray { - static_assert(std::is_trivial_v, "StackArray only supports trivial types."); - using size_type = uint32_t; T data_[N]; // Stack-allocated array size_type size_; // Current number of elements in the array @@ -35,20 +34,71 @@ class StackArray { public: StackArray() : size_(0) {} StackArray(size_type elems) : size_(elems) {} - //Call can fail + StackArray(const StackArray&) = delete; + StackArray& operator=(const StackArray&) = delete; + StackArray(StackArray&& other) noexcept(std::is_nothrow_move_constructible_v) + : size_(other.size_) { + if constexpr (std::is_trivially_copyable_v) { + std::memcpy(data_, other.data_, other.size_ * sizeof(T)); + } else { + for (size_type i = 0; i < other.size_; ++i) { + new (data_ + i) T(std::move(other.data_[i])); + other.data_[i].~T(); + } + } + other.size_ = 0; + } + StackArray& operator=(StackArray&& other) noexcept(std::is_nothrow_move_assignable_v) { + if (this != &other) { + clear(); + size_ = other.size_; + if constexpr (std::is_trivially_copyable_v) { + std::memcpy(data_, other.data_, other.size_ * sizeof(T)); + } else { + for (size_type i = 0; i < other.size_; ++i) { + new (data_ + i) T(std::move(other.data_[i])); + other.data_[i].~T(); + } + } + other.size_ = 0; + } + return *this; + } + ~StackArray() { clear(); } + // Call can fail void push_back(const T& value) { CX_ASSERT(size_ < N, "Attempt to add to a full StackArray"); - data_[size_++] = value; + if constexpr (std::is_trivially_copyable_v) { + data_[size_++] = value; + } else { + new (data_ + size_) T(value); + ++size_; + } } - //Call cannot fail | Loops around and overwrites first elements and sets size to length from 0 + void push_back(T&& value) { + CX_ASSERT(size_ < N, "Attempt to add to a full StackArray"); + if constexpr (std::is_trivially_copyable_v) { + data_[size_++] = std::move(value); + } else { + new (data_ + size_) T(std::move(value)); + ++size_; + } + } + // Call cannot fail | Overwrites first elements and resets size on loop void push_back_loop(const T& value) { - data_[size_ % N] = value; + size_type index = size_ % N; + if constexpr (!std::is_trivially_copyable_v) { + if (size_ >= N) { + data_[index].~T(); + } + } + new (data_ + index) T(value); size_ = (size_ + 1) % N; } template void emplace_back(Args&&... args) { CX_ASSERT(size_ < N, "Attempt to add to a full StackArray"); - new (data_ + size_) T(std::forward(args)...); + new (data_ + size_) T(std::forward(args)...); // Direct construction with arguments ++size_; } T& operator[](size_type index) { @@ -59,7 +109,14 @@ class StackArray { CX_ASSERT(index < size_, "Index out of range"); return data_[index]; } - inline void clear() { size_ = 0; } + inline void clear() { + if constexpr (!std::is_trivially_destructible_v) { + for (size_type i = 0; i < size_; ++i) { + data_[i].~T(); // Call destructor for each constructed element + } + } + size_ = 0; + } [[nodiscard]] inline size_type size() const { return size_; } [[nodiscard]] bool empty() const { return size_ == 0; } [[nodiscard]] inline bool full() const { return size_ == N; } @@ -122,7 +179,8 @@ class StackArray { // Move elements after pos one position to the left std::move(posPtr + 1, &(*end()), posPtr); - // Destroy the last element (now a duplicate) if it's not trivially destructible + // Destroy the last element (now a duplicate) if it's not trivially + // destructible if constexpr (!std::is_trivially_destructible_v) { data_[size_ - 1].~T(); } @@ -150,5 +208,5 @@ class StackArray { } #endif }; - +} // namespace cxstructs #endif //CXSTRUCTS_SRC_CXSTRUCTS_STACKARRAY_H_ diff --git a/src/cxstructs/StackHashMap.h b/src/cxstructs/StackHashMap.h index a5fe6ea..d055b05 100644 --- a/src/cxstructs/StackHashMap.h +++ b/src/cxstructs/StackHashMap.h @@ -22,11 +22,12 @@ #define CXSTRUCTS_SRC_CXSTRUCTS_STACKHASHMAP_H_ #include "../cxconfig.h" -#include //For std::bitset and std::hash -#include // For std::forward_iterator_tag +#include //For std::bitset and std::hash //Memory footprint is still quite big //Using std::bitset<> saves memory but is a bit slower +//Use set_rand(1) if you have your own perfect hash function -> will be overridden on collision +namespace cxstructs { /** * StackHashMap is a hash map implemented entirely on the stack with an STL-like interface.
@@ -259,6 +260,7 @@ class StackHashMap { return register_[hash] && data_[hash].key == key; } + //The multiplier for the hash | Set to 1 if you supply a perfect hash function | Resets on collision inline void set_rand(int rand) noexcept { rand_ = rand; } inline void clear() noexcept { @@ -317,7 +319,6 @@ class StackHashMap { size_t size_; size_t index_; }; - class ValueIterator { public: using iterator_category = std::forward_iterator_tag; @@ -362,7 +363,6 @@ class StackHashMap { size_t size_; size_t index_; }; - class PairIterator { public: using iterator_category = std::forward_iterator_tag; @@ -470,8 +470,9 @@ class StackHashMap { myMap2.insert("hey", 100); myMap2.insert("blabla", 100); - printf("%d", myMap2["hey"]); + CX_ASSERT(myMap2["hey"] == 100, ""); } #endif }; +} // namespace cxstructs #endif //CXSTRUCTS_SRC_CXSTRUCTS_STACKHASHMAP_H_ diff --git a/src/cxstructs/vec.h b/src/cxstructs/vec.h index d7e61e5..f1bec22 100644 --- a/src/cxstructs/vec.h +++ b/src/cxstructs/vec.h @@ -467,7 +467,7 @@ class vec { size_ += endIndex - startIndex; } /** - * Attempts to print the complete list to std::cout + * Attempts to bits_print the complete list to std::cout * @param prefix optional prefix */ inline void print(const std::string& prefix = "") { diff --git a/src/cxutil/cxbits.h b/src/cxutil/cxbits.h new file mode 100644 index 0000000..81ee0d2 --- /dev/null +++ b/src/cxutil/cxbits.h @@ -0,0 +1,174 @@ +// Copyright (c) 2023 gk646 +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#define CX_FINISHED +#ifndef CXSTRUCTS_SRC_CXUTIL_CXBITS_H_ +#define CXSTRUCTS_SRC_CXUTIL_CXBITS_H_ + +#include "../cxconfig.h" +#include +#include //For type traits + +//Assumes little endian for all operations + +//// Integer Literals +//int: 42 +//unsigned int: 42U +//long: 42L +//unsigned long: 42UL +//long long: 42LL +//unsigned long long: 42ULL +//octal: 052 // Equivalent to decimal 42 +//hexadecimal: 0x2A // Equivalent to decimal 42 +//binary: 0b101010 // Equivalent to decimal 42 +// +//// Floating-Point Literals +//float: 3.14F +//double: 3.14 +//long double: 3.14L +// +//// Character Literals +//char: 'A' +//wchar_t: L'A' +//char16_t: u'A' +//char32_t: U'A' +// +//// String Literals +//char string: "Hello" +//wchar_t string: L"Hello" +//char16_t string: u"Hello" +//char32_t string: U"Hello" +//UTF-8 string (C++20): u8"Hello" + +namespace cxstructs { + +enum Endianness { LITTLE_ENDIAN, BIG_ENDIAN }; + +// Type trait to check if both types are the same size and either both signed or both unsigned +template +struct is_same_size_a_sign { + static constexpr bool value = + (sizeof(T) == sizeof(U)) && (std::is_signed::value == std::is_signed::value); +}; + +template +struct ConcatType; + +template +struct ConcatType { + static constexpr size_t totalSize = sizeof(T) * 8 + sizeof(U) * 8; + using type = + std::conditional_t>>; +}; + +//Concatenates two equal sized numbers +template +inline constexpr auto bits_concat(T first, U second) -> + typename ConcatType::value>::type { + static_assert(std::is_integral::value && std::is_integral::value, + "Only integer types are supported."); + static_assert(is_same_size_a_sign::value, "T and U must be of the same size and sign."); + static_assert( + !std::is_same_v::value>::type, void>, + "Resulting type is too large to represent."); + + using ResultType = typename ConcatType::value>::type; + return (static_cast(second) << (sizeof(T) * 8)) | static_cast(first); +} + +//Prints the bits of the given num in the given format +template +inline constexpr void bits_print(T num, Endianness endian = LITTLE_ENDIAN) { + static_assert(std::is_integral::value, "Only integral types are supported."); + + constexpr size_t numBits = sizeof(T) * 8; // Total bits in type T + char bitRepresentation[numBits + 1]; // +1 for null terminator + bitRepresentation[numBits] = '\0'; // Null-terminate the string + + for (size_t i = 0; i < numBits; ++i) { + size_t index = endian == LITTLE_ENDIAN ? i : (numBits - 1) - i; + bitRepresentation[i] = (num & (T(1) << index)) ? '1' : '0'; + } + + // Print the bit representation directly + for (size_t i = 0; i < numBits; ++i) { + putchar(bitRepresentation[i]); + } + putchar('\n'); +} + +//Prints the bytes of the given num in the given format +template +void bits_print_bytes(const T& num, Endianness endian = LITTLE_ENDIAN) { + static_assert(std::is_integral::value, "Only integral types are supported."); + + const size_t numBytes = sizeof(T); + const auto* bytePointer = reinterpret_cast(&num); + + if (endian == LITTLE_ENDIAN) { + for (size_t i = 0; i < numBytes; ++i) { + printf("%02X ", bytePointer[i]); + } + } else { + for (size_t i = numBytes; i > 0; --i) { + printf("%02X ", bytePointer[i - 1]); + } + } + + printf("\n"); +} + +template +inline constexpr R bits_get(T num, uint8_t off) { + static_assert(std::is_integral::value, "Only integer type are supported."); + return static_cast(num >> off); +} + +#ifdef CX_INCLUDE_TESTS +static void TEST_BITS() { + CX_ASSERT(bits_concat(0x1, 0x01) == 4294967297, ""); + CX_ASSERT(bits_concat(0x0, 0x01) == 4294967296, ""); + CX_ASSERT(bits_concat(static_cast(0x1), static_cast(0x01)) == 0x101, + "Concatenation of uint8_t failed."); + CX_ASSERT(bits_concat(static_cast(0x0), static_cast(0x01)) == 0x10000, + "Concatenation of uint16_t failed."); + CX_ASSERT(bits_concat(static_cast(0x1), static_cast(0x2)) == 0x200000001, + "Concatenation of uint32_t failed."); + uint16_t num1 = 1, num2 = 2; + CX_ASSERT(bits_concat(num1, num2) == 0x20001, + "Concatenation of uint16_t with different values failed."); + + CX_ASSERT(bits_concat(static_cast(0xFF), static_cast(0xFF)) == 0xFFFF, + "Concatenation of max uint8_t failed."); + + // CX_ASSERT(concat(static_cast(-1), static_cast(-2)) == static_cast(0xFFFFFEFF), "Concatenation of int16_t failed."); + //CX_ASSERT(concat(static_cast(-1), static_cast(-1)) == static_cast(-1), "Concatenation of max int8_t failed."); + + bits_print(bits_concat(0b0001, 0b0001)); + CX_ASSERT(bits_get(bits_concat(0b0001, 0b0001), 0) == + bits_get(bits_concat(0b0001, 0b0001), 32), + ""); +} +#endif + +} // namespace cxstructs + +#endif //CXSTRUCTS_SRC_CXUTIL_CXBITS_H_ diff --git a/src/cxutil/cxgraphics.h b/src/cxutil/cxgraphics.h index eacef17..f966e3b 100644 --- a/src/cxutil/cxgraphics.h +++ b/src/cxutil/cxgraphics.h @@ -30,6 +30,7 @@ #include #include #include + namespace cxstructs { class GraphicsWindow { using RenderCallback = std::function;