Compare commits

...

7 Commits

Author SHA1 Message Date
Aryadev Chavali
925c5e0372 Add raylib 5.5 to our codebase and statically link with it
This way you won't need to install raylib as a dependency in order to
use it.  You'll still need its dependencies, like glfw, but this
should remove one layer of indirection.
2025-12-12 06:12:38 +00:00
Aryadev Chavali
82604480c0 Setup variable in build script for user to adjust thread timers 2025-12-12 05:42:13 +00:00
Aryadev Chavali
ca3bb64aae Allow compiler-level declarations for the thread timers
We use a #define here to set these up - if you define them via a -D
block yourself, you can adjust these as you require.
2025-12-12 05:42:10 +00:00
Aryadev Chavali
0cb14af26e Push THREAD_GENERAL_DELAY to 100ms 2025-12-12 05:16:01 +00:00
Aryadev Chavali
01484a65ec Add a camera to move around the numberline, add text for the bounds 2025-12-12 05:15:17 +00:00
Aryadev Chavali
8cd700a9e0 Delete old main 2025-12-12 04:42:16 +00:00
Aryadev Chavali
38d531ca31 feat! Multithreading
commit a763bff532
Author: 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)

commit 7112937b0b
Author: 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.

commit a5666328b7
Author: 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.

commit 5d78cb20df
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Fri Dec 12 04:28:21 2025 +0000

    Adjust TODOs

commit 9d3a202c27
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Fri Dec 12 04:09:50 2025 +0000

    Implement stepping via KEY_SPACE

commit 424fab2e40
Author: 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 :)

commit 6ffa63f7ac
Author: 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.

commit 2dbe3d0a58
Author: 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!

commit 7e801df280
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Fri Dec 12 03:53:51 2025 +0000

    no more numerics

commit f70517cf41
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Fri Dec 12 03:08:56 2025 +0000

    Simplify mutex locking scheme (lock at start, unlock at end)

commit 00858cf74f
Author: 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.

commit 3e065e50a9
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Fri Nov 28 17:24:12 2025 +0000

    Disable previous work

    We're going to rewrite this

commit ab0f152742
Author: 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.

commit 014821ceb5
Author: 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.

