diff --git a/CMakeLists.txt b/CMakeLists.txt index f329385..5115480 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(blt-gp VERSION 0.0.137) +project(blt-gp VERSION 0.0.138) include(CTest) @@ -78,7 +78,7 @@ macro(blt_add_project name source type) # set (passRegex "Pass" "Passed" "PASS" "PASSED") # set (failRegex "WARN" "FAIL" "ERROR" "FATAL") - set (failRegex "\[WARN\]" "FAIL" "ERROR" "FATAL" "exception") + set (failRegex "\\[WARN\\]" "FAIL" "ERROR" "FATAL" "exception") # set_property (TEST ${name} PROPERTY PASS_REGULAR_EXPRESSION "${passRegex}") set_property (TEST ${name} PROPERTY FAIL_REGULAR_EXPRESSION "${failRegex}") diff --git a/include/blt/gp/fwdecl.h b/include/blt/gp/fwdecl.h index ae5e769..c335344 100644 --- a/include/blt/gp/fwdecl.h +++ b/include/blt/gp/fwdecl.h @@ -52,13 +52,22 @@ namespace blt::gp namespace detail { + // requires operator[](bit_index), push_back, clear + using bitmask_t = std::vector; + class operator_storage_test; // context*, read stack, write stack - using callable_t = std::function; - // to, from - using transfer_t = std::function>, stack_allocator&)>; + using callable_t = std::function; // debug function, using print_func_t = std::function; + + enum class destroy_t + { + ARGS, + RETURN + }; + + using destroy_func_t = std::function; } } diff --git a/include/blt/gp/operations.h b/include/blt/gp/operations.h index 839ba9f..126f625 100644 --- a/include/blt/gp/operations.h +++ b/include/blt/gp/operations.h @@ -102,16 +102,16 @@ namespace blt::gp } template - void call_destructors_without_first(stack_allocator& read_allocator) + void call_destructors_without_first(stack_allocator& read_allocator, detail::bitmask_t* mask) { if constexpr (sizeof...(NoCtxArgs) > 0) { - read_allocator.call_destructors...>(); + read_allocator.call_destructors...>(mask); } } template - Return operator()(bool, Func&& func, stack_allocator& read_allocator, ExtraArgs&& ... args) + Return operator()(bool has_context, detail::bitmask_t* mask, Func&& func, stack_allocator& read_allocator, ExtraArgs&& ... args) { constexpr auto seq = std::make_integer_sequence(); #if BLT_DEBUG_LEVEL > 0 @@ -119,10 +119,10 @@ namespace blt::gp { #endif Return ret = exec_sequence_to_indices(std::forward(func), read_allocator, seq, std::forward(args)...); - /*if (has_context) - call_destructors_without_first(read_allocator); + if (has_context) + call_destructors_without_first(read_allocator, mask); else - read_allocator.call_destructors...>();*/ + read_allocator.call_destructors...>(mask); read_allocator.pop_bytes((stack_allocator::aligned_size>() + ...)); return ret; #if BLT_DEBUG_LEVEL > 0 @@ -158,18 +158,18 @@ namespace blt::gp constexpr explicit operation_t(const Functor& functor, std::optional name = {}): func(functor), name(name) {} - [[nodiscard]] constexpr inline Return operator()(stack_allocator& read_allocator) const + [[nodiscard]] constexpr inline Return operator()(stack_allocator& read_allocator, detail::bitmask_t* mask) const { if constexpr (sizeof...(Args) == 0) { return func(); } else { - return call_with()(false, func, read_allocator); + return call_with()(false, mask, func, read_allocator); } } - [[nodiscard]] constexpr inline Return operator()(void* context, stack_allocator& read_allocator) const + [[nodiscard]] constexpr inline Return operator()(void* context, stack_allocator& read_allocator, detail::bitmask_t* mask) const { // should be an impossible state if constexpr (sizeof...(Args) == 0) @@ -182,22 +182,22 @@ namespace blt::gp return func(ctx_ref); } else { - return call_without_first()(true, func, read_allocator, ctx_ref); + return call_without_first()(true, mask, func, read_allocator, ctx_ref); } } template [[nodiscard]] detail::callable_t make_callable() const { - return [this](void* context, stack_allocator& read_allocator, stack_allocator& write_allocator) { + return [this](void* context, stack_allocator& read_allocator, stack_allocator& write_allocator, detail::bitmask_t* mask) { if constexpr (detail::is_same_v::type>>) { // first arg is context - write_allocator.push(this->operator()(context, read_allocator)); + write_allocator.push(this->operator()(context, read_allocator, mask)); } else { // first arg isn't context - write_allocator.push(this->operator()(read_allocator)); + write_allocator.push(this->operator()(read_allocator, mask)); } }; } diff --git a/include/blt/gp/program.h b/include/blt/gp/program.h index 390129e..6dce563 100644 --- a/include/blt/gp/program.h +++ b/include/blt/gp/program.h @@ -91,6 +91,7 @@ namespace blt::gp blt::hashset_t static_types; std::vector operators; std::vector print_funcs; + std::vector destroy_funcs; std::vector> names; }; @@ -135,13 +136,24 @@ namespace blt::gp storage.print_funcs.push_back([&op](std::ostream& out, stack_allocator& stack) { if constexpr (blt::meta::is_streamable_v) { - out << stack.pop(); + out << stack.from(0); (void) (op); // remove warning } else { out << "[Printing Value on '" << (op.get_name() ? *op.get_name() : "") << "' Not Supported!]"; } }); + storage.destroy_funcs.push_back([](detail::destroy_t type, detail::bitmask_t* mask, stack_allocator& alloc) { + switch (type) + { + case detail::destroy_t::ARGS: + alloc.call_destructors(mask); + break; + case detail::destroy_t::RETURN: + alloc.from>(0).drop(); + break; + } + }); storage.names.push_back(op.get_name()); if (is_static) storage.static_types.insert(operator_id); @@ -374,6 +386,8 @@ namespace blt::gp void reset_program(type_id root_type, bool eval_fitness_now = true) { current_generation = 0; + for (auto& pop : current_pop) + pop.tree.drop(*this); current_pop = config.pop_initializer.get().generate( {*this, root_type, config.population_size, config.initial_min_tree_size, config.initial_max_tree_size}); if (eval_fitness_now) @@ -484,6 +498,11 @@ namespace blt::gp return storage.print_funcs[id]; } + inline detail::destroy_func_t& get_destroy_func(operator_id id) + { + return storage.destroy_funcs[id]; + } + inline std::optional get_name(operator_id id) { return storage.names[id]; @@ -521,6 +540,12 @@ namespace blt::gp ~gp_program() { + std::cout << ("Destroying Program!") << std::endl; + for (auto& pop : current_pop.get_individuals()) + { + pop.tree.drop(*this); + pop = {}; + } thread_helper.lifetime_over = true; thread_helper.barrier.notify_all(); thread_helper.thread_function_condition.notify_all(); diff --git a/include/blt/gp/stack.h b/include/blt/gp/stack.h index e09aade..42dbac1 100644 --- a/include/blt/gp/stack.h +++ b/include/blt/gp/stack.h @@ -234,7 +234,7 @@ namespace blt::gp NO_REF_T t = *reinterpret_cast(head->metadata.offset - TYPE_SIZE); // call destructor if constexpr (detail::has_func_drop_v) - call_drop(0); + call_drop(0, 0, nullptr); // move offset back head->metadata.offset -= TYPE_SIZE; // moving back allows us to allocate with other data, if there is room. @@ -347,11 +347,22 @@ namespace blt::gp } template - void call_destructors() + void call_destructors(detail::bitmask_t* mask) { - blt::size_t offset = (stack_allocator::aligned_size>() + ...) - - stack_allocator::aligned_size::First>>(); - ((call_drop(offset), offset -= stack_allocator::aligned_size>()), ...); + if constexpr (sizeof...(Args) > 0) { + blt::size_t offset = (stack_allocator::aligned_size>() + ...) - + stack_allocator::aligned_size::First>>(); + blt::size_t index = 0; + if (mask != nullptr) + index = mask->size() - sizeof...(Args); + ((call_drop(offset, index, mask), offset -= stack_allocator::aligned_size>(), ++index), ...); + if (mask != nullptr) + { + auto& mask_r = *mask; + for (blt::size_t i = 0; i < sizeof...(Args); i++) + mask_r.pop_back(); + } + } } [[nodiscard]] bool empty() const noexcept @@ -552,10 +563,16 @@ namespace blt::gp }; template - inline void call_drop(blt::size_t offset) + inline void call_drop(blt::size_t offset, blt::size_t index, detail::bitmask_t* mask) { if constexpr (detail::has_func_drop_v) { + if (mask != nullptr) + { + auto& mask_r = *mask; + if (!mask_r[index]) + return; + } from>(offset).drop(); } } diff --git a/include/blt/gp/tree.h b/include/blt/gp/tree.h index 70644e8..adbe430 100644 --- a/include/blt/gp/tree.h +++ b/include/blt/gp/tree.h @@ -60,6 +60,31 @@ namespace blt::gp { using iter_type = std::vector::const_iterator; public: + tree_t(): reference_counter(new std::atomic_int64_t(1)) + {} + + tree_t(const tree_t& copy): operations(copy.operations), values(copy.values), reference_counter(copy.reference_counter) + { + reference_counter->operator++(); + } + + tree_t(tree_t&& move) noexcept: + operations(std::move(move.operations)), values(std::move(move.values)), reference_counter(move.reference_counter) + { + move.reference_counter = nullptr; + } + + tree_t& operator=(const tree_t& copy) = delete; + + tree_t& operator=(tree_t&& move) noexcept + { + operations = std::exchange(operations, std::move(move.operations)); + values = std::exchange(values, std::move(move.values)); + reference_counter = std::exchange(reference_counter, move.reference_counter); + return *this; + } + + [[nodiscard]] inline std::vector& get_operations() { return operations; @@ -146,10 +171,22 @@ namespace blt::gp { return total_value_bytes(operations.begin(), operations.end()); } + + void drop(gp_program& program); + + ~tree_t() + { + if (reference_counter == nullptr) + return; + reference_counter->operator--(); + if (*reference_counter == 0) + delete reference_counter; + } private: std::vector operations; blt::gp::stack_allocator values; + std::atomic_int64_t* reference_counter; }; struct fitness_t diff --git a/lib/blt b/lib/blt index 92300bc..644f426 160000 --- a/lib/blt +++ b/lib/blt @@ -1 +1 @@ -Subproject commit 92300bc6a29927f5354d516df76ad40be7fca868 +Subproject commit 644f42684320edc95f9fd5fc3b203bda3c4e6633 diff --git a/src/generators.cpp b/src/generators.cpp index 2872460..1443f07 100644 --- a/src/generators.cpp +++ b/src/generators.cpp @@ -71,7 +71,7 @@ namespace blt::gp if (args.program.is_static(top.id)) { - info.function(nullptr, tree.get_values(), tree.get_values()); + info.function(nullptr, tree.get_values(), tree.get_values(), nullptr); continue; } diff --git a/src/tree.cpp b/src/tree.cpp index 82d9c96..a9de1f2 100644 --- a/src/tree.cpp +++ b/src/tree.cpp @@ -24,7 +24,8 @@ namespace blt::gp { - inline auto empty_callable = detail::callable_t([](void*, stack_allocator&, stack_allocator&) { BLT_ABORT("This should never be called!"); }); + inline auto empty_callable = detail::callable_t( + [](void*, stack_allocator&, stack_allocator&, detail::bitmask_t*) { BLT_ABORT("This should never be called!"); }); evaluation_context tree_t::evaluate(void* context) const { @@ -47,15 +48,19 @@ namespace blt::gp auto value_stack = values; auto& values_process = results.values; + static thread_local detail::bitmask_t bitfield; + bitfield.clear(); for (const auto& operation : blt::reverse_iterate(operations.begin(), operations.end())) { if (operation.is_value) { value_stack.transfer_bytes(values_process, operation.type_size); + bitfield.push_back(false); continue; } - operation.func(context, values_process, values_process); + operation.func(context, values_process, values_process, &bitfield); + bitfield.push_back(true); } return results; @@ -222,6 +227,8 @@ namespace blt::gp bool tree_t::check(gp_program& program, void* context) const { + static thread_local detail::bitmask_t bitfield; + bitfield.clear(); blt::size_t bytes_expected = 0; auto bytes_size = values.size().total_used_bytes; @@ -255,12 +262,14 @@ namespace blt::gp { value_stack.transfer_bytes(values_process, operation.type_size); total_produced += stack_allocator::aligned_size(operation.type_size); + bitfield.push_back(false); continue; } auto& info = program.get_operator_info(operation.id); for (auto& arg : info.argument_types) total_consumed += stack_allocator::aligned_size(program.get_typesystem().get_type(arg).size()); - operation.func(context, values_process, values_process); + operation.func(context, values_process, values_process, &bitfield); + bitfield.push_back(true); total_produced += stack_allocator::aligned_size(program.get_typesystem().get_type(info.return_type).size()); } @@ -276,4 +285,38 @@ namespace blt::gp } return true; } + + void tree_t::drop(gp_program& program) + { + return; + if (values.empty()) + return; + std::cout << "---- NEW TREE ---- References " << *reference_counter << " ----" << std::endl; + if (reference_counter->load() > 1) + return; + static blt::hashset_t sets; + while (!operations.empty()) + { + auto operation = operations.back(); + if (operation.is_value) + { + struct hello + { + float* f; + blt::size_t i; + }; + auto h = values.from(0); + if (sets.find(h.i) != sets.end()) + std::cout << "HEY ASSHOLE Duplicate Value " << h.i << std::endl; + else + { + std::cout << "Destroying Value " << h.i << std::endl; + sets.insert(h.i); + } + program.get_destroy_func(operation.id)(detail::destroy_t::RETURN, nullptr, values); + values.pop_bytes(static_cast(stack_allocator::aligned_size(operation.type_size))); + } + operations.pop_back(); + } + } } \ No newline at end of file diff --git a/tests/destructor_test.cpp b/tests/destructor_test.cpp index ee1677d..5bf05b8 100644 --- a/tests/destructor_test.cpp +++ b/tests/destructor_test.cpp @@ -53,41 +53,47 @@ class move_float move_float(): f(new float()), assignment(++last_value) { constructions++; - BLT_TRACE("Value %ld Default Constructed", assignment); + //BLT_TRACE("Value %ld Default Constructed", assignment); } explicit move_float(float f): f(new float(f)), assignment(++last_value) { constructions++; - BLT_TRACE("Value %ld Constructed", assignment); + //BLT_TRACE("Value %ld Constructed", assignment); } explicit operator float() const { - BLT_TRACE("Using value %ld", assignment); + //BLT_TRACE("Using value %ld", assignment); return *f; } [[nodiscard]] float get() const { - BLT_TRACE("Using value %ld", assignment); + //BLT_TRACE("Using value %ld", assignment); return *f; } float operator*() const { - BLT_TRACE("Using value %ld", assignment); + //BLT_TRACE("Using value %ld", assignment); return *f; } void drop() // NOLINT { - BLT_TRACE("Drop Called On %ld", assignment); + //BLT_TRACE("Drop Called On %ld", assignment); delete f; f = nullptr; destructions++; } - + + friend std::ostream& operator<<(std::ostream& stream, const move_float& e) + { + stream << *e; + return stream; + } + private: float* f = nullptr; blt::size_t assignment; @@ -106,13 +112,13 @@ std::array fitness_cases; blt::gp::prog_config_t config = blt::gp::prog_config_t() .set_initial_min_tree_size(2) .set_initial_max_tree_size(6) - .set_elite_count(2) + .set_elite_count(0) .set_crossover_chance(0.9) .set_mutation_chance(0.1) .set_reproduction_chance(0) - .set_max_generations(1) - .set_pop_size(1) - .set_thread_count(1); + .set_max_generations(5) + .set_pop_size(500) + .set_thread_count(0); blt::gp::type_provider type_system; blt::gp::gp_program program{type_system, SEED, config}; @@ -137,7 +143,10 @@ constexpr auto fitness_function = [](blt::gp::tree_t& current_tree, blt::gp::fit constexpr double value_cutoff = 1.e15; for (auto& fitness_case : fitness_cases) { - auto diff = std::abs(fitness_case.y - *current_tree.get_evaluation_value(&fitness_case)); + auto ctx = current_tree.evaluate(&fitness_case); + auto diff = std::abs(fitness_case.y - *current_tree.get_evaluation_ref(ctx)); + // this will call the drop function. + current_tree.get_evaluation_value(ctx); if (diff < value_cutoff) { fitness.raw_fitness += diff; @@ -211,7 +220,7 @@ int main() BLT_END_INTERVAL("Symbolic Regression", "Main"); - auto best = program.get_best_individuals<3>(); + auto best = program.get_best_individuals<1>(); BLT_INFO("Best approximations:"); for (auto& i_ref : best) diff --git a/tests/evaluation_tests.cpp b/tests/evaluation_tests.cpp index 4d88428..598710f 100644 --- a/tests/evaluation_tests.cpp +++ b/tests/evaluation_tests.cpp @@ -63,7 +63,7 @@ blt::gp::op_container_t make_container(blt::gp::operator_id id) blt::gp::op_container_t make_value(const blt::gp::type& id) { - static blt::gp::detail::callable_t empty([](void*, blt::gp::stack_allocator&, blt::gp::stack_allocator&) {}); + static blt::gp::detail::callable_t empty([](void*, blt::gp::stack_allocator&, blt::gp::stack_allocator&, blt::gp::detail::bitmask_t*) {}); return {empty, id.size(), 0, true}; } diff --git a/tests/gp_test_1.cpp b/tests/gp_test_1.cpp index 641ea8b..e1fe9be 100644 --- a/tests/gp_test_1.cpp +++ b/tests/gp_test_1.cpp @@ -424,7 +424,7 @@ int main() return f + g; }); - std::cout << silly_op(alloc) << std::endl; + std::cout << silly_op(alloc, nullptr) << std::endl; std::cout << "Is empty? " << alloc.empty() << std::endl; @@ -456,15 +456,15 @@ int main() context hello{5, 10}; alloc.push(1.153f); - de[0](static_cast(&hello), alloc, alloc); + de[0](static_cast(&hello), alloc, alloc, nullptr); BLT_TRACE("first value: %f", alloc.pop()); - de[1](static_cast(&hello), alloc, alloc); + de[1](static_cast(&hello), alloc, alloc, nullptr); BLT_TRACE("second value: %f", alloc.pop()); alloc.push(1.0f); alloc.push(52.213f); - de[2](static_cast(&hello), alloc, alloc); + de[2](static_cast(&hello), alloc, alloc, nullptr); BLT_TRACE("third value: %f", alloc.pop()); //auto* pointer = static_cast(head->metadata.offset); @@ -482,7 +482,7 @@ int main() //blt::span spv{arr}; - std::cout << silly_op.operator()(alloc) << std::endl; + std::cout << silly_op.operator()(alloc, nullptr) << std::endl; std::cout << "Hello World!" << std::endl; diff --git a/tests/stack_tests.cpp b/tests/stack_tests.cpp index effdf1c..10e9995 100644 --- a/tests/stack_tests.cpp +++ b/tests/stack_tests.cpp @@ -301,7 +301,7 @@ void test_basic() stack.push(50.0f); stack.push(10.0f); BLT_TRACE_STREAM << stack.size() << "\n"; - basic_2.make_callable()(nullptr, stack, stack); + basic_2.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; auto val = stack.pop(); RUN_TEST(val != 60.000000f, stack, "Basic 2 Test Passed", "Basic 2 Test Failed. Unexpected value produced '%lf'", val); @@ -317,7 +317,7 @@ void test_basic() auto size = stack.size(); BLT_TRACE_STREAM << size << "\n"; BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!"); - basic_2.make_callable()(nullptr, stack, stack); + basic_2.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; auto val = stack.pop(); stack.pop>(); @@ -338,7 +338,7 @@ void test_mixed() stack.push(false); BLT_TRACE_STREAM << stack.size() << "\n"; - basic_mixed_4.make_callable()(nullptr, stack, stack); + basic_mixed_4.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; auto val = stack.pop(); RUN_TEST(val != 50.000000f, stack, "Mixed 4 Test Passed", "Mixed 4 Test Failed. Unexpected value produced '%lf'", val); @@ -356,7 +356,7 @@ void test_mixed() auto size = stack.size(); BLT_TRACE_STREAM << size << "\n"; BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!"); - basic_mixed_4.make_callable()(nullptr, stack, stack); + basic_mixed_4.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; auto val = stack.pop(); stack.pop>(); @@ -376,7 +376,7 @@ void test_large_256() stack.push(69.420f); BLT_TRACE_STREAM << stack.size() << "\n"; - large_256_basic_3.make_callable()(nullptr, stack, stack); + large_256_basic_3.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; auto val = stack.pop(); RUN_TEST(!compare(val, base_256), stack, "Large 256 3 Test Passed", "Large 256 3 Test Failed. Unexpected value produced '%lf'", val); @@ -393,7 +393,7 @@ void test_large_256() auto size = stack.size(); BLT_TRACE_STREAM << size << "\n"; BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!"); - large_256_basic_3.make_callable()(nullptr, stack, stack); + large_256_basic_3.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; auto val = stack.pop(); stack.pop>(); @@ -412,7 +412,7 @@ void test_large_4096() stack.push(33.0f); stack.push(true); BLT_TRACE_STREAM << stack.size() << "\n"; - large_4096_basic_3b.make_callable()(nullptr, stack, stack); + large_4096_basic_3b.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; auto val = stack.pop(); RUN_TEST(!compare(val, base_4096), stack, "Large 4096 3 Test Passed", "Large 4096 3 Test Failed. Unexpected value produced '%lf'", val); @@ -428,7 +428,7 @@ void test_large_4096() auto size = stack.size(); BLT_TRACE_STREAM << size << "\n"; BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!"); - large_4096_basic_3b.make_callable()(nullptr, stack, stack); + large_4096_basic_3b.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; auto val = stack.pop(); RUN_TEST(!compare(val, base_4096), stack, "Large 4096 3 Boundary Test Passed", "Large 4096 3 Test Failed. Unexpected value produced '%lf'", val); @@ -446,7 +446,7 @@ void test_large_18290() stack.push(-2543.0f); stack.push(true); BLT_TRACE_STREAM << stack.size() << "\n"; - large_18290_basic_3b.make_callable()(nullptr, stack, stack); + large_18290_basic_3b.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; auto val = stack.pop(); RUN_TEST(!compare(val, base_18290), stack, "Large 18290 3 Test Passed", "Large 4096 3 Test Failed. Unexpected value produced '%lf'", val); @@ -463,7 +463,7 @@ void test_large_18290() auto size = stack.size(); BLT_TRACE_STREAM << size << "\n"; BLT_ASSERT(size.blocks > 1 && "Stack doesn't have more than one block!"); - large_18290_basic_3b.make_callable()(nullptr, stack, stack); + large_18290_basic_3b.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; auto val = stack.pop(); stack.pop>(); @@ -479,12 +479,12 @@ void test_large_18290() stack.push(true); auto size = stack.size(); BLT_TRACE_STREAM << size << "\n"; - large_18290_basic_3b.make_callable()(nullptr, stack, stack); + large_18290_basic_3b.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; stack.push(-2543.0f); stack.push(true); BLT_TRACE_STREAM << stack.size() << "\n"; - large_18290_basic_3b.make_callable()(nullptr, stack, stack); + large_18290_basic_3b.make_callable()(nullptr, stack, stack, nullptr); BLT_TRACE_STREAM << stack.size() << "\n"; auto val = stack.pop(); RUN_TEST(!compare(val, base_18290), stack, "Large 18290 3 Boundary Test Passed", "Large 4096 3 Test Failed. Unexpected value produced '%lf'", val);