46#include <gtest/gtest.h>
74 class Naive_Tree_Oracle
76 static constexpr size_t NONE = std::numeric_limits<size_t>::max();
87 void dfs(
const size_t u,
const size_t p,
const size_t d)
108 tin_[f.u] = timer_++;
113 for (
auto it = adj_[f.u].get_it(); it.has_curr(); it.next_ne())
115 const size_t v = it.get_curr();
120 for (
long i =
static_cast<long>(children.
size()) - 1; i >= 0; --i)
121 stack.
append(
Frame{children[
static_cast<size_t>(i)], f.u, f.d + 1,
true});
125 tout_[f.u] = timer_ - 1;
131 Naive_Tree_Oracle(
const size_t n,
132 const Array<std::pair<size_t, size_t>> & edges,
145 <<
"Naive_Tree_Oracle: edges.size() must be n-1";
147 for (
typename Array<std::pair<size_t, size_t>>::Iterator it(edges);
148 it.has_curr(); it.next_ne())
150 const auto & e = it.get_curr();
151 const size_t u = e.first;
152 const size_t v = e.second;
155 <<
"Naive_Tree_Oracle: invalid edge";
162 for (
size_t i = 0; i < n_; ++i)
164 <<
"Naive_Tree_Oracle: disconnected input";
167 [[
nodiscard]]
size_t depth(
const size_t u)
const
173 [[
nodiscard]]
bool is_ancestor(
const size_t u,
const size_t v)
const
176 return tin_[u] <= tin_[v]
and tout_[v] <= tout_[u];
179 [[
nodiscard]]
size_t lca(
size_t u,
size_t v)
const
183 if (depth_[u] < depth_[v])
186 while (depth_[u] > depth_[v])
198 [[
nodiscard]]
size_t distance(
const size_t u,
const size_t v)
const
200 const size_t a = lca(u, v);
201 return depth_[u] + depth_[v] - 2 * depth_[a];
204 [[
nodiscard]]
size_t kth_ancestor(
const size_t u,
const size_t k)
const
211 for (
size_t i = 0; i <
k; ++i)
220 class LcaTypedTest :
public ::testing::Test
294 edges.
append(std::make_pair(0, 1));
295 edges.
append(std::make_pair(0, 2));
296 edges.
append(std::make_pair(1, 3));
297 edges.
append(std::make_pair(1, 4));
298 edges.
append(std::make_pair(2, 5));
299 edges.
append(std::make_pair(2, 6));
300 edges.
append(std::make_pair(5, 7));
301 edges.
append(std::make_pair(5, 8));
304 auto nodes = build_graph_with_unit_arcs(g, 9, edges);
309 auto check = [&](
const size_t u,
const size_t v,
316 <<
"u=" << u <<
" v=" << v;
318 <<
"u=" << u <<
" v=" << v;
349 edges.
append(std::make_pair(0, 1));
350 edges.
append(std::make_pair(1, 2));
351 edges.
append(std::make_pair(2, 3));
352 edges.
append(std::make_pair(3, 4));
355 auto nodes = build_graph_with_unit_arcs(g, 5, edges);
380 const size_t expected_lca =
oracle.lca(4, 2);
391 edges.
append(std::make_pair(0, 1));
392 edges.
append(std::make_pair(1, 2));
393 edges.
append(std::make_pair(2, 0));
396 auto nodes = build_graph_with_unit_arcs(g, 3, edges);
408 edges.
append(std::make_pair(0, 1));
409 edges.
append(std::make_pair(2, 3));
412 auto nodes = build_graph_with_unit_arcs(g, 4, edges);
426 edges.
append(std::make_pair(0, 1));
427 edges.
append(std::make_pair(1, 2));
428 edges.
append(std::make_pair(2, 2));
429 auto nodes = build_graph_with_unit_arcs(g, 3, edges);
438 edges.
append(std::make_pair(0, 1));
439 edges.
append(std::make_pair(0, 1));
440 auto nodes = build_graph_with_unit_arcs(g, 2, edges);
452 for (
size_t i = 0; i < 6; ++i)
485 edges1.append(std::make_pair(0, 1));
486 edges1.append(std::make_pair(1, 2));
487 auto n1 = build_graph_with_unit_arcs(
g1, 3,
edges1);
492 edges2.append(std::make_pair(0, 1));
493 auto n2 = build_graph_with_unit_arcs(
g2, 2,
edges2);
513 std::mt19937
rng(0x1ca2026u);
515 for (
size_t sample = 0; sample < 24; ++sample)
517 std::uniform_int_distribution<size_t>
n_pick(2, 90);
522 auto nodes = build_graph_with_unit_arcs(g, n, edges);
524 std::uniform_int_distribution<size_t>
root_pick(0, n - 1);
531 for (
size_t u = 0; u < n; ++u)
538 std::uniform_int_distribution<size_t>
k_pick(0,
oracle.depth(u) + 2);
542 if (
expected == Naive_Tree_Oracle::none())
548 std::uniform_int_distribution<size_t>
node_pick(0, n - 1);
549 for (
size_t q = 0; q < 450; ++q)
554 const size_t expected_lca =
oracle.lca(u, v);
561 <<
"sample=" << sample;
563 <<
"sample=" << sample;
570 oracle.is_ancestor(expected_lca, u));
572 oracle.is_ancestor(expected_lca, v));
580 using Clock = std::chrono::steady_clock;
582# ifndef LCA_PERF_TEST
583 const char *
run_perf = std::getenv(
"ALEPH_RUN_LCA_PERF");
585 GTEST_SKIP() <<
"LCA perf test disabled by default. "
586 <<
"Enable with -DLCA_PERF_TEST or ALEPH_RUN_LCA_PERF=1";
589 constexpr size_t n = 100000;
597 std::mt19937
rng(0x1ca2026u);
601 const auto nodes = build_graph_with_unit_arcs(g, n, edges);
603 std::uniform_int_distribution<size_t>
root_pick(0, n - 1);
606 const auto t_start = Clock::now();
613 std::uniform_int_distribution<size_t>
sanity_pick(0, n - 1);
614 for (
size_t i = 0; i < 64; ++i)
618 const size_t expected_lca =
oracle.lca(u, v);
630 std::uniform_int_distribution<size_t>
node_pick(0, n - 1);
645 checksum ^= (
static_cast<size_t>(
bl_lca->get_info()) + 0x9e3779b9u);
649 const auto t_end = Clock::now();
651 const auto build_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
653 const auto query_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
655 const auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
660 <<
"Perf regression: n=" << n
664 <<
", elapsed_ms=" << elapsed_ms
Lowest Common Ancestor (LCA) on rooted trees represented as Aleph graphs.
#define ah_runtime_error_if(C)
Throws std::runtime_error if condition holds.
TYPED_TEST_SUITE(AStarHeapTest, HeapTypes)
WeightedDigraph::Node Node
Simple dynamic array with automatic resizing and functional operations.
static Array create(size_t n)
Create an array with n logical elements.
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
constexpr bool is_empty() const noexcept
Checks if the container is empty.
T & append(const T &data)
Append a copy of data
void reserve(size_t cap)
Reserves cap cells into the array.
Dynamic singly linked list with functional programming support.
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
LCA via binary lifting on a rooted tree.
LCA via Euler tour + RMQ on depth in a rooted tree.
Arc for graphs implemented with simple adjacency lists.
Graph implemented with double-linked adjacency lists.
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Graph class implemented with singly-linked adjacency lists.
Filtered iterator on the nodes of a graph.
std::chrono::steady_clock Clock
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
DynArray< Graph::Node * > nodes
Singly linked list implementations with head-tail access.
TYPED_TEST(LcaTypedTest, EmptyTree)
DFSResult< typename Domain::Distance > dfs(Domain &domain, typename Domain::State &state, SearchPath< typename Domain::Move > &path, const typename Domain::Distance g, const typename Domain::Distance threshold, const size_t depth, IDAStarResult< Solution, typename Domain::Distance > &result, OnSolution &on_solution)
Core recursive DFS used by IDA* for a single threshold pass.
Aleph::Array< typename GT::Node * > build_graph_with_unit_arcs(GT &g, const size_t n, const std::vector< std::pair< size_t, size_t > > &edges)
EdgeContainer make_random_tree_edges(const size_t n, std::mt19937 &rng)
Main namespace for Aleph-w library functions.
and
Check uniqueness with explicit hash + equality functors.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
bool none(const Container &container, Operation &operation)
Return true if no element satisfies operation.
Arc of graph implemented with double-linked adjacency lists.
Array-based graph implementation.
Dynamic array container with automatic resizing.
Generic graph and digraph implementations.
Simple graph implementation with adjacency lists.