diff --git a/.gitmodules b/.gitmodules index 7bafd8b..4d67868 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,9 @@ [submodule "Externals/implot"] path = Externals/implot url = https://github.com/epezent/implot.git +[submodule "Externals/googletest"] + path = Externals/googletest + url = https://github.com/google/googletest.git +[submodule "Externals/benchmark"] + path = Externals/benchmark + url = https://github.com/google/benchmark.git diff --git a/Containers/CMakeLists.txt b/Containers/CMakeLists.txt index 9b0f308..146ae01 100644 --- a/Containers/CMakeLists.txt +++ b/Containers/CMakeLists.txt @@ -1,5 +1,7 @@ project(Containers) +include(GoogleTest) + ### ---------------------- Static Library --------------------- ### file(GLOB SOURCES "./private/*.cpp") file(GLOB HEADERS "./public/*.hpp") @@ -16,6 +18,7 @@ file(GLOB TEST_SOURCES ./tests/ListTest.cpp ./tests/MapTest.cpp ./tests/TreeTest.cpp + ./tests/RingBufferByte.cpp ./tests/Tests.cpp ) @@ -24,5 +27,10 @@ target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++) add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME}) +add_executable(testLinearRingBuffer ./tests/testLinearRingBuffer.cpp) +target_link_libraries(testLinearRingBuffer ${PROJECT_NAME} gtest) +gtest_discover_tests(testLinearRingBuffer) + + add_executable(AVLTreeSpeedTest ./tests/AVLTreeProfiling.cpp) -target_link_libraries(AVLTreeSpeedTest ${PROJECT_NAME}) \ No newline at end of file +target_link_libraries(AVLTreeSpeedTest ${PROJECT_NAME}) diff --git a/Containers/private/linear_ring.cpp b/Containers/private/linear_ring.cpp new file mode 100644 index 0000000..3b6091c --- /dev/null +++ b/Containers/private/linear_ring.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace lrbm { + static int create_memfd(const char* name) { + int fd = syscall(SYS_memfd_create, name, 0); + if (fd == -1) throw std::runtime_error("memfd_create failed"); + return fd; + } + + std::pair create_linear_ring(size_t size) { + const size_t page = sysconf(_SC_PAGESIZE); + + size_t aligned = (size + page - 1) & ~(page - 1); + + // Create RAM-backed file descriptor + int fd = create_memfd("linear_ring"); + if (ftruncate(fd, aligned) != 0) throw std::runtime_error("ftruncate failed"); + + // Reserve 2× VA space + uint8_t* base = (uint8_t*) mmap(nullptr, aligned * 2, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (base == MAP_FAILED) throw std::runtime_error("mmap reserve failed"); + + // First mapping + void* p1 = mmap(base, aligned, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0); + if (p1 != base) throw std::runtime_error("first mmap failed"); + + // Mirror mapping + void* p2 = mmap(base + aligned, aligned, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0); + if (p2 != base + aligned) throw std::runtime_error("mirror mmap failed"); + + close(fd); + return { base, aligned }; // linear region [buf][buf] + } + + void destroy_linear_ring(void* base, size_t size) { munmap(base, size); } + + // void test() { + // const size_t RING_SIZE = 4096; + // uint8_t* ring = (uint8_t*) create_linear_ring(RING_SIZE); + + // printf("Linear ring created at %p (size = %zu)\n", ring, RING_SIZE); + + // const char* msg = "HELLO_LINEAR_RING"; + // size_t msg_len = strlen(msg); + + // size_t head = RING_SIZE - 4; // force boundary crossing + // std::memcpy(ring + head, msg, msg_len); + + // char out[64]; + // std::memcpy(out, ring + head, msg_len); + // out[msg_len] = 0; + + // printf("Expected: %s\n", msg); + // printf("Readback: %s\n", out); + + // if (std::strcmp(msg, out) == 0) printf("SUCCESS: Linear-address ring works!\n"); + // else printf("FAILURE: Ring broken!\n"); + // } +} diff --git a/Containers/public/LinearRingBuffer.hpp b/Containers/public/LinearRingBuffer.hpp new file mode 100644 index 0000000..ddccca2 --- /dev/null +++ b/Containers/public/LinearRingBuffer.hpp @@ -0,0 +1,54 @@ + +#include +#include +#include + +#include "liniar_ring.hpp" + +class LinearRingBuffer { +public: + LinearRingBuffer(size_t size, size_t history) { + const auto& [mem, aligned_size] = lrbm::create_linear_ring(size); + + mem_ = (uint8_t*) mem; + memSize_ = aligned_size; + bufferSize_ = size; + historySize_ = history; + + write_ptr_ = mem_; + read_ptr_ = mem_; + } + + ~LinearRingBuffer() { lrbm::destroy_linear_ring((void*) mem_, memSize_ * 2); } + + uint8_t* write_data() { return write_ptr_; } + uint8_t* read_data() { return read_ptr_; } + + void write_advance(size_t bytes) { write_ptr_ = advancePointer(write_ptr_, bytes); } + void read_advance(size_t bytes) { read_ptr_ = advancePointer(read_ptr_, bytes); } + + void read_reset_with_history() { + const auto offset = size_t(write_ptr_ - mem_); + + if (historySize_ < offset) { + read_ptr_ = write_ptr_ - historySize_; + return; + } + + read_ptr_ = mem_ + (memSize_ + offset - historySize_); + } + +private: + inline uint8_t* advancePointer(const uint8_t* ptr, size_t bytes) { + return mem_ + (size_t(ptr - mem_) + bytes) % memSize_; + } + + size_t bufferSize_ = 0; + size_t historySize_ = 0; + size_t memSize_ = 0; + + uint8_t* mem_ = nullptr; + + uint8_t* read_ptr_ = 0; + uint8_t* write_ptr_ = 0; +}; diff --git a/Containers/public/RingBufferByte.hpp b/Containers/public/RingBufferByte.hpp new file mode 100644 index 0000000..0f6d92c --- /dev/null +++ b/Containers/public/RingBufferByte.hpp @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +template +class RingBufferByte { +public: + explicit RingBufferByte(size_t maxCapacity) { buffer_.resize(maxCapacity); } + + ~RingBufferByte() = default; + + void pushBack(const uint8_t* buffer, size_t size) { + const auto REQUIRED_SIZE = size + sizeof(size); + const auto DEPOSIT = static_cast(freeSpace()) - static_cast(REQUIRED_SIZE); + + if (DEPOSIT < 0) { + if (ALLOW_BUFFER_OVERRIDE) { + freeupSpace(static_cast(-DEPOSIT)); + } else { + throw std::runtime_error("ring buffer overflow"); + } + } + + push(reinterpret_cast(&size), sizeof(size)); + push(buffer, size); + } + + [[nodiscard]] auto frontSize() const -> size_t { + size_t size = 0; + + if (dataSize() < sizeof(size)) { + throw std::runtime_error("no data in the buffer"); + } + + peek(reinterpret_cast(&size), sizeof(size)); + + return size; + } + + auto popFront(uint8_t* buffer) -> size_t { + size_t size = 0; + + if (dataSize() < sizeof(size)) { + return 0; + } + + pop(reinterpret_cast(&size), sizeof(size)); + pop(buffer, size); + + return size; + } + + void clear() { + end_ = 0; + start_ = 0; + } + + [[nodiscard]] bool empty() const { return start_ == end_; } + +private: + void push(const uint8_t* buffer, size_t size) { + for (size_t simpleIdx = 0; simpleIdx < size; simpleIdx++) { + buffer_[end_++] = buffer == nullptr ? 0 : buffer[simpleIdx]; + + if (end_ == buffer_.size()) { + end_ = 0; + } + } + } + + void pop(uint8_t* buffer, size_t size) { + for (size_t sampleIdx = 0; sampleIdx < size; sampleIdx++) { + if (buffer != nullptr) { + buffer[sampleIdx] = buffer_[start_]; + } + + start_++; + + if (start_ == buffer_.size()) { + start_ = 0; + } + } + } + + void peek(uint8_t* buffer, size_t size) const { + auto iter = start_; + for (size_t sampleIdx = 0; sampleIdx < size; sampleIdx++) { + buffer[sampleIdx] = buffer_[iter++]; + + if (iter == buffer_.size()) { + iter = 0; + } + } + } + + void freeupSpace(size_t additionalRequiredSpace) { + const auto CURRENT_DEPOSIT = freeSpace(); + + while (CURRENT_DEPOSIT + additionalRequiredSpace >= freeSpace()) { + popFront(nullptr); + } + } + + [[nodiscard]] size_t freeSpace() const { + if (start_ > end_) { + return start_ - end_; + } + + const auto OUT = buffer_.size() - (end_ - start_); + return OUT; + } + + [[nodiscard]] size_t dataSize() const { return buffer_.size() - freeSpace(); } + + [[nodiscard]] bool hasSpace(unsigned int length) const { return freeSpace() > length; } + + std::vector buffer_; + + size_t end_ = 0; + size_t start_ = 0; +}; diff --git a/Containers/public/liniar_ring.hpp b/Containers/public/liniar_ring.hpp new file mode 100644 index 0000000..ae7f565 --- /dev/null +++ b/Containers/public/liniar_ring.hpp @@ -0,0 +1,9 @@ + +#include +#include + +namespace lrbm { + std::pair create_linear_ring(size_t size); + void destroy_linear_ring(void* base, size_t size); + void test(); +} diff --git a/Containers/tests/RingBufferByte.cpp b/Containers/tests/RingBufferByte.cpp new file mode 100644 index 0000000..c9d2bb9 --- /dev/null +++ b/Containers/tests/RingBufferByte.cpp @@ -0,0 +1,69 @@ + +#include "Tests.hpp" +#include "RingBufferByte.hpp" + +SUITE(RingBufferBytes) { + TEST(Simple) { + + using Data = std::vector; + using DataStorage = std::vector; + + constexpr auto BUFFER_SIZE = 395; + constexpr auto MAX_MSG_SIZE = 31; + constexpr auto ITERATIONS = 100000; + + DataStorage pushed; + DataStorage popped; + + RingBufferByte buffer(BUFFER_SIZE); + + Data randomSamples(MAX_MSG_SIZE); + + for (auto idx = 0; idx < MAX_MSG_SIZE; idx++) { + randomSamples[idx] = (idx + 5 * 31) % 13; + } + + const auto PUSH = [&](RingBufferByte& buffer, DataStorage* storage) { + const Data& pushedSample = randomSamples; + + buffer.pushBack(pushedSample.data(), pushedSample.size()); + + if (storage) { + storage->push_back(pushedSample); + } + }; + + const auto POP = [](RingBufferByte& buffer, DataStorage* storage) { + const auto poppedSampleSize = buffer.frontSize(); + + Data poppedSample(poppedSampleSize); + + const auto SIZE = buffer.popFront(poppedSample.data()); + + CHECK(poppedSample.size() == SIZE); + + if (storage) { + storage->push_back(poppedSample); + } + }; + + for (auto iteration = 0; iteration < ITERATIONS; iteration++) { + PUSH(buffer, nullptr); + } + + while (!buffer.empty()) { + POP(buffer, nullptr); + } + + for (auto iteration = 0; iteration < ITERATIONS; iteration++) { + PUSH(buffer, &pushed); + POP(buffer, &popped); + } + + CHECK(pushed.size() == popped.size()); + + for (auto idx = 0; idx < popped.size(); idx++) { + CHECK(pushed[idx] == popped[idx]); + } + } +} diff --git a/Containers/tests/benchLinearRingBuffer.cpp b/Containers/tests/benchLinearRingBuffer.cpp new file mode 100644 index 0000000..a9293f3 --- /dev/null +++ b/Containers/tests/benchLinearRingBuffer.cpp @@ -0,0 +1,188 @@ +#include "LinearRingBuffer.hpp" + +#include + +#include +#include +#include + +static void fill(void* ptr, size_t n, uint8_t value) { std::memset(ptr, value, n); } + +static bool check(const void* ptr, size_t n, uint8_t value) { + const uint8_t* p = static_cast(ptr); + for (size_t i = 0; i < n; ++i) { + if (p[i] != value) return false; + } + return true; +} + +static bool expect_mem_eq(const void* ptr, size_t n, uint8_t value) { + const uint8_t* p = static_cast(ptr); + for (size_t i = 0; i < n; ++i) { + if (p[i] != value) return false; + } + return true; +} + +using Data = std::vector; +using DataStorage = std::vector; + +typedef ::testing::Types< + std::integral_constant, + std::integral_constant, + std::integral_constant, + std::integral_constant, + std::integral_constant> + BufferSizes; + +template +class LinearRingBufferTest : public ::testing::Test { +protected: + LinearRingBuffer rb; + + LinearRingBufferTest() : + historySize_(10), + rb(size_t(TypeParam::value), size_t(historySize_)) {} + + static constexpr auto ITERATIONS = TypeParam::value * 10; + + size_t messagesPushed_ = 0; + size_t historySize_ = 0; + + auto iterations() { return ITERATIONS; } + + auto historySize() { return historySize_; } + + size_t randomSize() { + static std::random_device rnd; + std::uniform_int_distribution dist(0, 1000); + return dist(rnd); + } + + auto write_and_advance(size_t messageSize, DataStorage* storage) { + Data pushedSample(messageSize); + + for (auto idx = 0; idx < messageSize; idx++) { + pushedSample[idx] = (idx + messagesPushed_++ * 53) % 321; + } + + std::memcpy(this->rb.write_data(), pushedSample.data(), pushedSample.size()); + this->rb.write_advance(pushedSample.size()); + + if (storage) { + storage->push_back(pushedSample); + } + }; + + auto read_and_advance(size_t messageSize, DataStorage* storage) { + Data poppedSample(messageSize); + + std::memcpy(poppedSample.data(), this->rb.read_data(), poppedSample.size()); + + this->rb.read_advance(poppedSample.size()); + + if (storage) { + storage->push_back(poppedSample); + } + }; + + auto reset_with_history() { this->rb.read_reset_with_history(); }; + + auto read_with_history(size_t messageSize, DataStorage* storage) { + Data poppedSample(messageSize); + + std::memcpy(poppedSample.data(), this->rb.read_data(), poppedSample.size()); + + if (storage) { + storage->push_back(poppedSample); + } + }; +}; + +TYPED_TEST_SUITE(LinearRingBufferTest, BufferSizes); + +TYPED_TEST(LinearRingBufferTest, LinearWriteAcrossBoundary) { + constexpr size_t N = TypeParam::value; + + if (N % 4096 != 0) { + return; + } + + auto* base = this->rb.write_data(); + + size_t half = N - 16; + fill(base, half, 0xAA); + fill(base + half, 32, 0xBB); + + EXPECT_EQ(expect_mem_eq(base, 16, 0xBB), true); + EXPECT_EQ(expect_mem_eq(base + 16, half - 16, 0xAA), true); + EXPECT_EQ(expect_mem_eq(base + half, 32, 0xBB), true); +} + +TYPED_TEST(LinearRingBufferTest, Chase) { + + DataStorage pushed; + DataStorage popped; + + static constexpr auto MSG_SIZE = 31; + + for (auto iteration = 0; iteration < this->iterations(); iteration++) { + this->write_and_advance(MSG_SIZE, &pushed); + this->read_and_advance(MSG_SIZE, &popped); + } + + ASSERT_EQ(pushed.size(), popped.size()); + + for (auto idx = 0; idx < popped.size(); idx++) { + ASSERT_EQ(pushed[idx], popped[idx]); + } +} + +Data makeMessageWithHistory(const Data& prev, const Data& current, size_t historySize) { + if (historySize > prev.size()) historySize = prev.size(); + + std::vector result; + + result.reserve(historySize + current.size()); + + result.insert(result.end(), prev.end() - historySize, prev.end()); + result.insert(result.end(), current.begin(), current.end()); + + return result; +} + +TYPED_TEST(LinearRingBufferTest, History) { + + DataStorage pushed; + DataStorage popped; + + pushed.push_back(Data(this->historySize())); + + static constexpr auto MAX_MSG_SIZE = 111; + + for (auto iteration = 0; iteration < this->iterations(); iteration++) { + + const auto MSG_SIZE = this->historySize() + this->randomSize() % MAX_MSG_SIZE; + + auto prevPushed = pushed.back(); + pushed.clear(); + + this->reset_with_history(); + + this->write_and_advance(MSG_SIZE, &pushed); + auto currentPushed = pushed.back(); + + this->read_with_history(this->historySize() + MSG_SIZE, &popped); + auto poppedWithHistory = popped.back(); + popped.clear(); + + auto correctResult = makeMessageWithHistory(prevPushed, currentPushed, this->historySize()); + + ASSERT_EQ(correctResult, poppedWithHistory); + } +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Containers/tests/testLinearRingBuffer.cpp b/Containers/tests/testLinearRingBuffer.cpp new file mode 100644 index 0000000..a9293f3 --- /dev/null +++ b/Containers/tests/testLinearRingBuffer.cpp @@ -0,0 +1,188 @@ +#include "LinearRingBuffer.hpp" + +#include + +#include +#include +#include + +static void fill(void* ptr, size_t n, uint8_t value) { std::memset(ptr, value, n); } + +static bool check(const void* ptr, size_t n, uint8_t value) { + const uint8_t* p = static_cast(ptr); + for (size_t i = 0; i < n; ++i) { + if (p[i] != value) return false; + } + return true; +} + +static bool expect_mem_eq(const void* ptr, size_t n, uint8_t value) { + const uint8_t* p = static_cast(ptr); + for (size_t i = 0; i < n; ++i) { + if (p[i] != value) return false; + } + return true; +} + +using Data = std::vector; +using DataStorage = std::vector; + +typedef ::testing::Types< + std::integral_constant, + std::integral_constant, + std::integral_constant, + std::integral_constant, + std::integral_constant> + BufferSizes; + +template +class LinearRingBufferTest : public ::testing::Test { +protected: + LinearRingBuffer rb; + + LinearRingBufferTest() : + historySize_(10), + rb(size_t(TypeParam::value), size_t(historySize_)) {} + + static constexpr auto ITERATIONS = TypeParam::value * 10; + + size_t messagesPushed_ = 0; + size_t historySize_ = 0; + + auto iterations() { return ITERATIONS; } + + auto historySize() { return historySize_; } + + size_t randomSize() { + static std::random_device rnd; + std::uniform_int_distribution dist(0, 1000); + return dist(rnd); + } + + auto write_and_advance(size_t messageSize, DataStorage* storage) { + Data pushedSample(messageSize); + + for (auto idx = 0; idx < messageSize; idx++) { + pushedSample[idx] = (idx + messagesPushed_++ * 53) % 321; + } + + std::memcpy(this->rb.write_data(), pushedSample.data(), pushedSample.size()); + this->rb.write_advance(pushedSample.size()); + + if (storage) { + storage->push_back(pushedSample); + } + }; + + auto read_and_advance(size_t messageSize, DataStorage* storage) { + Data poppedSample(messageSize); + + std::memcpy(poppedSample.data(), this->rb.read_data(), poppedSample.size()); + + this->rb.read_advance(poppedSample.size()); + + if (storage) { + storage->push_back(poppedSample); + } + }; + + auto reset_with_history() { this->rb.read_reset_with_history(); }; + + auto read_with_history(size_t messageSize, DataStorage* storage) { + Data poppedSample(messageSize); + + std::memcpy(poppedSample.data(), this->rb.read_data(), poppedSample.size()); + + if (storage) { + storage->push_back(poppedSample); + } + }; +}; + +TYPED_TEST_SUITE(LinearRingBufferTest, BufferSizes); + +TYPED_TEST(LinearRingBufferTest, LinearWriteAcrossBoundary) { + constexpr size_t N = TypeParam::value; + + if (N % 4096 != 0) { + return; + } + + auto* base = this->rb.write_data(); + + size_t half = N - 16; + fill(base, half, 0xAA); + fill(base + half, 32, 0xBB); + + EXPECT_EQ(expect_mem_eq(base, 16, 0xBB), true); + EXPECT_EQ(expect_mem_eq(base + 16, half - 16, 0xAA), true); + EXPECT_EQ(expect_mem_eq(base + half, 32, 0xBB), true); +} + +TYPED_TEST(LinearRingBufferTest, Chase) { + + DataStorage pushed; + DataStorage popped; + + static constexpr auto MSG_SIZE = 31; + + for (auto iteration = 0; iteration < this->iterations(); iteration++) { + this->write_and_advance(MSG_SIZE, &pushed); + this->read_and_advance(MSG_SIZE, &popped); + } + + ASSERT_EQ(pushed.size(), popped.size()); + + for (auto idx = 0; idx < popped.size(); idx++) { + ASSERT_EQ(pushed[idx], popped[idx]); + } +} + +Data makeMessageWithHistory(const Data& prev, const Data& current, size_t historySize) { + if (historySize > prev.size()) historySize = prev.size(); + + std::vector result; + + result.reserve(historySize + current.size()); + + result.insert(result.end(), prev.end() - historySize, prev.end()); + result.insert(result.end(), current.begin(), current.end()); + + return result; +} + +TYPED_TEST(LinearRingBufferTest, History) { + + DataStorage pushed; + DataStorage popped; + + pushed.push_back(Data(this->historySize())); + + static constexpr auto MAX_MSG_SIZE = 111; + + for (auto iteration = 0; iteration < this->iterations(); iteration++) { + + const auto MSG_SIZE = this->historySize() + this->randomSize() % MAX_MSG_SIZE; + + auto prevPushed = pushed.back(); + pushed.clear(); + + this->reset_with_history(); + + this->write_and_advance(MSG_SIZE, &pushed); + auto currentPushed = pushed.back(); + + this->read_with_history(this->historySize() + MSG_SIZE, &popped); + auto poppedWithHistory = popped.back(); + popped.clear(); + + auto correctResult = makeMessageWithHistory(prevPushed, currentPushed, this->historySize()); + + ASSERT_EQ(correctResult, poppedWithHistory); + } +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/DataAnalysis/applications/NumRecApp.cpp b/DataAnalysis/applications/NumRecApp.cpp index aa98ac9..ecde746 100644 --- a/DataAnalysis/applications/NumRecApp.cpp +++ b/DataAnalysis/applications/NumRecApp.cpp @@ -10,6 +10,8 @@ using namespace tp; +#include + void loadImage(Buffer& output, const char* name) { int x, y, channels_in_file; unsigned char* loadedImage = stbi_load(name, &x, &y, &channels_in_file, 4); @@ -28,7 +30,9 @@ void loadImage(Buffer& output, const char* name) { void loadNN(FCNN& nn) { ArchiverLocalConnection archiver; - archiver.connection.connect(LocalConnection::Location("NumRec.wb"), LocalConnection::Type(true)); + if (std::filesystem::exists(std::filesystem::path("NumRec.wb"))) { + archiver.connection.connect(LocalConnection::Location("NumRec.wb"), LocalConnection::Type(true)); + } if (archiver.connection.getConnectionStatus().isOpened()) { archiver % nn; @@ -56,7 +60,7 @@ void executeCmd(const char* imageName) { } int main(int argc, char** argv) { - const char* imageName = "digit.png"; + const char* imageName = "tmp2.png"; if (argc == 2) { imageName = argv[1]; diff --git a/DataAnalysis/applications/NumRecTraining.cpp b/DataAnalysis/applications/NumRecTraining.cpp index e6c808e..dcba426 100644 --- a/DataAnalysis/applications/NumRecTraining.cpp +++ b/DataAnalysis/applications/NumRecTraining.cpp @@ -1,3 +1,4 @@ +#include #include "FCNN.hpp" #include "LocalConnection.hpp" @@ -71,7 +72,9 @@ struct NumberRec { { ArchiverLocalConnection archiver; - archiver.connection.connect(LocalConnection::Location("NumRec.wb"), LocalConnection::Type(true)); + if (std::filesystem::exists(std::filesystem::path("NumRec.wb"))) { + archiver.connection.connect(LocalConnection::Location("NumRec.wb"), LocalConnection::Type(true)); + } if (archiver.connection.getConnectionStatus().isOpened()) { archiver % nn; @@ -215,7 +218,7 @@ int main() { auto batchSize = trainRange.idxDiff() / numBatches; - for (auto epoch : IterRange(1)) { + for (auto epoch : IterRange(10)) { printf("Epoch %i\n", epoch.index()); for (auto batchIdx : IterRange(trainRange.idxDiff() / batchSize)) { @@ -241,5 +244,5 @@ int main() { // app.displayImage(i); } - printf("\n\nIncorrect - %i out of %i (%f)\n\n", errors, testRange.idxDiff(), (halnf) errors / (halnf) testRange.idxDiff()); + printf("\n\nIncorrect - %i out of %i error percentage (%f)\n\n", errors, testRange.idxDiff(), (halnf) errors / (halnf) testRange.idxDiff()); } \ No newline at end of file diff --git a/Externals/CMakeLists.txt b/Externals/CMakeLists.txt index 4f40aa3..ec85993 100644 --- a/Externals/CMakeLists.txt +++ b/Externals/CMakeLists.txt @@ -53,4 +53,6 @@ project(ImageIO) add_library(${PROJECT_NAME} INTERFACE) target_include_directories(${PROJECT_NAME} INTERFACE ./imageIO/) -add_subdirectory(lalr) \ No newline at end of file +add_subdirectory(lalr) + +add_subdirectory(googletest) diff --git a/Externals/benchmark b/Externals/benchmark new file mode 160000 index 0000000..c3f8657 --- /dev/null +++ b/Externals/benchmark @@ -0,0 +1 @@ +Subproject commit c3f86578bb2081b52cee9d51615912ca4aa52fe4 diff --git a/Externals/googletest b/Externals/googletest new file mode 160000 index 0000000..1b96fa1 --- /dev/null +++ b/Externals/googletest @@ -0,0 +1 @@ +Subproject commit 1b96fa13f549387b7549cc89e1a785cf143a1a50 diff --git a/Externals/lalr b/Externals/lalr index 6d16a6b..4986122 160000 --- a/Externals/lalr +++ b/Externals/lalr @@ -1 +1 @@ -Subproject commit 6d16a6bb60490a2ee4e86f689c86e647b300c7fb +Subproject commit 498612239713a6dbbf9f26b8e412e985538a0650 diff --git a/Graphics/private/Canvas.cpp b/Graphics/private/Canvas.cpp index bc29a17..82a2cc5 100644 --- a/Graphics/private/Canvas.cpp +++ b/Graphics/private/Canvas.cpp @@ -82,6 +82,19 @@ void Canvas::frame(RectF rec, const RGBA& col, halnf round) { // nvgFill(mContext->vg); } +void Canvas::line(Vec2F start, Vec2F end, const RGBA& col, halnf thickness) { + start += mOrigin; + end += mOrigin; + + nvgBeginPath(mContext->vg); + nvgFillColor(mContext->vg, { col.r, col.g, col.b, col.a}); + nvgMoveTo(mContext->vg, start.x, start.y); + nvgLineTo(mContext->vg, end.x, end.y); + nvgStrokeWidth(mContext->vg, thickness); + nvgStrokeColor(mContext->vg, { col.r, col.g, col.b, col.a }); + nvgStroke(mContext->vg); +} + void Canvas::circle(Vec2F pos, halnf size, const RGBA& col) { pos += mOrigin; diff --git a/Graphics/public/Graphics.hpp b/Graphics/public/Graphics.hpp index f854d3f..afbc325 100644 --- a/Graphics/public/Graphics.hpp +++ b/Graphics/public/Graphics.hpp @@ -75,6 +75,7 @@ namespace tp { void circle(Vec2F pos, halnf size, const RGBA& col); void text(const char*, const RectF&, halnf size, Align, halnf padding, const RGBA&); void colorWheel(const RectF& rec, const ColorWheel& colorWheel); + void line(Vec2F start, Vec2F end, const RGBA& col, halnf thickness); ImageHandle createImageFromTextId(ualni id, Vec2F size); void updateTextureID(ImageHandle handle, ualni id); diff --git a/LibraryViewer/CMakeLists.txt b/LibraryViewer/CMakeLists.txt index becb208..4400a13 100644 --- a/LibraryViewer/CMakeLists.txt +++ b/LibraryViewer/CMakeLists.txt @@ -15,7 +15,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC Graphics Connection Widgets) target_link_libraries(${PROJECT_NAME} PUBLIC ${BINDINGS_LIBS}) file(COPY "library" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}") -file(COPY "applications/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") +file(COPY "applications/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/rsc/") ### -------------------------- Applications -------------------------- ### add_executable(libView ./applications/Entry.cpp) diff --git a/Math/public/Mat.hpp b/Math/public/Mat.hpp index 2483284..97e73c1 100644 --- a/Math/public/Mat.hpp +++ b/Math/public/Mat.hpp @@ -465,8 +465,15 @@ namespace tp { } // Matrix Properties + MVec toGlobal(const MVec &in) const { return i * in.x + j * in.y; } + + Mat toGlobal(const Mat &in) const { return {toGlobal(in.i), toGlobal(in.j)}; } + + MVec toLocal(const MVec &in) const { return transform(in); } + MVec transform(const MVec& in) const { return MVec(i.x * in.x + i.y * in.y, j.x * in.x + j.y * in.y); } + // ??? Mat transform(const Mat& in) const { Mat out; out.i.x = i.x * in.i.x + j.x * in.i.y; diff --git a/Math/public/Rect.hpp b/Math/public/Rect.hpp index a03655e..47ab3ef 100644 --- a/Math/public/Rect.hpp +++ b/Math/public/Rect.hpp @@ -70,6 +70,29 @@ namespace tp { return *this; } + Rect& adjust(tp::halnf left, tp::halnf bottom, tp::halnf right, tp::halnf top) { + x += left; + y += bottom; + size.x += -left + right; + size.y += -bottom + top; + return *this; + } + + Rect adjusted(tp::halnf left, tp::halnf bottom, tp::halnf right, tp::halnf top) const { + return Rect(*this).adjust(left, bottom, right, top); + } + + [[nodiscard]] halnf left() const { return x; } + [[nodiscard]] halnf bottom() { return y; } + [[nodiscard]] halnf top() { return y + size.y; } + [[nodiscard]] halnf right() { return x + size.x; } + + static Rect fromPoints(const Vec2& p1, const Vec2& p2) { + tp::Vec2F min = {tp::min(p1.x, p2.x), tp::min(p1.y, p2.y)}; + tp::Vec2F max = {tp::max(p1.x, p2.x), tp::max(p1.y, p2.y)}; + return { min, max - min }; + } + bool operator==(const Rect& rect) const { return (pos == rect.pos && size == rect.size); } bool isEnclosedIn(const Rect& rect, bool aParent = false) const { @@ -184,13 +207,23 @@ namespace tp { } void expand(const Vec2& point) { - pos.x = min(point.x, pos.x); - pos.y = min(point.y, pos.y); + if (point.x < x) { + size.x += x - point.x; + x = point.x; + } - auto p = pos + size; - p.x = max(point.x, p.x); - p.y = max(point.y, p.y); - size = p - pos; + if (point.y < y) { + size.y += y - point.y; + y = point.y; + } + + if (point.x > x + size.x) { + size.x = point.x - x; + } + + if (point.y > y + size.y) { + size.y = point.y - y; + } } void expand(const Rect& rect) { diff --git a/Sketch3D/CMakeLists.txt b/Sketch3D/CMakeLists.txt index 248c80d..b5eb9f8 100644 --- a/Sketch3D/CMakeLists.txt +++ b/Sketch3D/CMakeLists.txt @@ -19,4 +19,4 @@ add_executable(Sketch3DApp ./applications/Entry.cpp) target_link_libraries(Sketch3DApp ${PROJECT_NAME}) file(COPY "rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") -file(COPY "applications/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") +file(COPY "applications/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/rsc") diff --git a/Widgets/CMakeLists.txt b/Widgets/CMakeLists.txt index 0589f17..c4a68a5 100644 --- a/Widgets/CMakeLists.txt +++ b/Widgets/CMakeLists.txt @@ -14,4 +14,4 @@ add_executable(WidgetsExample examples/Example.cpp) target_link_libraries(WidgetsExample ${PROJECT_NAME} ${GLEW_LIB}) target_include_directories(WidgetsExample PUBLIC ../Externals/glfw/include ${GLEW_INCLUDE_DIR}) -file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/") \ No newline at end of file +file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/rsc") diff --git a/Widgets/public/WidgetApplication.hpp b/Widgets/public/WidgetApplication.hpp index 8f6c697..7bf6fbe 100644 --- a/Widgets/public/WidgetApplication.hpp +++ b/Widgets/public/WidgetApplication.hpp @@ -10,6 +10,7 @@ namespace tp { WidgetApplication() = default; void setRoot(Widget* widget); + virtual void debugUI(); private: void processFrame(EventHandler* eventHandler, halnf deltaTime) override; @@ -17,8 +18,6 @@ namespace tp { void drawFrame(Canvas* canvas) override; bool forceNewFrame() override ; - private: - void debugUI(); private: halnf mDebugSplitFactor = 0.7;