trying out some new ideas

dev
Brett 2025-05-14 21:32:26 -04:00
parent 3f4c63c8d1
commit 30372f1c71
3 changed files with 296 additions and 265 deletions

View File

@ -27,7 +27,7 @@ macro(compile_options target_name)
sanitizers(${target_name}) sanitizers(${target_name})
endmacro() endmacro()
project(blt-gp VERSION 0.5.29) project(blt-gp VERSION 0.5.30)
include(CTest) include(CTest)

View File

@ -29,6 +29,8 @@
#include <utility> #include <utility>
#include <stack> #include <stack>
#include "program.h"
namespace blt::gp namespace blt::gp
{ {
namespace detail namespace detail
@ -232,15 +234,43 @@ namespace blt::gp
struct subtree_point_t struct subtree_point_t
{ {
subtree_point_t(const size_t point): point(point) // NOLINT subtree_point_t(const size_t point, const operator_info_t& info): point(point), info(&info) // NOLINT
{ {
} }
subtree_point_t(const ptrdiff_t point): point(static_cast<size_t>(point)) // NOLINT subtree_point_t(const ptrdiff_t point, const operator_info_t& info): point(static_cast<size_t>(point)), info(&info) // NOLINT
{ {
} }
[[nodiscard]] size_t get_point() const
{
return point;
}
[[nodiscard]] ptrdiff_t get_spoint() const
{
return static_cast<ptrdiff_t>(point);
}
[[nodiscard]] const operator_info_t& get_info() const
{
return *info;
}
[[nodiscard]] type_id get_type() const
{
return info->return_type;
}
size_t point; size_t point;
const operator_info_t* info;
};
struct child_t
{
ptrdiff_t start;
// one past the end
ptrdiff_t end;
}; };
struct single_operation_tree_manipulator_t struct single_operation_tree_manipulator_t
@ -265,6 +295,103 @@ namespace blt::gp
detail::tree_modification_context_t context; detail::tree_modification_context_t context;
}; };
struct slow_tree_manipulator_t
{
explicit slow_tree_manipulator_t(tree_t* const context): tree(context)
{
}
/**
* Copies the subtree found at point into the provided out params
* @param point subtree point
* @param extent how far the subtree extends
* @param operators vector for storing subtree operators
* @param stack stack for storing subtree values
*/
void copy_subtree(subtree_point_t point, ptrdiff_t extent, tracked_vector<op_container_t>& operators, stack_allocator& stack) const;
/**
* Copies the subtree found at point into the provided out params
* @param point subtree point
* @param operators vector for storing subtree operators
* @param stack stack for storing subtree values
*/
void copy_subtree(const subtree_point_t point, tracked_vector<op_container_t>& operators, stack_allocator& stack) const;
void copy_subtree(subtree_point_t point, ptrdiff_t extent, tree_t& out_tree) const;
void copy_subtree(subtree_point_t point, tree_t& out_tree) const;
void copy_subtree(child_t subtree, tree_t& out_tree) const;
void swap_subtrees(child_t our_subtree, tree_t& other_tree, child_t other_subtree) const;
/**
* Swaps the subtrees between this tree and the other tree
* @param our_subtree
* @param other_tree
* @param other_subtree
*/
void swap_subtrees(subtree_point_t our_subtree, tree_t& other_tree, subtree_point_t other_subtree) const;
/**
* Replaces the point inside our tree with a new tree provided to this function.
* Uses the extent instead of calculating it for removing the existing subtree
* This can be used if you already have child tree information, such as when using @code find_child_extends@endcode
* @param point point to replace at
* @param extent extend of the subtree (child tree)
* @param other_tree other tree to replace with
*/
void replace_subtree(subtree_point_t point, ptrdiff_t extent, const tree_t& other_tree) const;
/**
* Replaces the point inside our tree with a new tree provided to this function
* @param point point to replace at
* @param other_tree other tree to replace with
*/
void replace_subtree(subtree_point_t point, const tree_t& other_tree) const;
/**
* Deletes the subtree at a point, bounded by extent. This is useful if you already know the size of the child tree
* Note: if you provide an incorrectly sized extent this will create UB within the GP program
* extent must be one past the last element in the subtree, as returned by all helper functions here.
* @param point point to delete from
* @param extent end point of the tree
*/
void delete_subtree(subtree_point_t point, ptrdiff_t extent) const;
/**
* Deletes the subtree at a point
* @param point point of subtree to recursively delete
*/
void delete_subtree(subtree_point_t point) const;
void delete_subtree(child_t subtree) const;
/**
* Insert a subtree before the specified point
* @param point point to insert into
* @param other_tree the tree to insert
* @return point + other_tree.size()
*/
ptrdiff_t insert_subtree(subtree_point_t point, tree_t& other_tree) const;
/**
* temporarily moves the last bytes amount of data from the current stack. this can be useful for if you are going to do a lot
* of consecutive operations on the tree as this will avoid extra copy + reinsert.
* The object returned by this function will automatically move the data back in when it goes out of scope.
* @param operator_index operator index to move from. this is inclusive
*/
void temporary_move(const size_t)
{
// return obt_move_t{*this, operator_index};
}
void modify_operator(size_t point, operator_id new_id, std::optional<type_id> return_type = {}) const;
tree_t* tree;
};
/** /**
* This is the parent class responsible for managing a tree's internal state at runtime. * This is the parent class responsible for managing a tree's internal state at runtime.
* While it is possible to create a tree without a need for this class, you should not attempt to modify a tree's internal state after creation. * While it is possible to create a tree without a need for this class, you should not attempt to modify a tree's internal state after creation.
@ -272,21 +399,27 @@ namespace blt::gp
*/ */
struct tree_manipulator_t struct tree_manipulator_t
{ {
explicit tree_manipulator_t(tree_t& tree, const subtree_point_t point): context{&tree, point.point} explicit tree_manipulator_t(tree_t& tree): context{&tree}
{ {
} }
[[nodiscard]] multi_operation_tree_manipulator_t explode() const // if you are fine with operations being slowing, this is the function to use as it allows you to modify trees without worrying about byte orders
[[nodiscard]] slow_tree_manipulator_t easy_manipulator() const
{ {
return multi_operation_tree_manipulator_t{context}; return slow_tree_manipulator_t{context};
} }
[[nodiscard]] single_operation_tree_manipulator_t single() const [[nodiscard]] multi_operation_tree_manipulator_t explode(const subtree_point_t point) const
{ {
return single_operation_tree_manipulator_t{context}; return multi_operation_tree_manipulator_t{{context, point.point}};
} }
detail::tree_modification_context_t context; [[nodiscard]] single_operation_tree_manipulator_t single(const subtree_point_t point) const
{
return single_operation_tree_manipulator_t{{context, point.point}};
}
tree_t* context;
}; };
class tree_t class tree_t
@ -295,13 +428,50 @@ namespace blt::gp
friend struct single_operation_tree_manipulator_t; friend struct single_operation_tree_manipulator_t;
friend struct multi_operation_tree_manipulator_t; friend struct multi_operation_tree_manipulator_t;
friend struct tree_manipulator_t; friend struct tree_manipulator_t;
friend struct slow_tree_manipulator_t;
public: public:
struct child_t struct byte_only_transaction_t
{ {
ptrdiff_t start; byte_only_transaction_t(tree_t& tree, const size_t bytes): tree(tree), data(nullptr), bytes(bytes)
// one past the end {
ptrdiff_t end; move(bytes);
}
explicit byte_only_transaction_t(tree_t& tree): tree(tree), data(nullptr), bytes(0)
{
}
byte_only_transaction_t(const byte_only_transaction_t& copy) = delete;
byte_only_transaction_t& operator=(const byte_only_transaction_t& copy) = delete;
byte_only_transaction_t(byte_only_transaction_t&& move) noexcept: tree(move.tree), data(std::exchange(move.data, nullptr)),
bytes(std::exchange(move.bytes, 0))
{
}
byte_only_transaction_t& operator=(byte_only_transaction_t&& move) noexcept = delete;
void move(size_t bytes_to_move);
[[nodiscard]] bool empty() const
{
return bytes == 0;
}
~byte_only_transaction_t()
{
if (!empty())
{
tree.values.copy_from(data, bytes);
bytes = 0;
}
}
private:
tree_t& tree;
u8* data;
size_t bytes;
}; };
explicit tree_t(gp_program& program): m_program(&program) explicit tree_t(gp_program& program): m_program(&program)
@ -383,154 +553,6 @@ namespace blt::gp
[[nodiscard]] std::optional<subtree_point_t> select_subtree_traverse(type_id type, u32 max_tries = 5, double terminal_chance = 0.1, [[nodiscard]] std::optional<subtree_point_t> select_subtree_traverse(type_id type, u32 max_tries = 5, double terminal_chance = 0.1,
double depth_multiplier = 0.6) const; double depth_multiplier = 0.6) const;
void copy_range(temporary_tree_storage_t& storage, const size_t begin, const size_t size)
{
copy_range(storage, operations.begin() + static_cast<ptrdiff_t>(begin), operations.begin() + static_cast<ptrdiff_t>(begin + size));
}
void copy_range(temporary_tree_storage_t& storage, const subtree_point_t point)
{
copy_range(storage, operations.begin() + static_cast<ptrdiff_t>(point.point),
operations.begin() + find_endpoint(static_cast<ptrdiff_t>(point.point)));
}
void copy_range(temporary_tree_storage_t& storage, detail::op_iter_t begin, detail::op_iter_t end);
void move_range(temporary_tree_storage_t& storage, const size_t begin, const size_t size)
{
move_range(storage, operations.begin() + static_cast<ptrdiff_t>(begin), operations.begin() + static_cast<ptrdiff_t>(begin + size));
}
void move_range(temporary_tree_storage_t& storage, const subtree_point_t point)
{
move_range(storage, operations.begin() + static_cast<ptrdiff_t>(point.point),
operations.begin() + find_endpoint(static_cast<ptrdiff_t>(point.point)));
}
void move_range(temporary_tree_storage_t& storage, detail::op_iter_t begin, detail::op_iter_t end);
void delete_range(const size_t begin, const size_t size)
{
delete_range(operations.begin() + static_cast<ptrdiff_t>(begin), operations.begin() + static_cast<ptrdiff_t>(begin + size));
}
void delete_range(const subtree_point_t point)
{
delete_range(operations.begin() + static_cast<ptrdiff_t>(point.point),
operations.begin() + find_endpoint(static_cast<ptrdiff_t>(point.point)));
}
void delete_range(detail::op_iter_t begin, detail::op_iter_t end);
/**
* Copies the subtree found at point into the provided out params
* @param point subtree point
* @param extent how far the subtree extends
* @param operators vector for storing subtree operators
* @param stack stack for storing subtree values
*/
void copy_subtree(subtree_point_t point, ptrdiff_t extent, tracked_vector<op_container_t>& operators, stack_allocator& stack);
/**
* Copies the subtree found at point into the provided out params
* @param point subtree point
* @param operators vector for storing subtree operators
* @param stack stack for storing subtree values
*/
void copy_subtree(const subtree_point_t point, tracked_vector<op_container_t>& operators, stack_allocator& stack)
{
copy_subtree(point, find_endpoint(point.pos), operators, stack);
}
void copy_subtree(const subtree_point_t point, const ptrdiff_t extent, tree_t& out_tree)
{
copy_subtree(point, extent, out_tree.operations, out_tree.values);
}
void copy_subtree(const subtree_point_t point, tree_t& out_tree)
{
copy_subtree(point, find_endpoint(point.pos), out_tree);
}
void copy_subtree(const child_t subtree, tree_t& out_tree)
{
copy_subtree(subtree_point_t{subtree.start}, subtree.end, out_tree);
}
void swap_subtrees(child_t our_subtree, tree_t& other_tree, child_t other_subtree);
/**
* Swaps the subtrees between this tree and the other tree
* @param our_subtree
* @param other_tree
* @param other_subtree
*/
void swap_subtrees(subtree_point_t our_subtree, tree_t& other_tree, subtree_point_t other_subtree);
/**
* Replaces the point inside our tree with a new tree provided to this function.
* Uses the extent instead of calculating it for removing the existing subtree
* This can be used if you already have child tree information, such as when using @code find_child_extends@endcode
* @param point point to replace at
* @param extent extend of the subtree (child tree)
* @param other_tree other tree to replace with
*/
void replace_subtree(subtree_point_t point, ptrdiff_t extent, tree_t& other_tree);
/**
* Replaces the point inside our tree with a new tree provided to this function
* @param point point to replace at
* @param other_tree other tree to replace with
*/
void replace_subtree(const subtree_point_t point, tree_t& other_tree)
{
replace_subtree(point, find_endpoint(point.pos), other_tree);
}
/**
* Deletes the subtree at a point, bounded by extent. This is useful if you already know the size of the child tree
* Note: if you provide an incorrectly sized extent this will create UB within the GP program
* extent must be one past the last element in the subtree, as returned by all helper functions here.
* @param point point to delete from
* @param extent end point of the tree
*/
void delete_subtree(subtree_point_t point, ptrdiff_t extent);
/**
* Deletes the subtree at a point
* @param point point of subtree to recursively delete
*/
void delete_subtree(const subtree_point_t point)
{
delete_subtree(point, find_endpoint(point.pos));
}
void delete_subtree(const child_t subtree)
{
delete_subtree(subtree_point_t{subtree.start}, subtree.end);
}
/**
* Insert a subtree before the specified point
* @param point point to insert into
* @param other_tree the tree to insert
* @return point + other_tree.size()
*/
ptrdiff_t insert_subtree(subtree_point_t point, tree_t& other_tree);
/**
* temporarily moves the last bytes amount of data from the current stack. this can be useful for if you are going to do a lot
* of consecutive operations on the tree as this will avoid extra copy + reinsert.
* The object returned by this function will automatically move the data back in when it goes out of scope.
* @param operator_index operator index to move from. this is inclusive
*/
void temporary_move(const size_t)
{
// return obt_move_t{*this, operator_index};
}
void modify_operator(size_t point, operator_id new_id, std::optional<type_id> return_type = {});
/** /**
* User function for evaluating this tree using a context reference. This function should only be used if the tree is expecting the context value * User function for evaluating this tree using a context reference. This function should only be used if the tree is expecting the context value
* This function returns a copy of your value, if it is too large for the stack, or you otherwise need a reference, please use the corresponding * This function returns a copy of your value, if it is too large for the stack, or you otherwise need a reference, please use the corresponding

View File

@ -197,31 +197,31 @@ namespace blt::gp
return depth; return depth;
} }
tree_t::subtree_point_t tree_t::select_subtree(const double terminal_chance) const subtree_point_t tree_t::select_subtree(const double terminal_chance) const
{ {
do do
{ {
const auto point = m_program->get_random().get_u64(0, operations.size()); const auto point = m_program->get_random().get_u64(0, operations.size());
const auto& info = m_program->get_operator_info(operations[point].id()); const auto& info = m_program->get_operator_info(operations[point].id());
if (!info.argc.is_terminal()) if (!info.argc.is_terminal())
return {static_cast<ptrdiff_t>(point), info.return_type}; return {static_cast<ptrdiff_t>(point), info};
if (m_program->get_random().choice(terminal_chance)) if (m_program->get_random().choice(terminal_chance))
return {static_cast<ptrdiff_t>(point), info.return_type}; return {static_cast<ptrdiff_t>(point), info};
} }
while (true); while (true);
} }
std::optional<tree_t::subtree_point_t> tree_t::select_subtree(const type_id type, const u32 max_tries, const double terminal_chance) const std::optional<subtree_point_t> tree_t::select_subtree(const type_id type, const u32 max_tries, const double terminal_chance) const
{ {
for (u32 i = 0; i < max_tries; ++i) for (u32 i = 0; i < max_tries; ++i)
{ {
if (const auto tree = select_subtree(terminal_chance); tree.type == type) if (const auto tree = select_subtree(terminal_chance); tree.get_type() == type)
return tree; return tree;
} }
return {}; return {};
} }
tree_t::subtree_point_t tree_t::select_subtree_traverse(const double terminal_chance, const double depth_multiplier) const subtree_point_t tree_t::select_subtree_traverse(const double terminal_chance, const double depth_multiplier) const
{ {
size_t index = 0; size_t index = 0;
double depth = 0; double depth = 0;
@ -232,14 +232,14 @@ namespace blt::gp
if (info.argc.is_terminal()) if (info.argc.is_terminal())
{ {
if (m_program->get_random().choice(terminal_chance)) if (m_program->get_random().choice(terminal_chance))
return {static_cast<ptrdiff_t>(index), info.return_type}; return {static_cast<ptrdiff_t>(index), info};
index = 0; index = 0;
depth = 0; depth = 0;
exit_chance = 0; exit_chance = 0;
continue; continue;
} }
if (m_program->get_random().choice(exit_chance)) if (m_program->get_random().choice(exit_chance))
return {static_cast<ptrdiff_t>(index), info.return_type}; return {static_cast<ptrdiff_t>(index), info};
const auto child = m_program->get_random().get_u32(0, info.argc.argc); const auto child = m_program->get_random().get_u32(0, info.argc.argc);
index++; index++;
@ -251,35 +251,23 @@ namespace blt::gp
} }
} }
std::optional<tree_t::subtree_point_t> tree_t::select_subtree_traverse(const type_id type, const u32 max_tries, const double terminal_chance, std::optional<subtree_point_t> tree_t::select_subtree_traverse(const type_id type, const u32 max_tries, const double terminal_chance,
const double depth_multiplier) const const double depth_multiplier) const
{ {
for (u32 i = 0; i < max_tries; ++i) for (u32 i = 0; i < max_tries; ++i)
{ {
if (const auto tree = select_subtree_traverse(terminal_chance, depth_multiplier); tree.type == type) if (const auto tree = select_subtree_traverse(terminal_chance, depth_multiplier); tree.get_type() == type)
return tree; return tree;
} }
return {}; return {};
} }
void tree_t::copy_range(temporary_tree_storage_t& storage, detail::op_iter_t begin, detail::op_iter_t end) void slow_tree_manipulator_t::copy_subtree(const subtree_point_t point, const ptrdiff_t extent, tracked_vector<op_container_t>& operators, stack_allocator& stack) const
{ {
} const auto point_begin_itr = tree->operations.begin() + point.get_spoint();
const auto point_end_itr = tree->operations.begin() + extent;
void tree_t::move_range(temporary_tree_storage_t& storage, detail::op_iter_t begin, detail::op_iter_t end) const size_t after_bytes = calculate_ephemeral_size(point_end_itr, tree->operations.end());
{
}
void tree_t::delete_range(detail::op_iter_t begin, detail::op_iter_t end)
{
}
void tree_t::copy_subtree(const subtree_point_t point, const ptrdiff_t extent, tracked_vector<op_container_t>& operators, stack_allocator& stack)
{
const auto point_begin_itr = operations.begin() + point.pos;
const auto point_end_itr = operations.begin() + extent;
const size_t after_bytes = calculate_ephemeral_size(point_end_itr, operations.end());
const size_t ops = std::distance(point_begin_itr, point_end_itr); const size_t ops = std::distance(point_begin_itr, point_end_itr);
operators.reserve(operators.size() + ops); operators.reserve(operators.size() + ops);
@ -295,20 +283,40 @@ namespace blt::gp
for_bytes += it.type_size(); for_bytes += it.type_size();
if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop()) if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop())
{ {
auto [_, ptr] = values.access_pointer(for_bytes + after_bytes, it.type_size()); auto [_, ptr] = tree->values.access_pointer(for_bytes + after_bytes, it.type_size());
++*ptr; ++*ptr;
} }
} }
operators[operators.size() - 1 - (pos++)] = it; operators[operators.size() - 1 - (pos++)] = it;
} }
stack.copy_from(values, for_bytes, after_bytes); stack.copy_from(tree->values, for_bytes, after_bytes);
} }
void tree_t::swap_subtrees(const child_t our_subtree, tree_t& other_tree, const child_t other_subtree) void slow_tree_manipulator_t::copy_subtree(const subtree_point_t point, tracked_vector<op_container_t>& operators, stack_allocator& stack) const
{ {
const auto c1_subtree_begin_itr = operations.begin() + our_subtree.start; copy_subtree(point, tree->find_endpoint(point.get_spoint()), operators, stack);
const auto c1_subtree_end_itr = operations.begin() + our_subtree.end; }
void slow_tree_manipulator_t::copy_subtree(const subtree_point_t point, const ptrdiff_t extent, tree_t& out_tree) const
{
copy_subtree(point, extent, out_tree.operations, out_tree.values);
}
void slow_tree_manipulator_t::copy_subtree(const subtree_point_t point, tree_t& out_tree) const
{
copy_subtree(point, tree->find_endpoint(point.get_spoint()), out_tree);
}
void slow_tree_manipulator_t::copy_subtree(const child_t subtree, tree_t& out_tree) const
{
copy_subtree(subtree_point_t{subtree.start, tree->m_program->get_operator_info(tree->operations[subtree.start].id())}, subtree.end, out_tree);
}
void slow_tree_manipulator_t::swap_subtrees(const child_t our_subtree, tree_t& other_tree, const child_t other_subtree) const
{
const auto c1_subtree_begin_itr = tree->operations.begin() + our_subtree.start;
const auto c1_subtree_end_itr = tree->operations.begin() + our_subtree.end;
const auto c2_subtree_begin_itr = other_tree.operations.begin() + other_subtree.start; const auto c2_subtree_begin_itr = other_tree.operations.begin() + other_subtree.start;
const auto c2_subtree_end_itr = other_tree.operations.begin() + other_subtree.end; const auto c2_subtree_end_itr = other_tree.operations.begin() + other_subtree.end;
@ -328,14 +336,7 @@ namespace blt::gp
for (const auto& it : iterate(c1_subtree_begin_itr, c1_subtree_end_itr)) for (const auto& it : iterate(c1_subtree_begin_itr, c1_subtree_end_itr))
{ {
if (it.is_value()) if (it.is_value())
{
// if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop())
// {
// auto& ptr = values.access_pointer_forward(for_our_bytes, it.type_size());
// ++*ptr;
// }
c1_subtree_bytes += it.type_size(); c1_subtree_bytes += it.type_size();
}
c1_subtree_operators.push_back(it); c1_subtree_operators.push_back(it);
} }
@ -343,29 +344,22 @@ namespace blt::gp
for (const auto& it : iterate(c2_subtree_begin_itr, c2_subtree_end_itr)) for (const auto& it : iterate(c2_subtree_begin_itr, c2_subtree_end_itr))
{ {
if (it.is_value()) if (it.is_value())
{
// if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop())
// {
// auto& ptr = values.access_pointer_forward(for_other_bytes, it.type_size());
// ++*ptr;
// }
c2_subtree_bytes += it.type_size(); c2_subtree_bytes += it.type_size();
}
c2_subtree_operators.push_back(it); c2_subtree_operators.push_back(it);
} }
const size_t c1_stack_after_bytes = calculate_ephemeral_size(c1_subtree_end_itr, operations.end()); const size_t c1_stack_after_bytes = calculate_ephemeral_size(c1_subtree_end_itr, tree->operations.end());
const size_t c2_stack_after_bytes = calculate_ephemeral_size(c2_subtree_end_itr, other_tree.operations.end()); const size_t c2_stack_after_bytes = calculate_ephemeral_size(c2_subtree_end_itr, other_tree.operations.end());
const auto c1_total = static_cast<ptrdiff_t>(c1_stack_after_bytes + c1_subtree_bytes); const auto c1_total = static_cast<ptrdiff_t>(c1_stack_after_bytes + c1_subtree_bytes);
const auto c2_total = static_cast<ptrdiff_t>(c2_stack_after_bytes + c2_subtree_bytes); const auto c2_total = static_cast<ptrdiff_t>(c2_stack_after_bytes + c2_subtree_bytes);
const auto copy_ptr_c1 = get_thread_pointer_for_size<struct c1_t>(c1_total); const auto copy_ptr_c1 = get_thread_pointer_for_size<struct c1_t>(c1_total);
const auto copy_ptr_c2 = get_thread_pointer_for_size<struct c2_t>(c2_total); const auto copy_ptr_c2 = get_thread_pointer_for_size<struct c2_t>(c2_total);
values.reserve(values.stored() - c1_subtree_bytes + c2_subtree_bytes); tree->values.reserve(tree->values.stored() - c1_subtree_bytes + c2_subtree_bytes);
other_tree.values.reserve(other_tree.values.stored() - c2_subtree_bytes + c1_subtree_bytes); other_tree.values.reserve(other_tree.values.stored() - c2_subtree_bytes + c1_subtree_bytes);
values.copy_to(copy_ptr_c1, c1_total); tree->values.copy_to(copy_ptr_c1, c1_total);
values.pop_bytes(c1_total); tree->values.pop_bytes(c1_total);
other_tree.values.copy_to(copy_ptr_c2, c2_total); other_tree.values.copy_to(copy_ptr_c2, c2_total);
other_tree.values.pop_bytes(c2_total); other_tree.values.pop_bytes(c2_total);
@ -373,33 +367,33 @@ namespace blt::gp
other_tree.values.copy_from(copy_ptr_c1, c1_subtree_bytes); other_tree.values.copy_from(copy_ptr_c1, c1_subtree_bytes);
other_tree.values.copy_from(copy_ptr_c2 + c2_subtree_bytes, c2_stack_after_bytes); other_tree.values.copy_from(copy_ptr_c2 + c2_subtree_bytes, c2_stack_after_bytes);
values.copy_from(copy_ptr_c2, c2_subtree_bytes); tree->values.copy_from(copy_ptr_c2, c2_subtree_bytes);
values.copy_from(copy_ptr_c1 + c1_subtree_bytes, c1_stack_after_bytes); tree->values.copy_from(copy_ptr_c1 + c1_subtree_bytes, c1_stack_after_bytes);
// now swap the operators // now swap the operators
// auto insert_point_c1 = c1_subtree_begin_itr - 1; // auto insert_point_c1 = c1_subtree_begin_itr - 1;
// auto insert_point_c2 = c2_subtree_begin_itr - 1; // auto insert_point_c2 = c2_subtree_begin_itr - 1;
// invalidates [begin, end()) so the insert points should be fine // invalidates [begin, end()) so the insert points should be fine
auto insert_point_c1 = operations.erase(c1_subtree_begin_itr, c1_subtree_end_itr); const auto insert_point_c1 = tree->operations.erase(c1_subtree_begin_itr, c1_subtree_end_itr);
auto insert_point_c2 = other_tree.operations.erase(c2_subtree_begin_itr, c2_subtree_end_itr); const auto insert_point_c2 = other_tree.operations.erase(c2_subtree_begin_itr, c2_subtree_end_itr);
operations.insert(insert_point_c1, c2_subtree_operators.begin(), c2_subtree_operators.end()); tree->operations.insert(insert_point_c1, c2_subtree_operators.begin(), c2_subtree_operators.end());
other_tree.operations.insert(insert_point_c2, c1_subtree_operators.begin(), c1_subtree_operators.end()); other_tree.operations.insert(insert_point_c2, c1_subtree_operators.begin(), c1_subtree_operators.end());
} }
void tree_t::swap_subtrees(const subtree_point_t our_subtree, tree_t& other_tree, const subtree_point_t other_subtree) void slow_tree_manipulator_t::swap_subtrees(const subtree_point_t our_subtree, tree_t& other_tree, const subtree_point_t other_subtree) const
{ {
swap_subtrees(child_t{our_subtree.pos, find_endpoint(our_subtree.pos)}, other_tree, swap_subtrees(child_t{our_subtree.get_spoint(), tree->find_endpoint(our_subtree.get_spoint())}, other_tree,
child_t{other_subtree.pos, find_endpoint(other_subtree.pos)}); child_t{other_subtree.get_spoint(), tree->find_endpoint(other_subtree.get_spoint())});
} }
void tree_t::replace_subtree(const subtree_point_t point, const ptrdiff_t extent, tree_t& other_tree) void slow_tree_manipulator_t::replace_subtree(const subtree_point_t point, const ptrdiff_t extent, const tree_t& other_tree) const
{ {
const auto point_begin_itr = operations.begin() + point.pos; const auto point_begin_itr = tree->operations.begin() + point.get_spoint();
const auto point_end_itr = operations.begin() + extent; const auto point_end_itr = tree->operations.begin() + extent;
const size_t after_bytes = calculate_ephemeral_size(point_end_itr, operations.end()); const size_t after_bytes = calculate_ephemeral_size(point_end_itr, tree->operations.end());
size_t for_bytes = 0; size_t for_bytes = 0;
for (auto& it : iterate(point_begin_itr, point_end_itr).rev()) for (auto& it : iterate(point_begin_itr, point_end_itr).rev())
@ -409,18 +403,18 @@ namespace blt::gp
for_bytes += it.type_size(); for_bytes += it.type_size();
if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop()) if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop())
{ {
auto [val, ptr] = values.access_pointer(for_bytes + after_bytes, it.type_size()); auto [val, ptr] = tree->values.access_pointer(for_bytes + after_bytes, it.type_size());
--*ptr; --*ptr;
if (*ptr == 0) if (*ptr == 0)
handle_ptr_empty(ptr, val, it.id()); tree->handle_ptr_empty(ptr, val, it.id());
} }
} }
} }
auto insert = operations.erase(point_begin_itr, point_end_itr); auto insert = tree->operations.erase(point_begin_itr, point_end_itr);
const auto ptr = get_thread_pointer_for_size<struct replace>(after_bytes); const auto ptr = get_thread_pointer_for_size<struct replace>(after_bytes);
values.copy_to(ptr, after_bytes); tree->values.copy_to(ptr, after_bytes);
values.pop_bytes(after_bytes + for_bytes); tree->values.pop_bytes(after_bytes + for_bytes);
size_t copy_bytes = 0; size_t copy_bytes = 0;
for (const auto& v : other_tree.operations) for (const auto& v : other_tree.operations)
@ -434,19 +428,24 @@ namespace blt::gp
} }
copy_bytes += v.type_size(); copy_bytes += v.type_size();
} }
insert = ++operations.emplace(insert, v); insert = ++tree->operations.emplace(insert, v);
} }
values.insert(other_tree.values); tree->values.insert(other_tree.values);
values.copy_from(ptr, after_bytes); tree->values.copy_from(ptr, after_bytes);
} }
void tree_t::delete_subtree(const subtree_point_t point, const ptrdiff_t extent) void slow_tree_manipulator_t::replace_subtree(const subtree_point_t point, const tree_t& other_tree) const
{ {
const auto point_begin_itr = operations.begin() + point.pos; replace_subtree(point, tree->find_endpoint(point.get_spoint()), other_tree);
const auto point_end_itr = operations.begin() + extent; }
const size_t after_bytes = calculate_ephemeral_size(point_end_itr, operations.end()); void slow_tree_manipulator_t::delete_subtree(const subtree_point_t point, const ptrdiff_t extent) const
{
const auto point_begin_itr = tree->operations.begin() + point.get_spoint();
const auto point_end_itr = tree->operations.begin() + extent;
const size_t after_bytes = calculate_ephemeral_size(point_end_itr, tree->operations.end());
size_t for_bytes = 0; size_t for_bytes = 0;
for (auto& it : iterate(point_begin_itr, point_end_itr).rev()) for (auto& it : iterate(point_begin_itr, point_end_itr).rev())
@ -456,27 +455,37 @@ namespace blt::gp
for_bytes += it.type_size(); for_bytes += it.type_size();
if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop()) if (it.get_flags().is_ephemeral() && it.has_ephemeral_drop())
{ {
auto [val, ptr] = values.access_pointer(for_bytes + after_bytes, it.type_size()); auto [val, ptr] = tree->values.access_pointer(for_bytes + after_bytes, it.type_size());
--*ptr; --*ptr;
if (*ptr == 0) if (*ptr == 0)
handle_ptr_empty(ptr, val, it.id()); tree->handle_ptr_empty(ptr, val, it.id());
} }
} }
} }
operations.erase(point_begin_itr, point_end_itr); tree->operations.erase(point_begin_itr, point_end_itr);
const auto ptr = get_thread_pointer_for_size<struct replace>(after_bytes); const auto ptr = get_thread_pointer_for_size<struct replace>(after_bytes);
values.copy_to(ptr, after_bytes); tree->values.copy_to(ptr, after_bytes);
values.pop_bytes(after_bytes + for_bytes); tree->values.pop_bytes(after_bytes + for_bytes);
values.copy_from(ptr, after_bytes); tree->values.copy_from(ptr, after_bytes);
} }
ptrdiff_t tree_t::insert_subtree(const subtree_point_t point, tree_t& other_tree) void slow_tree_manipulator_t::delete_subtree(const subtree_point_t point) const
{ {
const size_t after_bytes = calculate_ephemeral_size(operations.begin() + point.pos, operations.end()); delete_subtree(point, tree->find_endpoint(point.get_spoint()));
byte_only_transaction_t transaction{*this, after_bytes}; }
auto insert = operations.begin() + point.pos; void slow_tree_manipulator_t::delete_subtree(const child_t subtree) const
{
delete_subtree(subtree_point_t{subtree.start, tree->m_program->get_operator_info(tree->operations[subtree.start].id())}, subtree.end);
}
ptrdiff_t slow_tree_manipulator_t::insert_subtree(const subtree_point_t point, tree_t& other_tree) const
{
const size_t after_bytes = calculate_ephemeral_size(tree->operations.begin() + point.get_spoint(), tree->operations.end());
tree_t::byte_only_transaction_t transaction{*tree, after_bytes};
auto insert = tree->operations.begin() + point.get_spoint();
size_t bytes = 0; size_t bytes = 0;
for (auto& it : iterate(other_tree.operations).rev()) for (auto& it : iterate(other_tree.operations).rev())
{ {
@ -489,11 +498,11 @@ namespace blt::gp
++*ptr; ++*ptr;
} }
} }
insert = operations.insert(insert, it); insert = tree->operations.insert(insert, it);
} }
values.insert(other_tree.values); tree->values.insert(other_tree.values);
return static_cast<ptrdiff_t>(point.pos + other_tree.size()); return static_cast<ptrdiff_t>(point.get_spoint() + other_tree.size());
} }
@ -750,9 +759,9 @@ namespace blt::gp
operations.insert(operations.begin() + static_cast<ptrdiff_t>(index), container); operations.insert(operations.begin() + static_cast<ptrdiff_t>(index), container);
} }
tree_t::subtree_point_t tree_t::subtree_from_point(ptrdiff_t point) const subtree_point_t tree_t::subtree_from_point(const ptrdiff_t point) const
{ {
return {point, m_program->get_operator_info(operations[point].id()).return_type}; return subtree_point_t{point, m_program->get_operator_info(operations[point].id())};
} }
size_t tree_t::required_size() const size_t tree_t::required_size() const
@ -840,38 +849,38 @@ namespace blt::gp
BLT_ASSERT(file.read(values.data(), bytes_in_head) == static_cast<i64>(bytes_in_head)); BLT_ASSERT(file.read(values.data(), bytes_in_head) == static_cast<i64>(bytes_in_head));
} }
void tree_t::modify_operator(const size_t point, operator_id new_id, std::optional<type_id> return_type) void slow_tree_manipulator_t::modify_operator(const size_t point, operator_id new_id, std::optional<type_id> return_type) const
{ {
if (!return_type) if (!return_type)
return_type = m_program->get_operator_info(new_id).return_type; return_type = tree->m_program->get_operator_info(new_id).return_type;
byte_only_transaction_t move_data{*this}; tree_t::byte_only_transaction_t move_data{*tree};
if (operations[point].is_value()) if (tree->operations[point].is_value())
{ {
const size_t after_bytes = calculate_ephemeral_size(operations.begin() + static_cast<ptrdiff_t>(point) + 1, operations.end()); const size_t after_bytes = calculate_ephemeral_size(tree->operations.begin() + static_cast<ptrdiff_t>(point) + 1, tree->operations.end());
move_data.move(after_bytes); move_data.move(after_bytes);
if (operations[point].get_flags().is_ephemeral() && operations[point].has_ephemeral_drop()) if (tree->operations[point].get_flags().is_ephemeral() && tree->operations[point].has_ephemeral_drop())
{ {
auto [val, ptr] = values.access_pointer(operations[point].type_size(), operations[point].type_size()); auto [val, ptr] = tree->values.access_pointer(tree->operations[point].type_size(), tree->operations[point].type_size());
--*ptr; --*ptr;
if (*ptr == 0) if (*ptr == 0)
handle_ptr_empty(ptr, val, operations[point].id()); tree->handle_ptr_empty(ptr, val, tree->operations[point].id());
} }
values.pop_bytes(operations[point].type_size()); tree->values.pop_bytes(tree->operations[point].type_size());
} }
operations[point] = { tree->operations[point] = {
m_program->get_typesystem().get_type(*return_type).size(), tree->m_program->get_typesystem().get_type(*return_type).size(),
new_id, new_id,
m_program->is_operator_ephemeral(new_id), tree->m_program->is_operator_ephemeral(new_id),
m_program->get_operator_flags(new_id) tree->m_program->get_operator_flags(new_id)
}; };
if (operations[point].get_flags().is_ephemeral()) if (tree->operations[point].get_flags().is_ephemeral())
{ {
if (move_data.empty()) if (move_data.empty())
{ {
const size_t after_bytes = calculate_ephemeral_size(operations.begin() + static_cast<ptrdiff_t>(point) + 1, operations.end()); const size_t after_bytes = calculate_ephemeral_size(tree->operations.begin() + static_cast<ptrdiff_t>(point) + 1, tree->operations.end());
move_data.move(after_bytes); move_data.move(after_bytes);
} }
handle_operator_inserted(operations[point]); tree->handle_operator_inserted(tree->operations[point]);
} }
} }