@@ -774,9 +774,10 @@ namespace libsemigroups {
774774 Node1 next_preorder_num = 0 ;
775775 std::vector<Node1> postorder (N, N);
776776 Node1 next_postorder_num = 0 ;
777- // TODO(1) there should be a better way of doing this
777+
778+ auto ancestors = ancestors_of_no_checks (wg, target);
778779 for (auto n : wg.nodes ()) {
779- if (! is_reachable (wg, n, static_cast <Node1>(target)) ) {
780+ if (ancestors. count (n) == 0 ) {
780781 preorder[n] = N + 1 ;
781782 }
782783 }
@@ -875,6 +876,55 @@ namespace libsemigroups {
875876 return nodes_reachable_from_no_checks (wg, source);
876877 }
877878
879+ template <typename Node1, typename Node2>
880+ std::unordered_set<Node1> ancestors_of_no_checks (WordGraph<Node1> const & wg,
881+ Node2 target) {
882+ static_assert (sizeof (Node2) <= sizeof (Node1));
883+ using label_type = typename WordGraph<Node1>::label_type;
884+
885+ size_t const N = wg.number_of_nodes ();
886+ size_t const M = wg.out_degree ();
887+
888+ // Reverse the WordGraph and then just find the nodes reachable from
889+ // target in the reversed graph. Since the reverse of a WordGraph is no
890+ // longer a WordGraph we use a vector of vectors here. Alternatively, we
891+ // could use the technique used in WordGraphWithSources (the sources are
892+ // essentially the reversed graph) to create the reversed graph (or just
893+ // use it if we know it already, like in ToddCoxeter).
894+ std::vector<std::vector<Node1>> in_neighbours (N, std::vector<Node1>({}));
895+ for (Node1 s = 0 ; s < N; ++s) {
896+ for (label_type a = 0 ; a < M; ++a) {
897+ auto t = wg.target_no_checks (s, a);
898+ if (t != UNDEFINED) {
899+ in_neighbours[t].push_back (s);
900+ }
901+ }
902+ }
903+
904+ std::unordered_set<Node1> seen;
905+ std::stack<Node1> stack;
906+ stack.push (target);
907+
908+ while (!stack.empty ()) {
909+ Node1 s = stack.top ();
910+ stack.pop ();
911+ if (seen.insert (s).second ) {
912+ for (auto t : in_neighbours[s]) {
913+ stack.push (t);
914+ }
915+ }
916+ }
917+ return seen;
918+ }
919+
920+ template <typename Node1, typename Node2>
921+ std::unordered_set<Node1> ancestors_of (WordGraph<Node1> const & wg,
922+ Node2 target) {
923+ static_assert (sizeof (Node2) <= sizeof (Node1));
924+ throw_if_node_out_of_bounds (wg, static_cast <Node1>(target));
925+ return ancestors_of_no_checks (wg, target);
926+ }
927+
878928 template <typename Node1, typename Node2, typename Iterator>
879929 Node1 follow_path (WordGraph<Node1> const & wg,
880930 Node2 from,
@@ -1552,8 +1602,8 @@ namespace libsemigroups {
15521602 _uf.init (xnum_nodes_reachable_from_root + ynum_nodes_reachable_from_root);
15531603 _uf.unite (xroot, yroot + xnum_nodes_reachable_from_root);
15541604
1555- // The stack can't be empty if this function runs to the end so no need to
1556- // do anything.
1605+ // The stack can't be empty if this function runs to the end so no need
1606+ // to do anything.
15571607 LIBSEMIGROUPS_ASSERT (_stck.empty ());
15581608 // 0 .. x.number_of_nodes() - 1, x.number_of_nodes() ..
15591609 // x.number_of_nodes() + y.number_of_nodes() -1
@@ -1599,9 +1649,9 @@ namespace libsemigroups {
15991649 ynum_nodes_reachable_from_root,
16001650 yroot);
16011651 _uf.normalize ();
1602- // It can be that _uf is equivalent to [0, 0, 2] at this point (and there's
1603- // no way for it to not be like this, because 2 doesn't belong to the class
1604- // of 0), and so we require the following lookup.
1652+ // It can be that _uf is equivalent to [0, 0, 2] at this point (and
1653+ // there's no way for it to not be like this, because 2 doesn't belong
1654+ // to the class of 0), and so we require the following lookup.
16051655 _lookup.resize (xnum_nodes_reachable_from_root);
16061656 LIBSEMIGROUPS_ASSERT (_lookup.size () == xnum_nodes_reachable_from_root);
16071657 std::fill (_lookup.begin (), _lookup.end (), static_cast <Node>(UNDEFINED));
0 commit comments