diff --git a/src/atomdb/AtomDBUtils.cc b/src/atomdb/AtomDBUtils.cc new file mode 100644 index 00000000..f41191e8 --- /dev/null +++ b/src/atomdb/AtomDBUtils.cc @@ -0,0 +1,45 @@ +#include "AtomDBUtils.h" + +#include "AtomDBSingleton.h" + +using namespace atomdb; + +AtomDBUtils::AtomDBUtils() {} + +AtomDBUtils::~AtomDBUtils() {} + +// ------------------------------------------------------------------------------------------------- +// Public methods + +void AtomDBUtils::reachable_terminal_set(set& output, const string& handle, bool metta_mapping) { + auto atom = AtomDBSingleton::get_instance()->get_atom(handle); + if (atom != nullptr) { + if (Atom::is_node(atom)) { + output.insert(handle); + } else { + AtomDBUtils::reachable_terminal_set_recursive( + output, dynamic_pointer_cast(atom), metta_mapping); + } + } +} + +// ------------------------------------------------------------------------------------------------- +// Private methods + +void AtomDBUtils::reachable_terminal_set_recursive(set& output, + shared_ptr link, + bool metta_mapping) { + bool first_target = true; + for (string& target_handle : link->targets) { + auto atom = AtomDBSingleton::get_instance()->get_atom(target_handle); + if (Atom::is_node(atom)) { + if (!(metta_mapping && first_target)) { + output.insert(atom->handle()); + } + } else { + AtomDBUtils::reachable_terminal_set_recursive( + output, dynamic_pointer_cast(atom), metta_mapping); + } + first_target = false; + } +} diff --git a/src/atomdb/AtomDBUtils.h b/src/atomdb/AtomDBUtils.h new file mode 100644 index 00000000..3381373c --- /dev/null +++ b/src/atomdb/AtomDBUtils.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include +#include + +#include "AtomDB.h" + +using namespace std; + +namespace atomdb { + +/** + * + */ +class AtomDBUtils { + public: + AtomDBUtils(); + ~AtomDBUtils(); + + private: + static void reachable_terminal_set_recursive(set& output, + shared_ptr link, + bool metta_mapping); + + public: + /** + * The reachable set of a given Link contains any Node in its target list plus any Node + * reachable through the recursive application of this method to the Links in its + * targets list. The reachable set of a Node is the Node itself. + * + * @param output A std::set where output is supposed to be placed. + * @param handle The handle of the starting link. If a Node handle is passed + * instead, the output will be this single handle. + * @param metta_mapping Optional flag to indicate the use of MeTTa mapping. When MeTTa mapping + * is being used, the first element in a expression is not considered to be put in the output. + */ + static void reachable_terminal_set(set& output, + const string& handle, + bool metta_mapping = false); +}; + +} // namespace atomdb diff --git a/src/atomdb/BUILD b/src/atomdb/BUILD index 3395a676..fb0f072b 100644 --- a/src/atomdb/BUILD +++ b/src/atomdb/BUILD @@ -9,6 +9,7 @@ cc_library( ":atomdb", ":atomdb_api_types", ":atomdb_singleton", + ":atomdbutils", "//atomdb/adapterdb:adapterdb_lib", "//atomdb/inmemorydb:inmemorydb_lib", "//atomdb/morkdb:morkdb_lib", @@ -26,6 +27,17 @@ cc_library( ], ) +cc_library( + name = "atomdbutils", + srcs = ["AtomDBUtils.cc"], + hdrs = ["AtomDBUtils.h"], + includes = ["."], + deps = [ + ":atomdb", + ":atomdb_singleton", + ], +) + cc_library( name = "atomdb_api_types", hdrs = ["AtomDBAPITypes.h"], diff --git a/src/commons/atoms/Atom.h b/src/commons/atoms/Atom.h index 4c972603..2cecba3e 100644 --- a/src/commons/atoms/Atom.h +++ b/src/commons/atoms/Atom.h @@ -86,11 +86,23 @@ class Atom : public HandleTrie::TrieValue { static bool is_node(const Atom& atom) { return atom.arity() == 0; } /** - * @brief Returns truee iff the passed atom is a Link (or a LinkSchema, which is a wildcard Link). + * @brief Returns true iff the passed atom is a Link (or a LinkSchema, which is a wildcard Link). * @return truee iff the passed atom is a Node (or a LinkSchema, which is a wildcard Link). */ static bool is_link(const Atom& atom) { return atom.arity() > 0; } + /** + * @brief Returns true iff the passed atom is a Node (or a Variable, which is a wildcard Node). + * @return truee iff the passed atom is a Node (or a Variable, which is a wildcard Node). + */ + static bool is_node(shared_ptr atom) { return atom->arity() == 0; } + + /** + * @brief Returns true iff the passed atom is a Link (or a LinkSchema, which is a wildcard Link). + * @return truee iff the passed atom is a Node (or a LinkSchema, which is a wildcard Link). + */ + static bool is_link(shared_ptr atom) { return atom->arity() > 0; } + // --------------------------------------------------------------------------------------------- // Default implementation for virtual public API diff --git a/src/tests/cpp/BUILD b/src/tests/cpp/BUILD index db587a02..ac8e4890 100644 --- a/src/tests/cpp/BUILD +++ b/src/tests/cpp/BUILD @@ -801,6 +801,30 @@ cc_test( ], ) +cc_test( + name = "atomdbutils_test", + size = "small", + srcs = ["atomdbutils_test.cc"], + copts = [ + "-Iexternal/gtest/googletest/include", + "-Iexternal/gtest/googletest", + ], + linkopts = [ + "-L/usr/local/lib", + "-lhiredis_cluster", + "-lhiredis", + "-lmongocxx", + "-lbsoncxx", + ], + linkstatic = 1, + deps = [ + "//atomdb:atomdb_lib", + "//commons/atoms:atoms_lib", + "//tests/cpp/test_commons:test_atomdb_json_config", + "@com_github_google_googletest//:gtest_main", + ], +) + cc_test( name = "remote_atomdb_test", size = "small", diff --git a/src/tests/cpp/atomdbutils_test.cc b/src/tests/cpp/atomdbutils_test.cc new file mode 100644 index 00000000..dc30b962 --- /dev/null +++ b/src/tests/cpp/atomdbutils_test.cc @@ -0,0 +1,129 @@ +#include "AtomDBUtils.h" + +#include + +#include "AtomDBSingleton.h" +#include "InMemoryDB.h" +#include "TestAtomDBJsonConfig.h" + +using namespace atomdb; +using namespace atoms; +using namespace std; + +TEST(AtomDBTest, reachable_terminal_set) { + AtomDBSingleton::init(test_atomdb_json_config()); + auto db = AtomDBSingleton::get_instance(); + + auto A = new Node("Symbol", "A"); + auto B = new Node("Symbol", "B"); + auto C = new Node("Symbol", "C"); + auto D = new Node("Symbol", "D"); + auto E = new Node("Symbol", "E"); + auto F = new Node("Symbol", "F"); + auto G = new Node("Symbol", "G"); + auto H = new Node("Symbol", "H"); + auto I = new Node("Symbol", "I"); + auto J = new Node("Symbol", "J"); + auto K = new Node("Symbol", "K"); + auto NOT_ADDED = new Node("Symbol", "NOT_ADDED"); + cout << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 1" << endl; + db->add_nodes({A, B, C, D, E, F, G, H, I, J, K}); + cout << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2" << endl; + + auto L6 = new Link("Expression", {I->handle(), J->handle(), K->handle()}, true); + cout << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 3" << endl; + db->add_link(L6); + auto L5 = new Link("Expression", {C->handle(), D->handle()}, true); + cout << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 4" << endl; + db->add_link(L5); + auto L4 = new Link("Expression", {L5->handle(), E->handle()}, true); + cout << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 5" << endl; + db->add_link(L4); + auto L3 = new Link("Expression", {L4->handle(), F->handle()}, true); + cout << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 6" << endl; + db->add_link(L3); + auto L2 = new Link("Expression", {G->handle(), L6->handle(), H->handle()}, true); + cout << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 7" << endl; + db->add_link(L2); + auto L1 = new Link("Expression", {A->handle(), B->handle(), L3->handle()}, true); + cout << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 8" << endl; + db->add_link(L1); + auto L0 = new Link("Expression", {L1->handle(), L2->handle()}, true); + cout << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 9" << endl; + db->add_link(L0); + + string a = A->handle(); + string b = B->handle(); + string c = C->handle(); + string d = D->handle(); + string e = E->handle(); + string f = F->handle(); + string g = G->handle(); + string h = H->handle(); + string i = I->handle(); + string j = J->handle(); + string k = K->handle(); + + set handles; + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, NOT_ADDED->handle()); + EXPECT_EQ(handles.size(), 0); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, a); + EXPECT_EQ(handles, set({a})); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, L1->handle()); + EXPECT_EQ(handles, set({a, b, c, d, e, f})); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, L2->handle()); + EXPECT_EQ(handles, set({g, h, i, j, k})); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, L3->handle()); + EXPECT_EQ(handles, set({c, d, e, f})); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, L4->handle()); + EXPECT_EQ(handles, set({c, d, e})); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, L5->handle()); + EXPECT_EQ(handles, set({c, d})); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, L6->handle()); + EXPECT_EQ(handles, set({i, j, k})); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, L0->handle()); + EXPECT_EQ(handles, set({a, b, c, d, e, f, g, h, i, j, k})); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, L6->handle(), true); + EXPECT_EQ(handles, set({j, k})); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, L2->handle(), true); + EXPECT_EQ(handles, set({h, j, k})); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, L4->handle(), true); + EXPECT_EQ(handles, set({d, e})); + + handles.clear(); + AtomDBUtils::reachable_terminal_set(handles, L1->handle(), true); + EXPECT_EQ(handles, set({b, d, e, f})); + + db->delete_links({L0->handle(), + L1->handle(), + L2->handle(), + L3->handle(), + L4->handle(), + L5->handle(), + L6->handle()}, + true); +}