Compare commits

...

24 Commits

Author SHA1 Message Date
Aryadev Chavali
424fab2e40 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 :)
2025-12-12 04:08:01 +00:00
Aryadev Chavali
6ffa63f7ac Make general delay 1ms
Just for my puny eyes to see.
2025-12-12 04:07:43 +00:00
Aryadev Chavali
2dbe3d0a58 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!
2025-12-12 03:55:43 +00:00
Aryadev Chavali
7e801df280 no more numerics 2025-12-12 03:55:43 +00:00
Aryadev Chavali
f70517cf41 Simplify mutex locking scheme (lock at start, unlock at end) 2025-12-12 03:08:56 +00:00
Aryadev Chavali
00858cf74f 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.
2025-11-28 17:24:33 +00:00
Aryadev Chavali
3e065e50a9 Disable previous work
We're going to rewrite this
2025-11-28 17:24:12 +00:00
Aryadev Chavali
ab0f152742 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.
2025-11-28 17:23:05 +00:00
Aryadev Chavali
014821ceb5 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.
2025-11-28 17:22:39 +00:00
Aryadev Chavali
1bd8a9f7b2 Add run component to build script, activated by passing argument run 2025-11-28 17:21:21 +00:00
Aryadev Chavali
6f620644bf Define constructors for state.hpp explicitly 2025-11-27 01:57:10 +00:00
Aryadev Chavali
0008c31f53 Add src/worker.cpp to build pipeline 2025-11-27 01:53:20 +00:00
Aryadev Chavali
f8068c4a15 Implement the two worker functions 2025-11-27 01:52:52 +00:00
Aryadev Chavali
66c56f2c15 Define thread safe worker functions for generating new children on the tree 2025-11-27 01:51:58 +00:00
Aryadev Chavali
6247f2af49 Add src/state.cpp to the build pipeline 2025-11-27 01:44:23 +00:00
Aryadev Chavali
40e07e03f2 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.
2025-11-27 01:42:24 +00:00
Aryadev Chavali
0abd353368 Implement cw::state::DrawState::compute_bounds 2025-11-27 01:30:35 +00:00
Aryadev Chavali
0ac316ada4 Define the general state of the sim (extract from main.cpp) 2025-11-27 01:30:21 +00:00
Aryadev Chavali
a03fa13a07 Implementation (copied from numerics) 2025-11-27 01:30:03 +00:00
Aryadev Chavali
ff9a2851d4 Switch to using i64's instead of optional u64 in Node 2025-11-27 01:30:03 +00:00
Aryadev Chavali
6c2bc93874 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.
2025-11-27 01:30:03 +00:00
Aryadev Chavali
7a4d158d2f Add gcd to base.hpp 2025-11-27 01:30:03 +00:00
Aryadev Chavali
e032303773 Define node.hpp (move definitions away from numerics.hpp) 2025-11-27 01:30:03 +00:00
Aryadev Chavali
9821a2ab15 Define basic type aliases and useful functions/macros for use throughout the system 2025-11-27 01:02:13 +00:00
11 changed files with 628 additions and 230 deletions

View File

@@ -2,9 +2,15 @@
set -xe
OUT="cw_tree.out"
GFLAGS="-Wall -Wextra -Wswitch-enum -std=c++17"
DFLAGS="-ggdb -fsanitize=address -fsanitize=undefined"
CFLAGS="$GFLAGS $DFLAGS"
LIBS="-lraylib -lm"
c++ $CFLAGS -o cw_tree.out src/numerics.cpp src/main.cpp $LIBS
c++ $CFLAGS -o $OUT src/node.cpp src/state.cpp src/worker.cpp src/main.cpp $LIBS
if [ "$1" = "run" ]
then
./$OUT
fi

57
src/base.hpp Normal file
View 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/>.
*/

View File

@@ -5,18 +5,21 @@
* Commentary: 2024-07-25
*/
#include "./numerics.hpp"
#include <chrono>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <sstream>
#include <stack>
#include <thread>
#include <tuple>
#include <raylib.h>
#include "base.hpp"
#include "node.hpp"
#include "worker.hpp"
#define WIDTH 1024
#define HEIGHT 1024
#define FONT_SIZE 20
@@ -24,14 +27,17 @@
#define LINE_TOP (7 * 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)};
int width = MeasureText(s.c_str(), FONT_SIZE);
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;
int width;
@@ -40,6 +46,133 @@ void draw_fraction(Fraction f, word_t x, word_t y)
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(const DrawState &ds, const 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);
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));
}
}
}
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
std::thread threads[] = {
std::thread(cw::worker::worker, std::ref(state)),
std::thread(cw::worker::worker, std::ref(state)),
std::thread(cw::worker::worker, std::ref(state)),
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);
}
// 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
{
NodeAllocator allocator;
@@ -209,6 +342,7 @@ int main(void)
CloseWindow();
return 0;
}
#endif
/* Copyright (C) 2024, 2025 Aryadev Chavali

133
src/node.cpp Normal file
View 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
View 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/>.
*/

View File

@@ -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/>.
*/

View File

@@ -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/>.
*/

39
src/state.cpp Normal file
View File

@@ -0,0 +1,39 @@
/* 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()
{
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);
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
View 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
{
const State &state;
struct Bounds
{
cw::node::Node leftmost, rightmost;
f64 lower_val, upper_val;
} bounds;
DrawState(const 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
View 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
View 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/>.
*/