commit 1bd8a9f7b2
Author: 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`

commit 6f620644bf
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Thu Nov 27 01:57:10 2025 +0000

    Define constructors for state.hpp explicitly

commit 0008c31f53
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Thu Nov 27 01:53:10 2025 +0000

    Add src/worker.cpp to build pipeline

commit f8068c4a15
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Thu Nov 27 01:52:52 2025 +0000

    Implement the two worker functions

commit 66c56f2c15
Author: 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

commit 6247f2af49
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Thu Nov 27 01:44:23 2025 +0000

    Add src/state.cpp to the build pipeline

commit 40e07e03f2
Author: 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.

commit 0abd353368
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Thu Nov 27 01:30:35 2025 +0000

    Implement cw::state::DrawState::compute_bounds

commit 0ac316ada4
Author: 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)

commit a03fa13a07
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Thu Nov 27 01:19:52 2025 +0000

    Implementation (copied from numerics)

commit ff9a2851d4
Author: 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

commit 6c2bc93874
Author: 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.

commit 7a4d158d2f
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Thu Nov 27 01:02:31 2025 +0000

    Add gcd to base.hpp

commit e032303773
Author: Aryadev Chavali <aryadev@aryadevchavali.com>
Date:   Thu Nov 27 00:56:45 2025 +0000

    Define node.hpp (move definitions away from numerics.hpp)

commit 9821a2ab15
Author: 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
2025-12-12 04:33:54 +00:00
19 changed files with 13340 additions and 373 deletions

View File

@@ -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
generate a dense number line over many iterations.
* TODOs
** TODO Multithreading
SCHEDULED: <2025-11-18 Tue>
** 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.
** 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
multiple nodes generated at once, which would speed up the
implementation.
@@ -29,7 +49,7 @@ implementation.
Might need to study my current implementation to see if it could be
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
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
(~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
one for the allocator to ensure we're not messing up our indices in
the nodes.
We could just set these up in the state structure itself to make
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>
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
minimum, ensuring other threads can get in on the action.
*** TODO 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.
*** DONE Setup a thread pool utilising state and iterate

View File

@@ -2,9 +2,25 @@
set -xe
GFLAGS="-Wall -Wextra -Wswitch-enum -std=c++17"
DFLAGS="-ggdb -fsanitize=address -fsanitize=undefined"
CFLAGS="$GFLAGS $DFLAGS"
LIBS="-lraylib -lm"
OUT="cw_tree.out"
GFLAGS="-Wall -Wextra -Wswitch-enum -std=c++17 -Iraylib-5.5_linux_amd64/include"
LIBS="-Lraylib-5.5_linux_amd64/lib -l:libraylib.a" # link statically with raylib
VARFLAGS="-DTHREAD_PAUSE_MS=1000 -DTHREAD_GENERAL_MS=1"
c++ $CFLAGS -o cw_tree.out src/numerics.cpp src/main.cpp $LIBS
DFLAGS="-ggdb -fsanitize=address -fsanitize=undefined"
RFLAGS="-O2"
CFLAGS="$GFLAGS $VARFLAGS $DFLAGS"
if [ "$1" = "release" ]
then
CFLAGS="$GFLAGS $VARFLAGS $RFLAGS"
shift 1
elif [ "$1" = "debug" ]
then
shift 1
fi
c++ $CFLAGS -o $OUT src/node.cpp src/state.cpp src/worker.cpp src/main.cpp $LIBS
if [ "$1" = "run" ]
then
./$OUT
fi

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
This software is provided "as-is", without any express or implied warranty. In no event
will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you
wrote the original software. If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented
as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@@ -0,0 +1,150 @@
<img align="left" style="width:260px" src="https://github.com/raysan5/raylib/blob/master/logo/raylib_logo_animation.gif" width="288px">
**raylib is a simple and easy-to-use library to enjoy videogames programming.**
raylib is highly inspired by Borland BGI graphics lib and by XNA framework and it's especially well suited for prototyping, tooling, graphical applications, embedded systems and education.
*NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no debug button... just coding in the most pure spartan-programmers way.*
Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html)
---
<br>
[![GitHub Releases Downloads](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases)
[![GitHub Stars](https://img.shields.io/github/stars/raysan5/raylib?style=flat&label=stars)](https://github.com/raysan5/raylib/stargazers)
[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/5.0)](https://github.com/raysan5/raylib/commits/master)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/raysan5?label=sponsors)](https://github.com/sponsors/raysan5)
[![Packaging Status](https://repology.org/badge/tiny-repos/raylib.svg)](https://repology.org/project/raylib/versions)
[![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE)
[![Discord Members](https://img.shields.io/discord/426912293134270465.svg?label=Discord&logo=discord)](https://discord.gg/raylib)
[![Reddit Static Badge](https://img.shields.io/badge/-r%2Fraylib-red?style=flat&logo=reddit&label=reddit)](https://www.reddit.com/r/raylib/)
[![Youtube Subscribers](https://img.shields.io/youtube/channel/subscribers/UC8WIBkhYb5sBNqXO1mZ7WSQ?style=flat&label=Youtube&logo=youtube)](https://www.youtube.com/c/raylib)
[![Twitch Status](https://img.shields.io/twitch/status/raysan5?style=flat&label=Twitch&logo=twitch)](https://www.twitch.tv/raysan5)
[![Windows](https://github.com/raysan5/raylib/workflows/Windows/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows)
[![Linux](https://github.com/raysan5/raylib/workflows/Linux/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux)
[![macOS](https://github.com/raysan5/raylib/workflows/macOS/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AmacOS)
[![WebAssembly](https://github.com/raysan5/raylib/workflows/WebAssembly/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWebAssembly)
[![CMakeBuilds](https://github.com/raysan5/raylib/workflows/CMakeBuilds/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ACMakeBuilds)
[![Windows Examples](https://github.com/raysan5/raylib/actions/workflows/windows_examples.yml/badge.svg)](https://github.com/raysan5/raylib/actions/workflows/windows_examples.yml)
[![Linux Examples](https://github.com/raysan5/raylib/actions/workflows/linux_examples.yml/badge.svg)](https://github.com/raysan5/raylib/actions/workflows/linux_examples.yml)
features
--------
- **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external)
- Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!**
- Written in plain C code (C99) using PascalCase/camelCase notation
- Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3, ES 2.0, ES 3.0**)
- **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h)
- Multiple **Fonts** formats supported (TTF, OTF, FNT, BDF, sprite fonts)
- Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC)
- **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more!
- Flexible Materials system, supporting classic maps and **PBR maps**
- **Animated 3D models** supported (skeletal bones animation) (IQM, M3D, glTF)
- Shaders support, including model shaders and **postprocessing** shaders
- **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h)
- Audio loading and playing with streaming support (WAV, QOA, OGG, MP3, FLAC, XM, MOD)
- **VR stereo rendering** support with configurable HMD device parameters
- Huge examples collection with [+140 code examples](https://github.com/raysan5/raylib/tree/master/examples)!
- Bindings to [+70 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)!
- **Free and open source**
basic example
--------------
This is a basic raylib example, it creates a window and draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window).
```c
#include "raylib.h"
int main(void)
{
InitWindow(800, 450, "raylib [core] example - basic window");
while (!WindowShouldClose())
{
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
EndDrawing();
}
CloseWindow();
return 0;
}
```
build and installation
----------------------
raylib binary releases for Windows, Linux, macOS, Android and HTML5 are available at the [Github Releases page](https://github.com/raysan5/raylib/releases).
raylib is also available via multiple package managers on multiple OS distributions.
#### Installing and building raylib on multiple platforms
[raylib Wiki](https://github.com/raysan5/raylib/wiki#development-platforms) contains detailed instructions on building and usage on multiple platforms.
- [Working on Windows](https://github.com/raysan5/raylib/wiki/Working-on-Windows)
- [Working on macOS](https://github.com/raysan5/raylib/wiki/Working-on-macOS)
- [Working on GNU Linux](https://github.com/raysan5/raylib/wiki/Working-on-GNU-Linux)
- [Working on Chrome OS](https://github.com/raysan5/raylib/wiki/Working-on-Chrome-OS)
- [Working on FreeBSD](https://github.com/raysan5/raylib/wiki/Working-on-FreeBSD)
- [Working on Raspberry Pi](https://github.com/raysan5/raylib/wiki/Working-on-Raspberry-Pi)
- [Working for Android](https://github.com/raysan5/raylib/wiki/Working-for-Android)
- [Working for Web (HTML5)](https://github.com/raysan5/raylib/wiki/Working-for-Web-(HTML5))
- [Working anywhere with CMake](https://github.com/raysan5/raylib/wiki/Working-with-CMake)
*Note that the Wiki is open for edit, if you find some issues while building raylib for your target platform, feel free to edit the Wiki or open an issue related to it.*
#### Setup raylib with multiple IDEs
raylib has been developed on Windows platform using [Notepad++](https://notepad-plus-plus.org/) and [MinGW GCC](https://www.mingw-w64.org/) compiler but it can be used with other IDEs on multiple platforms.
[Projects directory](https://github.com/raysan5/raylib/tree/master/projects) contains several ready-to-use **project templates** to build raylib and code examples with multiple IDEs.
*Note that there are lots of IDEs supported, some of the provided templates could require some review, so please, if you find some issue with a template or you think they could be improved, feel free to send a PR or open a related issue.*
learning and docs
------------------
raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works.
Some additional documentation about raylib design can be found in [raylib GitHub Wiki](https://github.com/raysan5/raylib/wiki). Here are the relevant links:
- [raylib cheatsheet](https://www.raylib.com/cheatsheet/cheatsheet.html)
- [raylib architecture](https://github.com/raysan5/raylib/wiki/raylib-architecture)
- [raylib library design](https://github.com/raysan5/raylib/wiki)
- [raylib examples collection](https://github.com/raysan5/raylib/tree/master/examples)
- [raylib games collection](https://github.com/raysan5/raylib-games)
contact and networks
---------------------
raylib is present in several networks and raylib community is growing everyday. If you are using raylib and enjoying it, feel free to join us in any of these networks. The most active network is our [Discord server](https://discord.gg/raylib)! :)
- Webpage: [https://www.raylib.com](https://www.raylib.com)
- Discord: [https://discord.gg/raylib](https://discord.gg/raylib)
- Twitter: [https://www.twitter.com/raysan5](https://www.twitter.com/raysan5)
- Twitch: [https://www.twitch.tv/raysan5](https://www.twitch.tv/raysan5)
- Reddit: [https://www.reddit.com/r/raylib](https://www.reddit.com/r/raylib)
- Patreon: [https://www.patreon.com/raylib](https://www.patreon.com/raylib)
- YouTube: [https://www.youtube.com/channel/raylib](https://www.youtube.com/c/raylib)
contributors
------------
<a href="https://github.com/raysan5/raylib/graphs/contributors">
<img src="https://contrib.rocks/image?repo=raysan5/raylib&max=500&columns=20&anon=1" />
</a>
license
-------
raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software. Check [LICENSE](LICENSE) for further details.
raylib uses internally some libraries for window/graphics/inputs management and also to support different file formats loading, all those libraries are embedded with and are available in [src/external](https://github.com/raysan5/raylib/tree/master/src/external) directory. Check [raylib dependencies LICENSES](https://github.com/raysan5/raylib/wiki/raylib-dependencies) on [raylib Wiki](https://github.com/raysan5/raylib/wiki) for details.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

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,33 +5,41 @@
* 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 <raymath.h>
#include "base.hpp"
#include "node.hpp"
#include "worker.hpp"
#define WIDTH 1024
#define HEIGHT 1024
#define HEIGHT 800
#define FONT_SIZE 20
#define CIRCLE_SIZE 2
#define LINE_TOP (7 * HEIGHT / 16)
#define LINE_BOTTOM (9 * HEIGHT / 16)
#define N_THREADS 15
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,173 +48,155 @@ void draw_fraction(Fraction f, word_t x, word_t y)
DrawText(s.c_str(), x - width / 2, y - FONT_SIZE, FONT_SIZE, WHITE);
}
struct State
void draw_tree(DrawState &ds, State &state)
{
NodeAllocator allocator;
std::queue<word_t> iteration_queue;
word_t root;
// Number line
DrawLine(0, HEIGHT / 2, WIDTH, HEIGHT / 2, WHITE);
struct Bounds
// Bounds
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())
{
Node leftmost, rightmost;
long double lower, upper;
} bounds;
u64 node_index;
f64 norm;
std::tie(node_index, norm) = stack.top();
stack.pop();
u64 x = Remap(norm, ds.bounds.lower_val, ds.bounds.upper_val, 0, WIDTH);
DrawLine(x, LINE_TOP, x, LINE_BOTTOM, RED);
struct Iteration
{
Fraction left, centre, right;
} iteration;
State(const Fraction start) : allocator{256}
{
root = allocator.alloc(start);
iteration_queue.push(root);
bounds.leftmost = allocator.getVal(root);
bounds.rightmost = allocator.getVal(root);
compute_bounds();
}
void do_iteration(void)
{
std::tie(iteration.left, iteration.centre, iteration.right) =
iterate(iteration_queue, allocator);
compute_bound_nodes();
compute_bounds();
}
void compute_bounds()
{
bounds.lower = std::floorl(bounds.leftmost.value.norm);
bounds.upper = std::ceill(bounds.rightmost.value.norm);
}
void compute_bound_nodes()
{
bounds.leftmost = allocator.getVal(0);
while (bounds.leftmost.left.has_value())
bounds.leftmost = allocator.getVal(bounds.leftmost.left.value());
bounds.rightmost = allocator.getVal(0);
while (bounds.rightmost.right.has_value())
bounds.rightmost = allocator.getVal(bounds.rightmost.right.value());
}
constexpr word_t clamp_to_width(long double value)
{
return (WIDTH / (bounds.upper - bounds.lower)) * (value - bounds.lower);
}
void draw_bounds()
{
word_t lower_x = clamp_to_width(bounds.leftmost.value.norm);
word_t upper_x = clamp_to_width(bounds.rightmost.value.norm);
DrawLine(lower_x, LINE_TOP, lower_x, LINE_BOTTOM, WHITE);
DrawLine(upper_x, LINE_TOP, upper_x, LINE_BOTTOM, WHITE);
}
void draw_nodes()
{
std::stack<Node> stack;
stack.push(allocator.getVal(0));
while (!stack.empty())
cw::node::Node n = state.allocator.get_val(node_index);
if (n.left >= 0)
{
Node n = stack.top();
stack.pop();
word_t x = clamp_to_width(n.value.norm);
DrawLine(x, LINE_TOP, x, LINE_BOTTOM, RED);
if (n.left.has_value())
stack.push(allocator.getVal(n.left.value()));
if (n.right.has_value())
stack.push(allocator.getVal(n.right.value()));
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();
void draw_iteration_nodes()
{
word_t x_left = clamp_to_width(iteration.left.norm);
word_t x_centre = clamp_to_width(iteration.centre.norm);
word_t x_right = clamp_to_width(iteration.right.norm);
DrawLine(x_left, LINE_TOP, x_left, LINE_BOTTOM, BLUE);
DrawLine(x_right, LINE_TOP, x_right, LINE_BOTTOM, BLUE);
DrawLine(x_centre, LINE_TOP, x_centre, LINE_BOTTOM, GREEN);
}
};
DrawLine(0, LINE_TOP, 0, LINE_BOTTOM, WHITE);
DrawText("0", 0, LINE_TOP - FONT_SIZE, FONT_SIZE, WHITE);
DrawLine(WIDTH, LINE_TOP, WIDTH, LINE_BOTTOM, WHITE);
char buffer[64];
sprintf(buffer, "%d", (int)ds.bounds.upper_val);
DrawText(buffer, WIDTH - (FONT_SIZE / 2), LINE_TOP - FONT_SIZE, FONT_SIZE,
WHITE);
}
using Clock = std::chrono::steady_clock;
using Ms = std::chrono::milliseconds;
int main(void)
{
// Setup state
State state{{1, 1}};
// Setup meta text (counter, iterations, etc)
word_t count = 1, prev_count = 0;
std::stringstream format_stream;
std::string format_str;
word_t format_str_width = 0;
// Setup timer
bool is_playing = false;
// Init timer
auto time_current = Clock::now();
auto time_previous = time_current;
constexpr auto time_delta = 1;
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[N_THREADS];
for (auto i = 0; i < N_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);
// setup camera
Camera2D camera;
camera.target = {.x = 0, .y = 0};
camera.offset = {.x = WIDTH / 16, .y = 0};
camera.rotation = 0.0f;
camera.zoom = 0.8f;
InitWindow(WIDTH, HEIGHT, "Calkin-Wilf Tree");
while (!WindowShouldClose())
{
// timer logic
// Update
time_current = Clock::now();
if (is_playing &&
if (!state.pause_work &&
std::chrono::duration_cast<Ms>(time_current - time_previous).count() >=
time_delta)
{
time_previous = time_current;
state.do_iteration();
count += 2;
count = state.allocator.vec.size();
}
// Input logic
if (IsKeyDown(KEY_SPACE))
{
is_playing = true;
}
else if (IsKeyUp(KEY_SPACE))
{
is_playing = false;
}
if (IsKeyPressed(KEY_PERIOD))
{
state.do_iteration();
count += 2;
}
// Meta text logic
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(state.bounds.leftmost.value)
<< "Lower=" << to_string(draw_state.bounds.leftmost.value)
<< "\n\n"
<< "Upper=" << to_string(state.bounds.rightmost.value);
<< "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;
if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
{
Vector2 delta = GetMouseDelta();
delta = Vector2Scale(delta, -1.0f / camera.zoom);
camera.target = Vector2Add(camera.target, delta);
}
float wheel = GetMouseWheelMove();
if (wheel != 0)
{
Vector2 mouse_to_world = GetScreenToWorld2D(GetMousePosition(), camera);
camera.offset = GetMousePosition();
camera.target = mouse_to_world;
camera.zoom = Clamp(camera.zoom + (wheel * 0.1f), 0.125, 64);
printf("%lf\n", camera.zoom);
}
// Draw
ClearBackground(BLACK);
BeginDrawing();
DrawLine(0, HEIGHT / 2, WIDTH, HEIGHT / 2, WHITE);
state.draw_nodes();
state.draw_bounds();
state.draw_iteration_nodes();
DrawText(format_str.c_str(), WIDTH / 2 - format_str_width / 2, HEIGHT / 8,
FONT_SIZE, WHITE);
BeginMode2D(camera);
draw_tree(draw_state, state);
EndMode2D();
DrawText(format_str.c_str(), (31 * WIDTH / 32) - format_str_width / 2,
HEIGHT / 32, FONT_SIZE, WHITE);
EndDrawing();
}
CloseWindow();
state.stop_work = true;
for (auto &thread : threads)
{
thread.join();
}
return 0;
}

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

40
src/state.cpp Normal file
View File

@@ -0,0 +1,40 @@
/* 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.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/>.
*/

62
src/state.hpp Normal file
View File

@@ -0,0 +1,62 @@
/* 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}
{
// lim n -> -∞
bounds.lower_val = 0;
};
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/>.
*/

58
src/worker.hpp Normal file
View File

@@ -0,0 +1,58 @@
/* 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"
#ifndef THREAD_PAUSE_MS
#define THREAD_PAUSE_MS 1000
#endif
#ifndef THREAD_GENERAL_MS
#define THREAD_GENERAL_MS 10
#endif
namespace cw::worker
{
using cw::node::NodeAllocator;
using cw::state::State;
constexpr auto THREAD_PAUSE_DELAY =
std::chrono::milliseconds(THREAD_PAUSE_MS);
constexpr auto THREAD_GENERAL_DELAY =
std::chrono::milliseconds(THREAD_GENERAL_MS);
// 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/>.
*/