3#include <clingo/ground/base.hh>
4#include <clingo/ground/instantiator.hh>
5#include <clingo/ground/term.hh>
7#include <clingo/util/unordered_map.hh>
9#include <clingo/core/core.hh>
12#include <memory_resource>
14namespace CppClingo::Ground {
29 b.begin(std::declval<MatcherType>());
30 b.end(std::declval<MatcherType>());
31 b.contains(std::declval<typename Base::Key>(), std::declval<MatcherType>());
32 { b.nth(std::declval<size_t>()).key() } -> std::same_as<typename Base::Key const &>;
34 { b.template context<int>() } -> std::same_as<int &>;
35} &&
requires(
Base const &b) {
36 { b.nth(std::declval<size_t>()).key() } -> std::same_as<typename Base::Key const &>;
48concept IsMatch =
requires(Match
const &m) {
49 { m.vars() } -> std::same_as<VariableSet>;
50 m.match(std::declval<EvalContext const &>(), std::declval<typename Match::Key>());
51 m.eval(std::declval<EvalContext const &>());
52 m.signature(std::declval<VariableSet const &>(), std::declval<VariableSet const &>());
53 std::declval<std::ostream &>() << m;
58concept IsEval =
requires(Eval
const &m) { m.eval(std::declval<EvalContext const &>()); };
60static constexpr auto invalid_offset = std::numeric_limits<size_t>::max();
75 virtual auto do_once([[maybe_unused]]
EvalContext const &ctx) ->
bool {
return true; }
77 void do_init([[maybe_unused]]
InstantiationContext const &ctx, [[maybe_unused]]
size_t gen)
override {}
78 void do_match([[maybe_unused]] EvalContext
const &ctx)
override { match_ =
true; }
79 auto do_next(EvalContext
const &ctx) ->
bool override {
86 void do_print(std::ostream &out)
const override { out <<
"#once"; }
116template <IsBase Base, IsMatch Match>
122template <IsBase Base, IsMatch Match>
123auto make_atom_matcher(std::pmr::monotonic_buffer_resource &mbr, std::vector<bool>
const &bound,
Base &base,
128template <IsBase Base>
class FullIndex {
130 FullIndex(
Base &base) : base_{&base} {}
131 void init(
size_t gen) { base_->update(gen); }
132 auto match(
MatcherType type) -> std::pair<size_t, size_t> {
134 auto cur = base_->begin(type);
136 return {std::distance(index_.begin(),
137 std::ranges::upper_bound(index_, cur, std::less<>{},
138 [](
auto const &a) ->
decltype(
auto) { return a.second; })),
141 template <IsMatch Match>
143 size_t &prev) ->
bool {
144 auto n = base_->end(type);
147 for (; imported_ <= cur; ++imported_) {
152 for (
auto const &var : free) {
153 ctx.
ass()[var] = std::nullopt;
157 if (m.match(ctx, base_->nth(imported_).key())) {
158 if (index_.empty() || index_.back().second != imported_) {
160 index_.emplace_back(imported_, imported_ + 1);
162 ++index_.back().second;
164 if (imported_ == cur) {
170 }
else if (cur == imported_) {
176 for (; pos < index_.size(); ++pos) {
178 cur = std::max(cur, index_[pos].first);
184 if (cur < index_[pos].second) {
186 for (
auto const &var : free) {
187 ctx.
ass()[var] = std::nullopt;
189 return m.match(ctx, base_->nth(prev).key());
197 std::vector<std::pair<size_t, size_t>> index_;
198 size_t imported_ = 0;
203 BindVals() =
default;
206 for (
auto const &var : bind_vars) {
208 symbols_.emplace_back(*ass[var]);
211 void match(
size_t vars,
size_t begin, SymbolVec::iterator &it) {
212 it = symbols_.begin();
214 auto n =
static_cast<std::ptrdiff_t
>(vars + 1);
215 std::advance(it, offset_);
216 for (; it != symbols_.end() &&
Symbol::to_rep(*it) < begin; it += n, offset_ += n) {
220 auto next(
Assignment &ass,
VariableVec &bind_vars,
size_t end, SymbolVec::iterator &it,
size_t &offset) ->
bool {
223 for (
auto const &var : bind_vars) {
236template <IsBase Base>
class SingleIndex {
241 using KeyIterator = IndexMap::iterator;
242 using ValIterator = SymbolVec::iterator;
244 SingleIndex(
Base &base) : base_{&base} {}
245 void init(
size_t gen) { base_->update(gen); }
247 template <IsMatch Match>
249 KeyIterator &it, ValIterator &jt) {
250 auto &ass = ctx.
ass();
252 auto bound_val = *ass[bound_var];
255 for (; imported_ < n; ++imported_) {
257 ass[bound_var] = std::nullopt;
258 for (
auto const &var : bind_vars) {
259 ass[var] = std::nullopt;
262 if (m.match(ctx, base_->nth(imported_).key())) {
263 index_.try_emplace(*ass[bound_var]).first.value().add(ass, bind_vars, imported_);
267 ass[bound_var] = bound_val;
270 if (it = index_.find(bound_val); it != index_.end()) {
271 it.value().match(bind_vars.size(), base_->begin(type), jt);
276 size_t &offset) ->
bool {
277 return it != index_.end() && it.value().next(ctx.
ass(), bind_vars, base_->end(type), jt, offset);
283 size_t imported_ = 0;
286template <IsBase Base>
class HashIndex {
290 Key(
Symbol const *syms,
size_t hash)
293 syms_{
reinterpret_cast<uintptr_t
>(syms) | mask_} {}
294 [[nodiscard]]
auto hash()
const ->
size_t {
return hash_; }
295 [[nodiscard]]
auto marked()
const ->
bool {
return (syms_ & mask_) != 0; }
296 void unmark()
const { syms_ = syms_ & ~mask_; }
297 [[nodiscard]]
auto symbols()
const ->
Symbol const * {
299 return reinterpret_cast<Symbol const *
>(syms_ & ~mask_);
303 static constexpr uintptr_t mask_ = 1;
305 mutable uintptr_t syms_;
309 auto operator()(Key
const &a, Key
const &b)
const {
310 if (a.marked() || b.marked()) {
311 return std::equal(a.symbols(), std::next(a.symbols(), size), b.symbols(), std::next(b.symbols(), size));
313 return a.symbols() == b.symbols();
321 using KeyIterator = IndexMap::iterator;
322 using ValIterator = SymbolVec::iterator;
324 HashIndex(std::pmr::monotonic_buffer_resource &mbr,
Base &base,
size_t bound)
327 temp_values_.reserve(bound);
329 void init(
size_t gen) { base_->update(gen); }
331 template <IsMatch Match>
333 MatcherType type, KeyIterator &it, ValIterator &jt) {
334 auto &ass = ctx.
ass();
336 temp_values_.clear();
337 for (
auto const &var : bound_vars) {
338 temp_values_.emplace_back(*ass[var]);
342 for (; imported_ < n; ++imported_) {
344 for (
auto const &var : bound_vars) {
345 ass[var] = std::nullopt;
347 for (
auto const &var : bind_vars) {
348 ass[var] = std::nullopt;
351 if (m.match(ctx, base_->nth(imported_).key())) {
352 if (key_ ==
nullptr) {
354 static_cast<Symbol *
>(mbr_->allocate(bound_vars.size() *
sizeof(
Symbol),
alignof(
Symbol)));
357 for (
auto &var : bound_vars) {
358 std::construct_at(it, *ass[var]);
362 auto [kt, ins] = index_.try_emplace(Key{key_, key_hash});
367 kt.value().add(ass, bind_vars, imported_);
371 auto kt = temp_values_.begin();
372 for (
auto const &var : bound_vars) {
377 auto bound_hash =
Util::value_hash(std::span(temp_values_.data(), bound_vars.size()));
378 if (it = index_.find(Key{temp_values_.data(), bound_hash}); it != index_.end()) {
379 it.value().match(bind_vars.size(), base_->begin(type), jt);
384 size_t &offset) ->
bool {
385 return it != index_.end() && it.value().next(ctx.
ass(), bind_vars, base_->end(type), jt, offset);
389 std::pmr::monotonic_buffer_resource *mbr_;
394 size_t imported_ = 0;
397template <IsBase Base, IsMatch Match>
class LookupMatcher :
public OnceMatcher {
400 : base_{&base}, match_{&m}, type_{
type}, offset_{&offset} {}
403 void do_init([[maybe_unused]]
InstantiationContext const &ctx,
size_t gen)
override { base_->update(gen); }
404 auto do_once(
EvalContext const &ctx) ->
bool override {
405 if (
auto sym = match_->eval(ctx); sym) {
406 if (
auto idx = base_->contains(*sym, type_); idx) {
413 void do_print(std::ostream &out)
const override { out << *match_; }
414 [[nodiscard]]
auto do_type()
const ->
MatcherType override {
return type_; }
422template <IsBase Base, IsMatch Match>
class FullMatcher :
public Matcher {
424 using Index = FullIndex<Base>;
427 : index_{&index}, offset_{&offset}, match_{&m}, free_{std::move(free)}, type_{
type} {}
430 void do_init([[maybe_unused]]
InstantiationContext const &ctx,
size_t gen)
override { index_->init(gen); }
431 void do_match([[maybe_unused]]
EvalContext const &ctx)
override { std::tie(pos_, cur_) = index_->match(type_); }
432 auto do_next(
EvalContext const &ctx) ->
bool override {
433 return index_->next(ctx, *match_, free_, type_, pos_, cur_, *offset_);
435 void do_print(std::ostream &out)
const override { out << *match_; }
436 [[nodiscard]]
auto do_type()
const ->
MatcherType override {
return type_; }
447template <IsBase Base, IsMatch Match>
class SingleMatcher :
public Matcher {
449 using Index = SingleIndex<Base>;
450 using KeyIterator = Index::KeyIterator;
451 using ValIterator = Index::ValIterator;
454 : index_{&index}, match_{&m}, offset_{&offset}, bound_{bound}, bind_{std::move(bind)}, type_{
type} {}
457 void do_init([[maybe_unused]]
InstantiationContext const &ctx,
size_t gen)
override { index_->init(gen); }
458 void do_match([[maybe_unused]]
EvalContext const &ctx)
override {
459 return index_->match(ctx, bound_, bind_, *match_, type_, pos_, cur_);
461 auto do_next(
EvalContext const &ctx) ->
bool override {
462 return index_->next(ctx, bind_, type_, pos_, cur_, *offset_);
464 void do_print(std::ostream &out)
const override { out << *match_; }
465 [[nodiscard]]
auto do_type()
const ->
MatcherType override {
return type_; }
477template <IsBase Base, IsMatch Match>
class HashMatcher :
public Matcher {
479 using Index = HashIndex<Base>;
480 using KeyIterator = Index::KeyIterator;
481 using ValIterator = Index::ValIterator;
484 : index_{&index}, match_{&m}, offset_{&offset}, bound_{std::move(bound)}, bind_{std::move(bind)}, type_{
type} {}
487 void do_init([[maybe_unused]]
InstantiationContext const &ctx,
size_t gen)
override { index_->init(gen); }
488 void do_match([[maybe_unused]]
EvalContext const &ctx)
override {
489 return index_->match(ctx, bound_, bind_, *match_, type_, pos_, cur_);
491 auto do_next(
EvalContext const &ctx) ->
bool override {
492 return index_->next(ctx, bind_, type_, pos_, cur_, *offset_);
494 void do_print(std::ostream &out)
const override { out << *match_; }
495 [[nodiscard]]
auto do_type()
const ->
MatcherType override {
return type_; }
507template <IsBase Base,
class Sig>
class IndexSet :
public BaseContext {
509 auto add_full(
Base &base, Sig sig) -> FullIndex<Base> & {
510 auto it = full_.try_emplace(std::move(sig),
nullptr).first;
511 if (it->second ==
nullptr) {
512 it.value() = std::make_unique<FullIndex<Base>>(base);
517 auto add_single(
Base &base, Sig sig) -> SingleIndex<Base> & {
518 auto it = single_.try_emplace(std::move(sig),
nullptr).first;
519 if (it->second ==
nullptr) {
520 it.value() = std::make_unique<SingleIndex<Base>>(base);
525 auto add_hash(std::pmr::monotonic_buffer_resource &mbr,
Base &base, Sig sig,
size_t bound) -> HashIndex<Base> & {
526 auto it = hash_.try_emplace(std::move(sig),
nullptr).first;
527 if (it->second ==
nullptr) {
528 it.value() = std::make_unique<HashIndex<Base>>(mbr, base, bound);
540template <IsBase Base, IsMatch Match>
class NonFactMatcher :
public OnceMatcher {
542 NonFactMatcher(
Base &base, Match
const &
match,
typename Match::Key &target)
543 : base_{&base}, match_{&
match}, target_{&target} {}
546 void do_init([[maybe_unused]]
InstantiationContext const &ctx,
size_t gen)
override { base_->update(gen); }
547 auto do_once(
EvalContext const &ctx) ->
bool override {
548 if (
auto sym = match_->eval(ctx)) {
550 return !base_->is_fact(*sym);
554 void do_print(std::ostream &out)
const override { out <<
"#not_fact " << *match_; }
558 typename Match::Key *target_;
562template <IsMatch Match>
class EvalMatcher :
public OnceMatcher {
565 EvalMatcher(Match
const &
match,
typename Match::Key &target) : match_{&
match}, target_{&target} {}
569 auto do_once(
EvalContext const &ctx) ->
bool override {
570 if (
auto sym = match_->eval(ctx)) {
578 typename Match::Key *target_;
583template <IsBase Base, IsMatch Match>
588 erase_if(bind, [&bound, &lookup](
auto const &var) {
596 return std::make_unique<Detail::LookupMatcher<Base, Match>>(base, atom, type, offset);
598 auto &ctx = base.template context<Detail::IndexSet<
Base,
decltype(atom.signature(lookup, bind))>>();
600 if (lookup.empty()) {
601 auto &full = ctx.add_full(base, atom.signature(lookup, bind));
602 return std::make_unique<Detail::FullMatcher<Base, Match>>(full, atom, bind.release(), type, offset);
605 if (lookup.size() == 1) {
606 auto &hash = ctx.add_single(base, atom.signature(lookup, bind));
607 return std::make_unique<Detail::SingleMatcher<Base, Match>>(hash, atom, lookup.front(), bind.release(), type,
611 auto &hash = ctx.add_hash(mbr, base, atom.signature(lookup, bind), lookup.size());
612 return std::make_unique<Detail::HashMatcher<Base, Match>>(hash, atom, lookup.release(), bind.release(), type,
616template <IsBase Base, IsMatch Match>
618 return std::make_unique<Detail::NonFactMatcher<Base, Match>>(base, match, target);
622 return std::make_unique<Detail::EvalMatcher<Expr>>(expr, target);
A context object.
Definition base.hh:139
Context object to capture state used during instantiation.
Definition instantiator.hh:35
auto ass() const -> Assignment &
Get the assignment.
Definition instantiator.hh:41
Context object to capture state used during instantiation.
Definition instantiator.hh:17
A matcher to match expressions.
Definition instantiator.hh:74
void match(EvalContext const &ctx)
Initialize matching.
Definition instantiator.hh:81
auto type() const -> MatcherType
Get the type of the matcher.
Definition instantiator.hh:90
A matcher that matches only provides one match.
Definition matcher.hh:66
OnceMatcher()=default
Construct the matcher.
Term interface.
Definition term.hh:67
Variant-like class to store symbols stored in a symbol store.
Definition symbol.hh:225
static auto from_rep(uint64_t rep) noexcept -> Symbol
Create a symbol from its representation.
Definition symbol.hh:284
static auto to_rep(Symbol sym) noexcept -> uint64_t
Get the representation of the symbol.
Definition symbol.hh:282
Concept for atom bases.
Definition matcher.hh:28
Concept for evaluable expressions.
Definition matcher.hh:58
Concept for matchable expressions.
Definition matcher.hh:48
Base
The base of a number.
Definition number.hh:17
std::vector< std::optional< Symbol > > Assignment
Assignment mapping variables to symbols.
Definition symbol.hh:222
std::vector< Symbol > SymbolVec
A vector of symbols.
Definition symbol.hh:220
Relation
Enumeration of supported relations.
Definition core.hh:35
VariableSet::values_container_type VariableVec
A vector of variables.
Definition base.hh:21
Util::ordered_set< size_t > VariableSet
A set of variables.
Definition base.hh:19
MatcherType
Enumeration of matcher types.
Definition instantiator.hh:48
std::unique_ptr< Matcher > UMatcher
A unique pointer to a matcher.
Definition instantiator.hh:100
@ all_atoms
Indicates a matcher for previously added atoms.
auto make_comp_matcher(std::vector< bool > const &bound, Term const &lhs, Relation rel, Term const &rhs) -> UMatcher
Construct a matcher for comparisons.
auto make_non_fact_matcher(Base &base, Match const &match, typename Match::Key &target) -> UMatcher
Construct a matcher for facts.
Definition matcher.hh:617
auto make_atom_matcher(std::pmr::monotonic_buffer_resource &mbr, std::vector< bool > const &bound, Base &base, Match const &atom, MatcherType type, size_t &offset) -> UMatcher
Construct a matcher for an atom.
Definition matcher.hh:584
auto make_interval_matcher(std::vector< bool > const &bound, Term const &lhs, Term const &lower, Term const &upper) -> UMatcher
Construct an interval matcher.
auto make_once_matcher() -> UMatcher
Construct a matcher matching only once.
tsl::hopscotch_map< Key, T, Hash, KeyEqual, Allocator, NeighborhoodSize, StoreHash, GrowthPolicy > unordered_map
Alias for unordered maps.
Definition unordered_map.hh:17
auto value_hash(std::type_info const &x) -> size_t
Compute a hash for type_info.
Definition hash.hh:277
Compute a hash using std::hash.
Definition hash.hh:85