feat! Multithreading
commita763bff532Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Dec 12 04:33:13 2025 +0000 Added release mode building Now the build script enables you to: - Build in debug mode (default no arguments) - Build in debug mode then run (`run` argument) - Build in release mode (`release` argument) commit7112937b0bAuthor: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Dec 12 04:30:50 2025 +0000 Better thread pool constructor statically define a number of threads, then setup the necessary machinery to make it work. commita5666328b7Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Dec 12 04:29:32 2025 +0000 Fixed the heap-use-after-free issue. Just standard multithreading stuff; access to the allocator while hot threads are running means stuff can change underneath us even /during/ a read. I've mutex locked state for stuff in the drawing domain which stops this issue. commit5d78cb20dfAuthor: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Dec 12 04:28:21 2025 +0000 Adjust TODOs commit9d3a202c27Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Dec 12 04:09:50 2025 +0000 Implement stepping via KEY_SPACE commit424fab2e40Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Dec 12 04:08:01 2025 +0000 Weird bug in draw_tree Seems near the 100,000 node mark, the vector craps itself? I keep getting "heap use after free" errors here when trying to access the allocator vector. Disabling fsan just leads to a segment fault near the same point. I think we might need to introduce our own vector :) commit6ffa63f7acAuthor: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Dec 12 04:07:43 2025 +0000 Make general delay 1ms Just for my puny eyes to see. commit2dbe3d0a58Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Dec 12 03:54:30 2025 +0000 merge generate_children into do_iteration Also fix this really weird slow down we get from doing direct mutation to a state.allocator.get_ref (i.e. a Node by reference). Maybe the compiler just can't inline that instruction? Regardless, this patch fixes the slow downs! commit7e801df280Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Dec 12 03:53:51 2025 +0000 no more numerics commitf70517cf41Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Dec 12 03:08:56 2025 +0000 Simplify mutex locking scheme (lock at start, unlock at end) commit00858cf74fAuthor: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Nov 28 17:24:33 2025 +0000 Rewrite main to draw based on our new state/draw_state Arranges a thread set, draws based on draw_state. No need to lock the mutex since drawing should only require reading. Still has a timer in case we need to do timed checks. commit3e065e50a9Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Nov 28 17:24:12 2025 +0000 Disable previous work We're going to rewrite this commitab0f152742Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Nov 28 17:23:05 2025 +0000 Define `worker` function which a thread should run Pauses until state.pause_work is false (checking every THREAD_PAUSE_DELAY), then performs an iteration. Quits when state.quit_work is true (based on loop). Generally delays itself by THREAD_GENERAL_DELAY. commit014821ceb5Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Nov 28 17:22:39 2025 +0000 Added booleans for thread workers to look at when running pause_work temporarily stops work, but should keep the thread alive. stop_work should kill the thread. commit1bd8a9f7b2Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Fri Nov 28 17:21:21 2025 +0000 Add run component to build script, activated by passing argument `run` commit6f620644bfAuthor: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:57:10 2025 +0000 Define constructors for state.hpp explicitly commit0008c31f53Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:53:10 2025 +0000 Add src/worker.cpp to build pipeline commitf8068c4a15Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:52:52 2025 +0000 Implement the two worker functions commit66c56f2c15Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:51:41 2025 +0000 Define thread safe worker functions for generating new children on the tree commit6247f2af49Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:44:23 2025 +0000 Add src/state.cpp to the build pipeline commit40e07e03f2Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:42:24 2025 +0000 Add mutexes to state for the queue and the allocator Our threads need to negotiate access and mutation to the two resources - we don't want them treading on each others toes when generating new child nodes /or/ when trying to remove/add work to the queue. The former situation is particularly worrisome due to how weak the indexing system is with the allocator. commit0abd353368Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:30:35 2025 +0000 Implement cw::state::DrawState::compute_bounds commit0ac316ada4Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:30:21 2025 +0000 Define the general state of the sim (extract from main.cpp) commita03fa13a07Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:19:52 2025 +0000 Implementation (copied from numerics) commitff9a2851d4Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:19:41 2025 +0000 Switch to using i64's instead of optional u64 in Node commit6c2bc93874Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:12:35 2025 +0000 remove index_t definition conflicts with prev code base. Also std::optional doubles the size of the underlying type. horrifying! I don't want to have to give 16 bytes over for something that could be embedded in the 8 bytes of the u64 directly. I'm thinking we could just use i64's instead, sacrificing that top bit to indicate if the value is present or not. commit7a4d158d2fAuthor: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 01:02:31 2025 +0000 Add gcd to base.hpp commite032303773Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 00:56:45 2025 +0000 Define node.hpp (move definitions away from numerics.hpp) commit9821a2ab15Author: Aryadev Chavali <aryadev@aryadevchavali.com> Date: Thu Nov 27 00:55:44 2025 +0000 Define basic type aliases and useful functions/macros for use throughout the system
This commit is contained in:
39
README.org
39
README.org
@@ -20,8 +20,28 @@ the generator fraction is in green.
|
|||||||
This was done just for fun really, but it's quite fun to see it
|
This was done just for fun really, but it's quite fun to see it
|
||||||
generate a dense number line over many iterations.
|
generate a dense number line over many iterations.
|
||||||
* TODOs
|
* TODOs
|
||||||
** TODO Multithreading
|
** TODO Tree visualisation
|
||||||
SCHEDULED: <2025-11-18 Tue>
|
Instead of a number line, how about visualising the actual tree at
|
||||||
|
work as a graph of nodes? Maybe colouring nodes based on where it is
|
||||||
|
on the number line.
|
||||||
|
** TODO Don't walk the tree everytime we compute_bounds
|
||||||
|
[[file:src/state.cpp::void DrawState::compute_bounds()][location]]
|
||||||
|
|
||||||
|
We already have the latest bound nodes so we're part-way through the
|
||||||
|
tree. Just keep going down what we have so far surely? Even better,
|
||||||
|
don't use nodes _at all_. Run with an index!
|
||||||
|
** DONE Fix weird issue at past 100K nodes
|
||||||
|
std::vector seems to crap itself past 100K nodes - we keep getting
|
||||||
|
heap-use-after-free issues when trying to access the allocator nodes
|
||||||
|
at that point. Seemingly random. What's going on?
|
||||||
|
|
||||||
|
Solution: _everytime_ we want to access the allocator for nontrivial
|
||||||
|
(like memory reads) we need to lock the mutex. No two ways about it.
|
||||||
|
All draw functions were causing the issue.
|
||||||
|
** DONE Prettify code base
|
||||||
|
It's a big blob of code currently in the graphics portion. Not very
|
||||||
|
pretty but it gets the job done. Try modularisation.
|
||||||
|
** DONE Multithreading
|
||||||
Currently single threaded. A multithreaded implementation could have
|
Currently single threaded. A multithreaded implementation could have
|
||||||
multiple nodes generated at once, which would speed up the
|
multiple nodes generated at once, which would speed up the
|
||||||
implementation.
|
implementation.
|
||||||
@@ -29,7 +49,7 @@ implementation.
|
|||||||
Might need to study my current implementation to see if it could be
|
Might need to study my current implementation to see if it could be
|
||||||
done better.
|
done better.
|
||||||
|
|
||||||
*** TODO Formalise state structure (separate drawing functions)
|
*** DONE Formalise state structure (separate drawing functions)
|
||||||
Make a dedicated header and fit the necessary functions to our state
|
Make a dedicated header and fit the necessary functions to our state
|
||||||
structure: [[file:src/main.cpp::struct State][state structure]].
|
structure: [[file:src/main.cpp::struct State][state structure]].
|
||||||
|
|
||||||
@@ -40,14 +60,14 @@ A good working name would be ~cw_state~.
|
|||||||
|
|
||||||
We could then have a separate structure for the drawing context
|
We could then have a separate structure for the drawing context
|
||||||
(~cw_draw~) which can update itself by reading the ~cw_state~.
|
(~cw_draw~) which can update itself by reading the ~cw_state~.
|
||||||
*** TODO Setup a queue and allocator mutex on state
|
*** DONE Setup a queue and allocator mutex on state
|
||||||
We need one for the queue so we can make clean pushes and pops, and
|
We need one for the queue so we can make clean pushes and pops, and
|
||||||
one for the allocator to ensure we're not messing up our indices in
|
one for the allocator to ensure we're not messing up our indices in
|
||||||
the nodes.
|
the nodes.
|
||||||
|
|
||||||
We could just set these up in the state structure itself to make
|
We could just set these up in the state structure itself to make
|
||||||
lookup easier.
|
lookup easier.
|
||||||
*** TODO Make iterate use the state structure and mutexes
|
*** DONE Make iterate use the state structure and mutexes
|
||||||
[[file:src/numerics.cpp::std::tuple<Fraction, Fraction, Fraction>
|
[[file:src/numerics.cpp::std::tuple<Fraction, Fraction, Fraction>
|
||||||
iterate(std::queue<word_t> &queue,]]
|
iterate(std::queue<word_t> &queue,]]
|
||||||
|
|
||||||
@@ -64,11 +84,4 @@ iterate(std::queue<word_t> &queue,]]
|
|||||||
|
|
||||||
I think this scheme minimises the any mutex is locked to just the bare
|
I think this scheme minimises the any mutex is locked to just the bare
|
||||||
minimum, ensuring other threads can get in on the action.
|
minimum, ensuring other threads can get in on the action.
|
||||||
*** TODO Setup a thread pool utilising state and iterate
|
*** DONE Setup a thread pool utilising state and iterate
|
||||||
** TODO Prettify code base
|
|
||||||
It's a big blob of code currently in the graphics portion. Not very
|
|
||||||
pretty but it gets the job done. Try modularisation.
|
|
||||||
** TODO Tree visualisation
|
|
||||||
Instead of a number line, how about visualising the actual tree at
|
|
||||||
work as a graph of nodes? Maybe colouring nodes based on where it is
|
|
||||||
on the number line.
|
|
||||||
|
|||||||
17
build.sh
17
build.sh
@@ -2,9 +2,24 @@
|
|||||||
|
|
||||||
set -xe
|
set -xe
|
||||||
|
|
||||||
|
OUT="cw_tree.out"
|
||||||
GFLAGS="-Wall -Wextra -Wswitch-enum -std=c++17"
|
GFLAGS="-Wall -Wextra -Wswitch-enum -std=c++17"
|
||||||
DFLAGS="-ggdb -fsanitize=address -fsanitize=undefined"
|
DFLAGS="-ggdb -fsanitize=address -fsanitize=undefined"
|
||||||
|
RFLAGS="-O2"
|
||||||
CFLAGS="$GFLAGS $DFLAGS"
|
CFLAGS="$GFLAGS $DFLAGS"
|
||||||
LIBS="-lraylib -lm"
|
LIBS="-lraylib -lm"
|
||||||
|
|
||||||
c++ $CFLAGS -o cw_tree.out src/numerics.cpp src/main.cpp $LIBS
|
build() {
|
||||||
|
c++ $CFLAGS -o $OUT src/node.cpp src/state.cpp src/worker.cpp src/main.cpp $LIBS
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$1" = "run" ]
|
||||||
|
then
|
||||||
|
build && ./$OUT
|
||||||
|
elif [ "$1" = "release" ]
|
||||||
|
then
|
||||||
|
CFLAGS="$GFLAGS $RFLAGS"
|
||||||
|
build;
|
||||||
|
else
|
||||||
|
build;
|
||||||
|
fi
|
||||||
|
|||||||
57
src/base.hpp
Normal file
57
src/base.hpp
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/* base.hpp: Basic definitions
|
||||||
|
* Created: 2025-11-27
|
||||||
|
* Author: Aryadev Chavali
|
||||||
|
* License: See end of file
|
||||||
|
* Commentary:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BASE_HPP
|
||||||
|
#define BASE_HPP
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||||
|
#define MAX(A, B) ((B) < (A) ? (A) : (B))
|
||||||
|
|
||||||
|
using u8 = uint8_t;
|
||||||
|
using u16 = uint16_t;
|
||||||
|
using u32 = uint32_t;
|
||||||
|
using u64 = uint64_t;
|
||||||
|
|
||||||
|
using i8 = int8_t;
|
||||||
|
using i16 = int16_t;
|
||||||
|
using i32 = int32_t;
|
||||||
|
using i64 = int64_t;
|
||||||
|
|
||||||
|
static_assert(sizeof(float) == 4, "f32 requires 4 byte floats");
|
||||||
|
static_assert(sizeof(double) == 8, "f64 requires 8 byte doubles");
|
||||||
|
using f32 = float;
|
||||||
|
using f64 = double;
|
||||||
|
|
||||||
|
inline u64 gcd(u64 a, u64 b)
|
||||||
|
{
|
||||||
|
if (a == b)
|
||||||
|
return a;
|
||||||
|
else if (a <= 1 || b <= 1)
|
||||||
|
return 1;
|
||||||
|
for (u64 r = b % a; r != 0; b = a, a = r, r = b % a)
|
||||||
|
continue;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Aryadev Chavali
|
||||||
|
|
||||||
|
* 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 Version 2 for
|
||||||
|
* details.
|
||||||
|
|
||||||
|
* You may distribute and modify this code under the terms of the GNU General
|
||||||
|
* Public License Version 2, which you should have received a copy of along with
|
||||||
|
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
149
src/main.cpp
149
src/main.cpp
@@ -5,18 +5,21 @@
|
|||||||
* Commentary: 2024-07-25
|
* Commentary: 2024-07-25
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "./numerics.hpp"
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <thread>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include <raylib.h>
|
#include <raylib.h>
|
||||||
|
|
||||||
|
#include "base.hpp"
|
||||||
|
#include "node.hpp"
|
||||||
|
#include "worker.hpp"
|
||||||
|
|
||||||
#define WIDTH 1024
|
#define WIDTH 1024
|
||||||
#define HEIGHT 1024
|
#define HEIGHT 1024
|
||||||
#define FONT_SIZE 20
|
#define FONT_SIZE 20
|
||||||
@@ -24,14 +27,17 @@
|
|||||||
#define LINE_TOP (7 * HEIGHT / 16)
|
#define LINE_TOP (7 * HEIGHT / 16)
|
||||||
#define LINE_BOTTOM (9 * HEIGHT / 16)
|
#define LINE_BOTTOM (9 * HEIGHT / 16)
|
||||||
|
|
||||||
std::pair<std::string, int> fraction_to_string(Fraction f)
|
using cw::state::DrawState;
|
||||||
|
using cw::state::State;
|
||||||
|
|
||||||
|
std::pair<std::string, int> fraction_to_string(cw::node::Fraction f)
|
||||||
{
|
{
|
||||||
std::string s{to_string(f)};
|
std::string s{to_string(f)};
|
||||||
int width = MeasureText(s.c_str(), FONT_SIZE);
|
int width = MeasureText(s.c_str(), FONT_SIZE);
|
||||||
return std::make_pair(s, width);
|
return std::make_pair(s, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw_fraction(Fraction f, word_t x, word_t y)
|
void draw_fraction(cw::node::Fraction f, u64 x, u64 y)
|
||||||
{
|
{
|
||||||
std::string s;
|
std::string s;
|
||||||
int width;
|
int width;
|
||||||
@@ -40,6 +46,140 @@ void draw_fraction(Fraction f, word_t x, word_t y)
|
|||||||
DrawText(s.c_str(), x - width / 2, y - FONT_SIZE, FONT_SIZE, WHITE);
|
DrawText(s.c_str(), x - width / 2, y - FONT_SIZE, FONT_SIZE, WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr u64 clamp_to_width(const DrawState &ds, f64 val)
|
||||||
|
{
|
||||||
|
return (WIDTH / (ds.bounds.upper_val - ds.bounds.lower_val)) *
|
||||||
|
(val - ds.bounds.lower_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_tree(DrawState &ds, State &state)
|
||||||
|
{
|
||||||
|
// Number line
|
||||||
|
DrawLine(0, HEIGHT / 2, WIDTH, HEIGHT / 2, WHITE);
|
||||||
|
|
||||||
|
// Bounds
|
||||||
|
u64 lower_x = clamp_to_width(ds, ds.bounds.leftmost.value.norm);
|
||||||
|
u64 upper_x = clamp_to_width(ds, ds.bounds.rightmost.value.norm);
|
||||||
|
DrawLine(lower_x, LINE_TOP, lower_x, LINE_BOTTOM, WHITE);
|
||||||
|
DrawLine(upper_x, LINE_TOP, upper_x, LINE_BOTTOM, WHITE);
|
||||||
|
|
||||||
|
state.mutex.lock();
|
||||||
|
std::stack<std::pair<u64, f64>> stack;
|
||||||
|
cw::node::Node n = state.allocator.get_val(0);
|
||||||
|
stack.push(std::make_pair(0, n.value.norm));
|
||||||
|
while (!stack.empty())
|
||||||
|
{
|
||||||
|
u64 node_index;
|
||||||
|
f64 norm;
|
||||||
|
std::tie(node_index, norm) = stack.top();
|
||||||
|
stack.pop();
|
||||||
|
u64 x = clamp_to_width(ds, norm);
|
||||||
|
DrawLine(x, LINE_TOP, x, LINE_BOTTOM, RED);
|
||||||
|
|
||||||
|
cw::node::Node n = state.allocator.get_val(node_index);
|
||||||
|
if (n.left >= 0)
|
||||||
|
{
|
||||||
|
cw::node::Node left = state.allocator.get_val(n.left);
|
||||||
|
stack.push(std::make_pair(n.left, left.value.norm));
|
||||||
|
}
|
||||||
|
if (n.right >= 0)
|
||||||
|
{
|
||||||
|
cw::node::Node right = state.allocator.get_val(n.right);
|
||||||
|
stack.push(std::make_pair(n.right, right.value.norm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
using Clock = std::chrono::steady_clock;
|
||||||
|
using Ms = std::chrono::milliseconds;
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
// Init timer
|
||||||
|
auto time_current = Clock::now();
|
||||||
|
auto time_previous = time_current;
|
||||||
|
constexpr auto time_delta = 0;
|
||||||
|
|
||||||
|
// Init general state
|
||||||
|
cw::state::State state;
|
||||||
|
state.stop_work = false;
|
||||||
|
state.pause_work = false;
|
||||||
|
state.allocator.alloc(cw::node::Node{{1, 1}, -1, -1});
|
||||||
|
state.queue.push(0);
|
||||||
|
|
||||||
|
cw::state::DrawState draw_state{state};
|
||||||
|
|
||||||
|
// Init meta text (counter, iterations, etc)
|
||||||
|
u64 count = 1, prev_count = 0;
|
||||||
|
std::stringstream format_stream;
|
||||||
|
std::string format_str;
|
||||||
|
u64 format_str_width = 0;
|
||||||
|
|
||||||
|
// Init threads
|
||||||
|
#define THREADS 15
|
||||||
|
std::thread threads[THREADS];
|
||||||
|
for (auto i = 0; i < THREADS; ++i)
|
||||||
|
{
|
||||||
|
threads[i] = std::move(std::thread(cw::worker::worker, std::ref(state)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup raylib window
|
||||||
|
InitWindow(WIDTH, HEIGHT, "Calkin-Wilf tree");
|
||||||
|
SetTargetFPS(60);
|
||||||
|
|
||||||
|
while (!WindowShouldClose())
|
||||||
|
{
|
||||||
|
// Update
|
||||||
|
time_current = Clock::now();
|
||||||
|
if (!state.pause_work &&
|
||||||
|
std::chrono::duration_cast<Ms>(time_current - time_previous).count() >=
|
||||||
|
time_delta)
|
||||||
|
{
|
||||||
|
time_previous = time_current;
|
||||||
|
count = state.allocator.vec.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev_count != count)
|
||||||
|
{
|
||||||
|
draw_state.compute_bounds();
|
||||||
|
prev_count = count;
|
||||||
|
format_stream << "Count=" << count << "\n\n"
|
||||||
|
<< "Iterations=" << (count - 1) / 2 << "\n\n"
|
||||||
|
<< "Lower=" << to_string(draw_state.bounds.leftmost.value)
|
||||||
|
<< "\n\n"
|
||||||
|
<< "Upper=" << to_string(draw_state.bounds.rightmost.value);
|
||||||
|
format_str = format_stream.str();
|
||||||
|
format_stream.str("");
|
||||||
|
format_str_width = MeasureText(format_str.c_str(), FONT_SIZE * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsKeyPressed(KEY_SPACE))
|
||||||
|
{
|
||||||
|
state.pause_work = !state.pause_work;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
|
||||||
|
ClearBackground(BLACK);
|
||||||
|
BeginDrawing();
|
||||||
|
draw_tree(draw_state, state);
|
||||||
|
DrawText(format_str.c_str(), WIDTH / 2 - format_str_width / 2, HEIGHT / 8,
|
||||||
|
FONT_SIZE, WHITE);
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseWindow();
|
||||||
|
|
||||||
|
state.stop_work = true;
|
||||||
|
for (auto &thread : threads)
|
||||||
|
{
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
struct State
|
struct State
|
||||||
{
|
{
|
||||||
NodeAllocator allocator;
|
NodeAllocator allocator;
|
||||||
@@ -209,6 +349,7 @@ int main(void)
|
|||||||
CloseWindow();
|
CloseWindow();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Copyright (C) 2024, 2025 Aryadev Chavali
|
/* Copyright (C) 2024, 2025 Aryadev Chavali
|
||||||
|
|
||||||
|
|||||||
133
src/node.cpp
Normal file
133
src/node.cpp
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/* node.cpp: Implementations of various functions for fractions and nodes
|
||||||
|
* Created: 2025-11-27
|
||||||
|
* Author: Aryadev Chavali
|
||||||
|
* License: See end of file
|
||||||
|
* Commentary:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "node.hpp"
|
||||||
|
|
||||||
|
namespace cw::node
|
||||||
|
{
|
||||||
|
/**************************************/
|
||||||
|
/* ___ _ _ */
|
||||||
|
/* | __| _ __ _ __| |_(_)___ _ _ ___ */
|
||||||
|
/* | _| '_/ _` / _| _| / _ \ ' \(_-< */
|
||||||
|
/* |_||_| \__,_\__|\__|_\___/_||_/__/ */
|
||||||
|
/* */
|
||||||
|
/**************************************/
|
||||||
|
Fraction::Fraction(u64 numerator, u64 denominator)
|
||||||
|
: numerator{numerator}, denominator{denominator},
|
||||||
|
norm{numerator / ((f64)denominator)}
|
||||||
|
{
|
||||||
|
u64 hcf = gcd(MIN(numerator, denominator), MAX(numerator, denominator));
|
||||||
|
numerator /= hcf;
|
||||||
|
denominator /= hcf;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Fraction::operator<(const Fraction other)
|
||||||
|
{
|
||||||
|
if (other.denominator == denominator)
|
||||||
|
return numerator < other.numerator;
|
||||||
|
return (numerator * other.denominator) < (other.numerator * denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Fraction::operator==(const Fraction &other)
|
||||||
|
{
|
||||||
|
return numerator == other.numerator && denominator == other.denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string(const Fraction &f)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << f.numerator << "/" << f.denominator;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************/
|
||||||
|
/* _ _ _ */
|
||||||
|
/* | \| |___ __| |___ ___ */
|
||||||
|
/* | .` / _ \/ _` / -_|_-< */
|
||||||
|
/* |_|\_\___/\__,_\___/__/ */
|
||||||
|
/* */
|
||||||
|
/***************************/
|
||||||
|
Node::Node(const Fraction &&val, i64 left, i64 right)
|
||||||
|
: value{val}, left{left}, right{right}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeAllocator::NodeAllocator(u64 capacity)
|
||||||
|
{
|
||||||
|
vec.reserve(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 NodeAllocator::alloc(Node n)
|
||||||
|
{
|
||||||
|
u64 ind = vec.size();
|
||||||
|
vec.push_back(n);
|
||||||
|
return ind;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: This is annoying. DRY?
|
||||||
|
Node &NodeAllocator::get_ref(u64 n)
|
||||||
|
{
|
||||||
|
if (n >= vec.size())
|
||||||
|
return vec[0];
|
||||||
|
return vec[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
Node NodeAllocator::get_val(u64 n) const
|
||||||
|
{
|
||||||
|
if (n >= vec.size())
|
||||||
|
return vec[0];
|
||||||
|
return vec[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
void indent_depth(int depth, std::stringstream &ss)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < depth; ++i)
|
||||||
|
ss << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_string(const NodeAllocator &allocator, const i64 n, int depth)
|
||||||
|
{
|
||||||
|
if (n < 0)
|
||||||
|
return "NIL";
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
Node x = allocator.get_val(n);
|
||||||
|
ss << "(" << to_string(x.value) << "\n";
|
||||||
|
|
||||||
|
indent_depth(depth, ss);
|
||||||
|
if (x.left == -1)
|
||||||
|
ss << "NIL";
|
||||||
|
else
|
||||||
|
ss << to_string(allocator, x.left, depth + 1);
|
||||||
|
ss << "\n";
|
||||||
|
|
||||||
|
indent_depth(depth, ss);
|
||||||
|
if (x.right == -1)
|
||||||
|
ss << "NIL";
|
||||||
|
else
|
||||||
|
ss << to_string(allocator, x.right, depth + 1);
|
||||||
|
|
||||||
|
ss << ")";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cw::node
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Aryadev Chavali
|
||||||
|
|
||||||
|
* 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 Version 2 for
|
||||||
|
* details.
|
||||||
|
|
||||||
|
* You may distribute and modify this code under the terms of the GNU General
|
||||||
|
* Public License Version 2, which you should have received a copy of along with
|
||||||
|
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
66
src/node.hpp
Normal file
66
src/node.hpp
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/* node.hpp: Definition of fractions and nodes on the Calkin-Wilf tree
|
||||||
|
* Created: 2025-11-27
|
||||||
|
* Author: Aryadev Chavali
|
||||||
|
* License: See end of file
|
||||||
|
* Commentary:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NODE_HPP
|
||||||
|
#define NODE_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base.hpp"
|
||||||
|
|
||||||
|
namespace cw::node
|
||||||
|
{
|
||||||
|
struct Fraction
|
||||||
|
{
|
||||||
|
u64 numerator, denominator; // always simplified
|
||||||
|
f64 norm;
|
||||||
|
|
||||||
|
Fraction(u64 numerator = 0, u64 denominator = 1);
|
||||||
|
|
||||||
|
// Complete ordering on Fractions
|
||||||
|
bool operator<(const Fraction other);
|
||||||
|
bool operator==(const Fraction &other);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string to_string(const Fraction &);
|
||||||
|
|
||||||
|
struct Node
|
||||||
|
{
|
||||||
|
Fraction value;
|
||||||
|
i64 left, right;
|
||||||
|
|
||||||
|
Node(const Fraction &&val = {}, i64 left = -1, i64 right = -1);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NodeAllocator
|
||||||
|
{
|
||||||
|
std::vector<Node> vec;
|
||||||
|
|
||||||
|
NodeAllocator(u64 capacity = 256);
|
||||||
|
u64 alloc(Node n);
|
||||||
|
Node get_val(u64 n) const;
|
||||||
|
Node &get_ref(u64 n);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string to_string(const NodeAllocator &, const i64, int depth = 1);
|
||||||
|
} // namespace cw::node
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Aryadev Chavali
|
||||||
|
|
||||||
|
* 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 Version 2 for
|
||||||
|
* details.
|
||||||
|
|
||||||
|
* You may distribute and modify this code under the terms of the GNU General
|
||||||
|
* Public License Version 2, which you should have received a copy of along with
|
||||||
|
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
153
src/numerics.cpp
153
src/numerics.cpp
@@ -1,153 +0,0 @@
|
|||||||
/* numerics.cpp: Implementation of numerics
|
|
||||||
* Created: 2024-07-26
|
|
||||||
* Author: Aryadev Chavali
|
|
||||||
* License: See end of file
|
|
||||||
* Commentary:
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "./numerics.hpp"
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
Fraction::Fraction(word_t numerator, word_t denominator)
|
|
||||||
: numerator{numerator}, denominator{denominator},
|
|
||||||
norm{numerator / ((long double)denominator)}
|
|
||||||
{
|
|
||||||
word_t hcf = gcd(MIN(numerator, denominator), MAX(numerator, denominator));
|
|
||||||
numerator /= hcf;
|
|
||||||
denominator /= hcf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// floating point arithmetic inaccuracies blah blah blah better to use
|
|
||||||
// simplified fractions here
|
|
||||||
|
|
||||||
bool Fraction::operator<(const Fraction other)
|
|
||||||
{
|
|
||||||
if (other.denominator == denominator)
|
|
||||||
return numerator < other.numerator;
|
|
||||||
// TODO: Is it better to use the GCD?
|
|
||||||
return (numerator * other.denominator) < (other.numerator * denominator);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Fraction::operator==(const Fraction &other)
|
|
||||||
{
|
|
||||||
return numerator == other.numerator && denominator == other.denominator;
|
|
||||||
}
|
|
||||||
|
|
||||||
Node::Node(Fraction val, index_t left, index_t right)
|
|
||||||
: value{val}, left{left}, right{right}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeAllocator::NodeAllocator(word_t capacity)
|
|
||||||
{
|
|
||||||
vec.reserve(capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
word_t NodeAllocator::alloc(Node n)
|
|
||||||
{
|
|
||||||
word_t ind = vec.size();
|
|
||||||
vec.push_back(n);
|
|
||||||
return ind;
|
|
||||||
}
|
|
||||||
|
|
||||||
// WHY DO I NEED TO DO IT TWICE REEEEEEE
|
|
||||||
Node &NodeAllocator::getRef(word_t n)
|
|
||||||
{
|
|
||||||
if (n >= vec.size())
|
|
||||||
return vec[0];
|
|
||||||
return vec[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
Node NodeAllocator::getVal(word_t n) const
|
|
||||||
{
|
|
||||||
if (n >= vec.size())
|
|
||||||
return vec[0];
|
|
||||||
return vec[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
word_t gcd(word_t a, word_t b)
|
|
||||||
{
|
|
||||||
if (a == b)
|
|
||||||
return a;
|
|
||||||
else if (a <= 1 || b <= 1)
|
|
||||||
return 1;
|
|
||||||
for (word_t r = b % a; r != 0; b = a, a = r, r = b % a)
|
|
||||||
continue;
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<Fraction, Fraction, Fraction> iterate(std::queue<word_t> &queue,
|
|
||||||
NodeAllocator &allocator)
|
|
||||||
{
|
|
||||||
if (queue.empty())
|
|
||||||
return {};
|
|
||||||
word_t index = queue.front();
|
|
||||||
Node node = allocator.getVal(index);
|
|
||||||
if (!node.left.has_value())
|
|
||||||
{
|
|
||||||
allocator.getRef(index).left = allocator.alloc(Fraction{
|
|
||||||
node.value.numerator, node.value.numerator + node.value.denominator});
|
|
||||||
}
|
|
||||||
if (!node.right.has_value())
|
|
||||||
{
|
|
||||||
allocator.getRef(index).right = allocator.alloc(Fraction{
|
|
||||||
node.value.numerator + node.value.denominator, node.value.denominator});
|
|
||||||
}
|
|
||||||
queue.pop();
|
|
||||||
queue.push(allocator.getVal(index).left.value());
|
|
||||||
queue.push(allocator.getVal(index).right.value());
|
|
||||||
node = allocator.getVal(index);
|
|
||||||
// NOTE: We can be assured that left and right DO have values
|
|
||||||
return std::tuple(allocator.getVal(node.left.value()).value, node.value,
|
|
||||||
allocator.getVal(node.right.value()).value);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string to_string(const Fraction &f)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << f.numerator << "/" << f.denominator;
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void indent_depth(int depth, std::stringstream &ss)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < depth; ++i)
|
|
||||||
ss << " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string to_string(const NodeAllocator &allocator, const index_t n,
|
|
||||||
int depth)
|
|
||||||
{
|
|
||||||
if (!n.has_value())
|
|
||||||
return "NIL";
|
|
||||||
std::stringstream ss;
|
|
||||||
Node x = allocator.getVal(n.value());
|
|
||||||
ss << "(" << to_string(x.value) << "\n";
|
|
||||||
indent_depth(depth, ss);
|
|
||||||
if (x.left == -1)
|
|
||||||
ss << "NIL";
|
|
||||||
else
|
|
||||||
ss << to_string(allocator, x.left, depth + 1);
|
|
||||||
ss << "\n";
|
|
||||||
indent_depth(depth, ss);
|
|
||||||
if (x.right == -1)
|
|
||||||
ss << "NIL";
|
|
||||||
else
|
|
||||||
ss << to_string(allocator, x.right, depth + 1);
|
|
||||||
ss << ")";
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copyright (C) 2024, 2025 Aryadev Chavali
|
|
||||||
|
|
||||||
* 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 Version 2 for
|
|
||||||
* details.
|
|
||||||
|
|
||||||
* You may distribute and modify this code under the terms of the GNU General
|
|
||||||
* Public License Version 2, which you should have received a copy of along with
|
|
||||||
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
*/
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
/* numerics.hpp: Computation necessary for generating the tree
|
|
||||||
* Created: 2024-07-26
|
|
||||||
* Author: Aryadev Chavali
|
|
||||||
* License: See end of file
|
|
||||||
* Commentary:
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef NUMERICS_HPP
|
|
||||||
#define NUMERICS_HPP
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <queue>
|
|
||||||
#include <string>
|
|
||||||
#include <tuple>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
|
||||||
#define MAX(A, B) ((B) < (A) ? (A) : (B))
|
|
||||||
typedef uint64_t word_t;
|
|
||||||
typedef std::optional<word_t> index_t;
|
|
||||||
|
|
||||||
struct Fraction
|
|
||||||
{
|
|
||||||
word_t numerator, denominator;
|
|
||||||
long double norm;
|
|
||||||
|
|
||||||
Fraction(word_t numerator = 0, word_t denominator = 1);
|
|
||||||
bool operator<(const Fraction other);
|
|
||||||
bool operator==(const Fraction &other);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Node
|
|
||||||
{
|
|
||||||
Fraction value;
|
|
||||||
index_t left, right;
|
|
||||||
|
|
||||||
Node(Fraction val = {}, index_t left = std::nullopt,
|
|
||||||
index_t right = std::nullopt);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NodeAllocator
|
|
||||||
{
|
|
||||||
std::vector<Node> vec;
|
|
||||||
|
|
||||||
NodeAllocator(word_t capacity = 0);
|
|
||||||
word_t alloc(Node n);
|
|
||||||
Node getVal(word_t n) const;
|
|
||||||
Node &getRef(word_t n);
|
|
||||||
};
|
|
||||||
|
|
||||||
word_t gcd(word_t a, word_t b);
|
|
||||||
std::tuple<Fraction, Fraction, Fraction> iterate(std::queue<word_t> &queue,
|
|
||||||
NodeAllocator &allocator);
|
|
||||||
|
|
||||||
std::string to_string(const Fraction &);
|
|
||||||
std::string to_string(const NodeAllocator &, const index_t, int depth = 1);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Copyright (C) 2024, 2025 Aryadev Chavali
|
|
||||||
|
|
||||||
* 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 Version 2 for
|
|
||||||
* details.
|
|
||||||
|
|
||||||
* You may distribute and modify this code under the terms of the GNU General
|
|
||||||
* Public License Version 2, which you should have received a copy of along with
|
|
||||||
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
41
src/state.cpp
Normal file
41
src/state.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/* state.cpp:
|
||||||
|
* Created: 2025-11-27
|
||||||
|
* Author: Aryadev Chavali
|
||||||
|
* License: See end of file
|
||||||
|
* Commentary:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "state.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace cw::state
|
||||||
|
{
|
||||||
|
void DrawState::compute_bounds()
|
||||||
|
{
|
||||||
|
state.mutex.lock();
|
||||||
|
bounds.leftmost = state.allocator.get_val(0);
|
||||||
|
while (bounds.leftmost.left >= 0)
|
||||||
|
bounds.leftmost = state.allocator.get_val(bounds.leftmost.left);
|
||||||
|
|
||||||
|
bounds.rightmost = state.allocator.get_val(0);
|
||||||
|
while (bounds.rightmost.right >= 0)
|
||||||
|
bounds.rightmost = state.allocator.get_val(bounds.rightmost.right);
|
||||||
|
state.mutex.unlock();
|
||||||
|
|
||||||
|
bounds.lower_val = std::floorl(bounds.leftmost.value.norm);
|
||||||
|
bounds.upper_val = std::ceill(bounds.rightmost.value.norm);
|
||||||
|
}
|
||||||
|
} // namespace cw::state
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Aryadev Chavali
|
||||||
|
|
||||||
|
* 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 Version 2 for
|
||||||
|
* details.
|
||||||
|
|
||||||
|
* You may distribute and modify this code under the terms of the GNU General
|
||||||
|
* Public License Version 2, which you should have received a copy of along with
|
||||||
|
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
58
src/state.hpp
Normal file
58
src/state.hpp
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/* state.hpp: General state of the simulation
|
||||||
|
* Created: 2025-11-27
|
||||||
|
* Author: Aryadev Chavali
|
||||||
|
* License: See end of file
|
||||||
|
* Commentary:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STATE_HPP
|
||||||
|
#define STATE_HPP
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include "base.hpp"
|
||||||
|
#include "node.hpp"
|
||||||
|
|
||||||
|
namespace cw::state
|
||||||
|
{
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
cw::node::NodeAllocator allocator;
|
||||||
|
std::queue<u64> queue;
|
||||||
|
|
||||||
|
bool pause_work, stop_work;
|
||||||
|
std::mutex mutex;
|
||||||
|
|
||||||
|
State(void) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DrawState
|
||||||
|
{
|
||||||
|
State &state;
|
||||||
|
struct Bounds
|
||||||
|
{
|
||||||
|
cw::node::Node leftmost, rightmost;
|
||||||
|
f64 lower_val, upper_val;
|
||||||
|
} bounds;
|
||||||
|
|
||||||
|
DrawState(State &state) : state{state} {};
|
||||||
|
|
||||||
|
void compute_bounds(void);
|
||||||
|
};
|
||||||
|
} // namespace cw::state
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Aryadev Chavali
|
||||||
|
|
||||||
|
* 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 Version 2 for
|
||||||
|
* details.
|
||||||
|
|
||||||
|
* You may distribute and modify this code under the terms of the GNU General
|
||||||
|
* Public License Version 2, which you should have received a copy of along with
|
||||||
|
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
81
src/worker.cpp
Normal file
81
src/worker.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/* worker.cpp: Implementation of workers in our simulation
|
||||||
|
* Created: 2025-11-27
|
||||||
|
* Author: Aryadev Chavali
|
||||||
|
* License: See end of file
|
||||||
|
* Commentary:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include "worker.hpp"
|
||||||
|
|
||||||
|
namespace cw::worker
|
||||||
|
{
|
||||||
|
using cw::node::Fraction;
|
||||||
|
using cw::node::Node;
|
||||||
|
|
||||||
|
void do_iteration(State &state)
|
||||||
|
{
|
||||||
|
state.mutex.lock();
|
||||||
|
if (state.queue.empty())
|
||||||
|
{
|
||||||
|
// Unlock since there isn't any work to be done.
|
||||||
|
state.mutex.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
u64 index = state.queue.front();
|
||||||
|
state.queue.pop();
|
||||||
|
|
||||||
|
Node node = state.allocator.get_val(index);
|
||||||
|
|
||||||
|
i64 left = node.left, right = node.right;
|
||||||
|
if (left < 0)
|
||||||
|
{
|
||||||
|
left = state.allocator.alloc(Fraction{
|
||||||
|
node.value.numerator, node.value.numerator + node.value.denominator});
|
||||||
|
}
|
||||||
|
if (right < 0)
|
||||||
|
{
|
||||||
|
right = state.allocator.alloc(
|
||||||
|
Fraction{node.value.numerator + node.value.denominator,
|
||||||
|
node.value.denominator});
|
||||||
|
}
|
||||||
|
|
||||||
|
Node &node_ref = state.allocator.get_ref(index);
|
||||||
|
node_ref.left = left;
|
||||||
|
node_ref.right = right;
|
||||||
|
|
||||||
|
state.queue.push(left);
|
||||||
|
state.queue.push(right);
|
||||||
|
state.mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker(State &state)
|
||||||
|
{
|
||||||
|
while (!state.stop_work)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(THREAD_GENERAL_DELAY);
|
||||||
|
while (state.pause_work)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(THREAD_PAUSE_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_iteration(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace cw::worker
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Aryadev Chavali
|
||||||
|
|
||||||
|
* 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 Version 2 for
|
||||||
|
* details.
|
||||||
|
|
||||||
|
* You may distribute and modify this code under the terms of the GNU General
|
||||||
|
* Public License Version 2, which you should have received a copy of along with
|
||||||
|
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
49
src/worker.hpp
Normal file
49
src/worker.hpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/* worker.hpp: Definition of "workers" who will generate nodes on the tree
|
||||||
|
* Created: 2025-11-27
|
||||||
|
* Author: Aryadev Chavali
|
||||||
|
* License: See end of file
|
||||||
|
* Commentary:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WORKER_HPP
|
||||||
|
#define WORKER_HPP
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include "state.hpp"
|
||||||
|
|
||||||
|
namespace cw::worker
|
||||||
|
{
|
||||||
|
using cw::node::NodeAllocator;
|
||||||
|
using cw::state::State;
|
||||||
|
constexpr auto THREAD_PAUSE_DELAY = std::chrono::milliseconds(1000);
|
||||||
|
constexpr auto THREAD_GENERAL_DELAY = std::chrono::milliseconds(1);
|
||||||
|
|
||||||
|
// Performs a single iteration which consists of the following:
|
||||||
|
// 1) pop an index off the iteration queue
|
||||||
|
// 2) generate the children of the node at that index
|
||||||
|
// 3) push the indices of the children onto the iteration queue
|
||||||
|
// Each step will block on the relevant mutex for the resource (1,3 will block
|
||||||
|
// on the queue mutex, 2 will block on the allocator mutex) so is thread safe.
|
||||||
|
void do_iteration(State &state);
|
||||||
|
|
||||||
|
// Steady living thread worker which performs iterations. If state.pause_work
|
||||||
|
// is true, thread will pause until otherwise.
|
||||||
|
void worker(State &state);
|
||||||
|
} // namespace cw::worker
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Copyright (C) 2025 Aryadev Chavali
|
||||||
|
|
||||||
|
* 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 Version 2 for
|
||||||
|
* details.
|
||||||
|
|
||||||
|
* You may distribute and modify this code under the terms of the GNU General
|
||||||
|
* Public License Version 2, which you should have received a copy of along with
|
||||||
|
* this program. If not, please go to <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
Reference in New Issue
Block a user