Compare commits
14 Commits
739409d3be
...
6f620644bf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f620644bf | ||
|
|
0008c31f53 | ||
|
|
f8068c4a15 | ||
|
|
66c56f2c15 | ||
|
|
6247f2af49 | ||
|
|
40e07e03f2 | ||
|
|
0abd353368 | ||
|
|
0ac316ada4 | ||
|
|
a03fa13a07 | ||
|
|
ff9a2851d4 | ||
|
|
6c2bc93874 | ||
|
|
7a4d158d2f | ||
|
|
e032303773 | ||
|
|
9821a2ab15 |
2
build.sh
2
build.sh
@@ -7,4 +7,4 @@ 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 cw_tree.out src/node.cpp src/state.cpp src/worker.cpp src/numerics.cpp src/main.cpp $LIBS
|
||||
|
||||
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/>.
|
||||
|
||||
*/
|
||||
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/>.
|
||||
|
||||
*/
|
||||
39
src/state.cpp
Normal file
39
src/state.cpp
Normal 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
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;
|
||||
|
||||
std::mutex mutex_queue;
|
||||
std::mutex mutex_allocator;
|
||||
|
||||
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/>.
|
||||
|
||||
*/
|
||||
70
src/worker.cpp
Normal file
70
src/worker.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/* worker.cpp: Implementation of workers in our simulation
|
||||
* Created: 2025-11-27
|
||||
* Author: Aryadev Chavali
|
||||
* License: See end of file
|
||||
* Commentary:
|
||||
*/
|
||||
|
||||
#include "worker.hpp"
|
||||
#include <tuple>
|
||||
|
||||
namespace cw::worker
|
||||
{
|
||||
using cw::node::Fraction;
|
||||
using cw::node::Node;
|
||||
|
||||
std::tuple<u64, u64> generate_children(NodeAllocator &allocator, u64 index)
|
||||
{
|
||||
Node node = allocator.get_val(index);
|
||||
if (node.left < 0)
|
||||
{
|
||||
allocator.get_ref(index).left = allocator.alloc(Fraction{
|
||||
node.value.numerator, node.value.numerator + node.value.denominator});
|
||||
}
|
||||
if (node.right < 0)
|
||||
{
|
||||
allocator.get_ref(index).right = allocator.alloc(
|
||||
Fraction{node.value.numerator + node.value.denominator,
|
||||
node.value.denominator});
|
||||
}
|
||||
return {node.left, node.right};
|
||||
}
|
||||
|
||||
void do_iteration(State &state)
|
||||
{
|
||||
state.mutex_queue.lock();
|
||||
if (state.queue.empty())
|
||||
{
|
||||
// Unlock since there isn't any work to be done.
|
||||
state.mutex_queue.unlock();
|
||||
return;
|
||||
}
|
||||
u64 index = state.queue.front();
|
||||
state.queue.pop();
|
||||
state.mutex_queue.unlock();
|
||||
|
||||
u64 left_child, right_child;
|
||||
state.mutex_allocator.lock();
|
||||
std::tie(left_child, right_child) =
|
||||
generate_children(state.allocator, index);
|
||||
state.mutex_allocator.unlock();
|
||||
|
||||
state.mutex_queue.lock();
|
||||
state.queue.push(left_child);
|
||||
state.queue.push(right_child);
|
||||
state.mutex_queue.unlock();
|
||||
}
|
||||
} // 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/>.
|
||||
|
||||
*/
|
||||
46
src/worker.hpp
Normal file
46
src/worker.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/* 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 <tuple>
|
||||
|
||||
#include "state.hpp"
|
||||
|
||||
namespace cw::worker
|
||||
{
|
||||
using cw::node::NodeAllocator;
|
||||
using cw::state::State;
|
||||
|
||||
// Given `index`, return the indices of its children in the tree. If not
|
||||
// present already, generate them using the allocator.
|
||||
std::tuple<u64, u64> generate_children(NodeAllocator &allocator, u64 index);
|
||||
|
||||
// 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);
|
||||
} // 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