From 1e58726a7aaaf14644f949d4e163a33f2e3c4562 Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Fri, 25 Oct 2024 23:14:03 +0200 Subject: [PATCH 1/7] Make parallel parsing the default again for a single input stream (#1585) With the introduction of mutiple input streams (through #1537), the default regarding "parallel-parsing" became "false". That was unfortunate because most existing configurations (in particular, all the Qleverfiles from https://github.com/ad-freiburg/qlever-control) do not set the "parallel-parsing" option explicitly. Without parallel parsing, indexing is much slower. This felt like a regression for many users, for example, see #1563. This is now fixed as follows: When there is a single input stream, and "parallel-parsing" is neither specified in the `settings.json` file (which is deprecated) nor on the command line for `IndexBuilderMain` (new since #1537), then the default is "true". A warning is shown that this is deprecated. The QLever CLI and Qleverfiles from https://github.com/ad-freiburg/qlever-control will be adapted to avoid this deprecated behavior. Co-authored-by: Hannah Bast --- src/index/IndexImpl.cpp | 13 +++++++++++-- src/index/IndexImpl.h | 5 +++-- test/IndexTest.cpp | 16 ++++++++++++---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/index/IndexImpl.cpp b/src/index/IndexImpl.cpp index 90cadea65..cd12e58b6 100644 --- a/src/index/IndexImpl.cpp +++ b/src/index/IndexImpl.cpp @@ -289,7 +289,7 @@ std::pair IndexImpl::createInternalPSOandPOS( // _____________________________________________________________________________ void IndexImpl::updateInputFileSpecificationsAndLog( std::vector& spec, - bool parallelParsingSpecifiedViaJson) { + std::optional parallelParsingSpecifiedViaJson) { if (spec.size() == 1) { LOG(INFO) << "Processing triples from " << spec.at(0).filename_ << " ..." << std::endl; @@ -297,7 +297,7 @@ void IndexImpl::updateInputFileSpecificationsAndLog( LOG(INFO) << "Processing triples from " << spec.size() << " input streams ..." << std::endl; } - if (parallelParsingSpecifiedViaJson) { + if (parallelParsingSpecifiedViaJson.value_or(false) == true) { if (spec.size() == 1) { LOG(WARN) << "Parallel parsing set to `true` in the `.settings.json` " "file; this is deprecated, please use the command-line " @@ -311,6 +311,15 @@ void IndexImpl::updateInputFileSpecificationsAndLog( " via the command-line option --parse-parallel or -p"}; } } + + if (spec.size() == 1 && !parallelParsingSpecifiedViaJson.has_value()) { + LOG(WARN) << "Implicitly using the parallel parser for a single input file " + "for reasons of backward compatibility; this is deprecated, " + "please use the command-line option --parse-parallel or -p " + "instead" + << std::endl; + spec.at(0).parseInParallel_ = true; + } } // _____________________________________________________________________________ diff --git a/src/index/IndexImpl.h b/src/index/IndexImpl.h index c2c0c0c80..d62f4a7e1 100644 --- a/src/index/IndexImpl.h +++ b/src/index/IndexImpl.h @@ -119,7 +119,8 @@ class IndexImpl { string onDiskBase_; string settingsFileName_; bool onlyAsciiTurtlePrefixes_ = false; - bool useParallelParser_ = false; + // Note: `std::nullopt` means `not specified by the user`. + std::optional useParallelParser_ = std::nullopt; TurtleParserIntegerOverflowBehavior turtleParserIntegerOverflowBehavior_ = TurtleParserIntegerOverflowBehavior::Error; bool turtleParserSkipIllegalLiterals_ = false; @@ -777,5 +778,5 @@ class IndexImpl { // and write a summary to the log. static void updateInputFileSpecificationsAndLog( std::vector& spec, - bool parallelParsingSpecifiedViaJson); + std::optional parallelParsingSpecifiedViaJson); }; diff --git a/test/IndexTest.cpp b/test/IndexTest.cpp index 9b472453d..b707b1111 100644 --- a/test/IndexTest.cpp +++ b/test/IndexTest.cpp @@ -509,13 +509,13 @@ TEST(IndexTest, trivialGettersAndSetters) { EXPECT_EQ(std::as_const(index).memoryLimitIndexBuilding(), 7_kB); } -TEST(IndexTest, loggingAndSettingOfParallelParsing) { +TEST(IndexTest, updateInputFileSpecificationsAndLog) { using enum qlever::Filetype; std::vector files{ {"singleFile.ttl", Turtle, std::nullopt, false}}; - testing::internal::CaptureStdout(); using namespace ::testing; { + testing::internal::CaptureStdout(); IndexImpl::updateInputFileSpecificationsAndLog(files, false); EXPECT_THAT( testing::internal::GetCapturedStdout(), @@ -527,8 +527,16 @@ TEST(IndexTest, loggingAndSettingOfParallelParsing) { testing::internal::CaptureStdout(); IndexImpl::updateInputFileSpecificationsAndLog(files, true); EXPECT_THAT(testing::internal::GetCapturedStdout(), - AllOf(HasSubstr("from singleFile.ttl"), HasSubstr("deprecated"), - HasSubstr("--parse-parallel"))); + AllOf(HasSubstr("from singleFile.ttl"), + HasSubstr("settings.json"), HasSubstr("deprecated"))); + EXPECT_TRUE(files.at(0).parseInParallel_); + } + { + testing::internal::CaptureStdout(); + IndexImpl::updateInputFileSpecificationsAndLog(files, std::nullopt); + EXPECT_THAT(testing::internal::GetCapturedStdout(), + AllOf(HasSubstr("from singleFile.ttl"), + HasSubstr("single input"), HasSubstr("deprecated"))); EXPECT_TRUE(files.at(0).parseInParallel_); } From 6e6aa7bafa80f042cf713f8a11fbe7409edaadf1 Mon Sep 17 00:00:00 2001 From: Julian <14220769+Qup42@users.noreply.github.com> Date: Sat, 26 Oct 2024 08:58:05 +0200 Subject: [PATCH 2/7] Parse all SPARQL Update Requests (#1574) The SPARQL parser now supports all kinds of `UPDATE`s. These are parsed into a unified class `UpdateClause` which is part of the `ParsedQuery`. This is another major step towards support for SPARQL UPDATE. --- src/parser/SparqlTriple.h | 16 ++ src/parser/UpdateClause.h | 69 +++++- src/parser/data/GraphRef.h | 20 +- .../sparqlParser/SparqlQleverVisitor.cpp | 226 +++++++++++------- src/parser/sparqlParser/SparqlQleverVisitor.h | 41 ++-- .../sparqlParser/generated/SparqlAutomatic.g4 | 2 +- .../generated/SparqlAutomatic.interp | 2 +- .../generated/SparqlAutomaticParser.cpp | 19 +- .../generated/SparqlAutomaticParser.h | 3 +- test/SparqlAntlrParserTest.cpp | 209 ++++++++++++---- test/SparqlAntlrParserTestHelpers.h | 110 ++++++++- 11 files changed, 543 insertions(+), 174 deletions(-) diff --git a/src/parser/SparqlTriple.h b/src/parser/SparqlTriple.h index 0efe19b8b..afe45fe4e 100644 --- a/src/parser/SparqlTriple.h +++ b/src/parser/SparqlTriple.h @@ -5,6 +5,7 @@ #pragma once +#include #include #include "PropertyPath.h" @@ -53,6 +54,21 @@ class SparqlTripleSimple : public SparqlTripleBase { using Base::Base; }; +class SparqlTripleSimpleWithGraph : public SparqlTripleSimple { + public: + using Graph = std::variant; + + SparqlTripleSimpleWithGraph(TripleComponent s, TripleComponent p, + TripleComponent o, Graph g, + AdditionalScanColumns additionalScanColumns = {}) + : SparqlTripleSimple(std::move(s), std::move(p), std::move(o), + std::move(additionalScanColumns)), + g_{std::move(g)} {} + Graph g_; + + bool operator==(const SparqlTripleSimpleWithGraph&) const = default; +}; + // A triple where the predicate is a `PropertyPath` (which technically still // might be a variable or fixed entity in the current implementation). class SparqlTriple : public SparqlTripleBase { diff --git a/src/parser/UpdateClause.h b/src/parser/UpdateClause.h index 85f79bc04..16abed871 100644 --- a/src/parser/UpdateClause.h +++ b/src/parser/UpdateClause.h @@ -4,18 +4,77 @@ #pragma once +#include "parser/Iri.h" #include "parser/SelectClause.h" #include "parser/SparqlTriple.h" +#include "parser/data/GraphRef.h" #include "parser/data/Types.h" +namespace updateClause { +struct Load { + bool silent_; + ad_utility::triple_component::Iri source_; + std::optional target_; +}; + +struct Clear { + bool silent_; + GraphRefAll target_; +}; + +struct Drop { + bool silent_; + GraphRefAll target_; +}; + +struct Create { + bool silent_; + GraphRef target_; +}; + +struct Add { + bool silent_; + GraphOrDefault source_; + GraphOrDefault target_; +}; + +struct Move { + bool silent_; + GraphOrDefault source_; + GraphOrDefault target_; +}; + +struct Copy { + bool silent_; + GraphOrDefault source_; + GraphOrDefault target_; +}; + +// A Graph Update is an Update operation that inserts or deletes some triples. +// These triples can contain variables that are bound the result of the +// ParsedQueries GraphPattern. This used for `INSERT DATA`, `DELETE DATA`, +// `DELETE WHERE {...}` and `DELETE/INSERT {..} WHERE {...}`. +struct GraphUpdate { + std::vector toInsert_; + std::vector toDelete_; + std::optional with_; + + GraphUpdate() = default; + GraphUpdate(std::vector toInsert, + std::vector toDelete) + : toInsert_{std::move(toInsert)}, toDelete_{std::move(toDelete)} {} +}; + +// All the available update operations. +using Operation = + std::variant; +} // namespace updateClause + namespace parsedQuery { struct UpdateClause : ClauseBase { - std::vector toInsert_; - std::vector toDelete_; + updateClause::Operation op_; UpdateClause() = default; - UpdateClause(std::vector toInsert, - std::vector toDelete) - : toInsert_{std::move(toInsert)}, toDelete_{std::move(toDelete)} {} + explicit UpdateClause(updateClause::Operation op) : op_{std::move(op)} {} }; } // namespace parsedQuery diff --git a/src/parser/data/GraphRef.h b/src/parser/data/GraphRef.h index fff4f19be..06b7e3ab5 100644 --- a/src/parser/data/GraphRef.h +++ b/src/parser/data/GraphRef.h @@ -9,9 +9,23 @@ #include "parser/Iri.h" using GraphRef = TripleComponent::Iri; -struct DEFAULT {}; -struct NAMED {}; -struct ALL {}; +// Denotes the target graph for an operation. Here the target is the default +// graph. +struct DEFAULT { + // For testing + bool operator==(const DEFAULT&) const = default; +}; +// Denotes the target graphs for an operation. Here the target are all named +// graphs. +struct NAMED { + // For testing + bool operator==(const NAMED&) const = default; +}; +// Denotes the target graphs for an operation. Here the target are all graphs. +struct ALL { + // For testing + bool operator==(const ALL&) const = default; +}; using GraphRefAll = std::variant; using GraphOrDefault = std::variant; diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 8c73f9b8a..d1f0242cd 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -35,6 +35,7 @@ using namespace ad_utility::sparql_types; using namespace sparqlExpression; +using namespace updateClause; using ExpressionPtr = sparqlExpression::SparqlExpression::Ptr; using SparqlExpressionPimpl = sparqlExpression::SparqlExpressionPimpl; using SelectClause = parsedQuery::SelectClause; @@ -385,91 +386,73 @@ ParsedQuery Visitor::visit(Parser::UpdateContext* ctx) { // ____________________________________________________________________________________ ParsedQuery Visitor::visit(Parser::Update1Context* ctx) { - if (ctx->modify()) { - return visit(ctx->modify()); - } else if (ctx->clear()) { - return visit(ctx->clear()); - } - - parsedQuery_._clause = parsedQuery::UpdateClause(); - - if (ctx->insertData() || ctx->deleteData()) { - // handles insertData and deleteData cases - visitIf(&parsedQuery_.updateClause().toInsert_, ctx->insertData()); - visitIf(&parsedQuery_.updateClause().toDelete_, ctx->deleteData()); - } else if (ctx->deleteWhere()) { - auto [toDelete, pattern] = visit(ctx->deleteWhere()); - parsedQuery_.updateClause().toDelete_ = std::move(toDelete); - parsedQuery_._rootGraphPattern = std::move(pattern); + if (ctx->deleteWhere() || ctx->modify()) { + return visitAlternative(ctx->deleteWhere(), ctx->modify()); } else { - visitAlternative(ctx->load(), ctx->drop(), ctx->add(), ctx->move(), - ctx->copy(), ctx->create()); - AD_FAIL(); + parsedQuery_._clause = visitAlternative( + ctx->load(), ctx->clear(), ctx->drop(), ctx->create(), ctx->add(), + ctx->move(), ctx->copy(), ctx->insertData(), ctx->deleteData()); } return parsedQuery_; } // ____________________________________________________________________________________ -void Visitor::visit(const Parser::LoadContext* ctx) const { - reportNotSupported(ctx, "SPARQL 1.1 Update Load is"); +Load Visitor::visit(Parser::LoadContext* ctx) { + return Load{ + static_cast(ctx->SILENT()), visit(ctx->iri()), + ctx->graphRef() ? visit(ctx->graphRef()) : std::optional{}}; } // ____________________________________________________________________________________ -ParsedQuery Visitor::visit(Parser::ClearContext* ctx) { - auto graphRef = visit(ctx->graphRefAll()); - - if (holds_alternative(graphRef)) { - parsedQuery_._clause = parsedQuery::UpdateClause(); - parsedQuery_.updateClause().toDelete_ = { - {Variable("?s"), Variable("?p"), Variable("?o")}}; - parsedQuery_._rootGraphPattern._graphPatterns.emplace_back( - BasicGraphPattern{{{Variable("?s"), "?p", Variable("?o")}}}); - return parsedQuery_; - } else { - reportNotSupported(ctx, "Named Graphs are"); - } +Clear Visitor::visit(Parser::ClearContext* ctx) { + return Clear{static_cast(ctx->SILENT()), visit(ctx->graphRefAll())}; } // ____________________________________________________________________________________ -void Visitor::visit(const Parser::DropContext* ctx) const { - reportNotSupported(ctx, "SPARQL 1.1 Update Drop is"); +Drop Visitor::visit(Parser::DropContext* ctx) { + return Drop{static_cast(ctx->SILENT()), visit(ctx->graphRefAll())}; } // ____________________________________________________________________________________ -void Visitor::visit(const Parser::CreateContext* ctx) const { - reportNotSupported(ctx, "SPARQL 1.1 Update Create is"); +Create Visitor::visit(Parser::CreateContext* ctx) { + return Create{static_cast(ctx->SILENT()), visit(ctx->graphRef())}; } // ____________________________________________________________________________________ -void Visitor::visit(const Parser::AddContext* ctx) const { - reportNotSupported(ctx, "SPARQL 1.1 Update Add is"); +Add Visitor::visit(Parser::AddContext* ctx) { + AD_CORRECTNESS_CHECK(ctx->graphOrDefault().size() == 2); + return Add{static_cast(ctx->SILENT()), + visit(ctx->graphOrDefault().at(0)), + visit(ctx->graphOrDefault().at(1))}; } // ____________________________________________________________________________________ -void Visitor::visit(const Parser::MoveContext* ctx) const { - reportNotSupported(ctx, "SPARQL 1.1 Update Move is"); +Move Visitor::visit(Parser::MoveContext* ctx) { + AD_CORRECTNESS_CHECK(ctx->graphOrDefault().size() == 2); + return Move{static_cast(ctx->SILENT()), + visit(ctx->graphOrDefault().at(0)), + visit(ctx->graphOrDefault().at(1))}; } // ____________________________________________________________________________________ -void Visitor::visit(const Parser::CopyContext* ctx) const { - reportNotSupported(ctx, "SPARQL 1.1 Update Copy is"); +Copy Visitor::visit(Parser::CopyContext* ctx) { + return Copy{static_cast(ctx->SILENT()), visit(ctx->graphOrDefault()[0]), + visit(ctx->graphOrDefault()[1])}; } // ____________________________________________________________________________________ -vector Visitor::visit(Parser::InsertDataContext* ctx) { - return visit(ctx->quadData()); +GraphUpdate Visitor::visit(Parser::InsertDataContext* ctx) { + return {visit(ctx->quadData()), {}}; } // ____________________________________________________________________________________ -vector Visitor::visit(Parser::DeleteDataContext* ctx) { - return visit(ctx->quadData()); +GraphUpdate Visitor::visit(Parser::DeleteDataContext* ctx) { + return {{}, visit(ctx->quadData())}; } // ____________________________________________________________________________________ -std::pair, ParsedQuery::GraphPattern> Visitor::visit( - Parser::DeleteWhereContext* ctx) { - auto triples = visit(ctx->quadPattern()); +ParsedQuery Visitor::visit(Parser::DeleteWhereContext* ctx) { auto registerIfVariable = [this](const TripleComponent& component) { if (component.isVariable()) { addVisibleVariable(component.getVariable()); @@ -486,39 +469,73 @@ std::pair, ParsedQuery::GraphPattern> Visitor::visit( AD_CORRECTNESS_CHECK(triple.p_.isVariable() || triple.p_.isIri()); return SparqlTriple::fromSimple(triple); }; + AD_CORRECTNESS_CHECK(visibleVariables_.empty()); GraphPattern pattern; + auto triples = visit(ctx->quadPattern()); pattern._graphPatterns.emplace_back(BasicGraphPattern{ ad_utility::transform(triples, transformAndRegisterTriple)}); + parsedQuery_._rootGraphPattern = std::move(pattern); + parsedQuery_.registerVariablesVisibleInQueryBody(visibleVariables_); + visibleVariables_.clear(); + // The query body and template are identical. Variables will always be visible + // - no need to check that. + parsedQuery_._clause = + parsedQuery::UpdateClause{GraphUpdate{{}, std::move(triples)}}; - return {std::move(triples), std::move(pattern)}; + return parsedQuery_; } // ____________________________________________________________________________________ ParsedQuery Visitor::visit(Parser::ModifyContext* ctx) { - if (ctx->iri()) { - reportNotSupported(ctx->iri(), "Named graphs are"); - } - if (!ctx->usingClause().empty()) { - reportNotSupported(ctx->usingClause(0), - "USING inside an DELETE or INSERT is"); - } - - parsedQuery_._rootGraphPattern = visit(ctx->groupGraphPattern()); - - parsedQuery_._clause = parsedQuery::UpdateClause(); - visitIf(&parsedQuery_.updateClause().toInsert_, ctx->insertClause()); - visitIf(&parsedQuery_.updateClause().toDelete_, ctx->deleteClause()); + auto isVisibleIfVariable = [this](const TripleComponent& component) { + if (component.isVariable()) { + return std::ranges::find(parsedQuery_.getVisibleVariables(), + component.getVariable()) != + parsedQuery_.getVisibleVariables().end(); + } else { + return true; + } + }; + auto checkTriples = + [&isVisibleIfVariable, + &ctx](const std::vector& triples) { + for (auto& triple : triples) { + if (!(isVisibleIfVariable(triple.s_) && + isVisibleIfVariable(triple.p_) && + isVisibleIfVariable(triple.o_))) { + reportError(ctx, + absl::StrCat("A triple contains a variable that was " + "not bound in the query body.")); + } + } + }; + AD_CORRECTNESS_CHECK(visibleVariables_.empty()); + auto graphPattern = visit(ctx->groupGraphPattern()); + parsedQuery_._rootGraphPattern = std::move(graphPattern); + parsedQuery_.registerVariablesVisibleInQueryBody(visibleVariables_); + visibleVariables_.clear(); + auto op = GraphUpdate{}; + visitIf(&op.toInsert_, ctx->insertClause()); + checkTriples(op.toInsert_); + visitIf(&op.toDelete_, ctx->deleteClause()); + checkTriples(op.toDelete_); + visitIf(&op.with_, ctx->iri()); + parsedQuery_._clause = parsedQuery::UpdateClause{op}; + parsedQuery_.datasetClauses_ = + parsedQuery::DatasetClauses::fromClauses(visitVector(ctx->usingClause())); return parsedQuery_; } // ____________________________________________________________________________________ -vector Visitor::visit(Parser::DeleteClauseContext* ctx) { +vector Visitor::visit( + Parser::DeleteClauseContext* ctx) { return visit(ctx->quadPattern()); } // ____________________________________________________________________________________ -vector Visitor::visit(Parser::InsertClauseContext* ctx) { +vector Visitor::visit( + Parser::InsertClauseContext* ctx) { return visit(ctx->quadPattern()); } @@ -552,12 +569,14 @@ GraphRefAll Visitor::visit(Parser::GraphRefAllContext* ctx) { } // ____________________________________________________________________________________ -vector Visitor::visit(Parser::QuadPatternContext* ctx) { +vector Visitor::visit( + Parser::QuadPatternContext* ctx) { return visit(ctx->quads()); } // ____________________________________________________________________________________ -vector Visitor::visit(Parser::QuadDataContext* ctx) { +vector Visitor::visit( + Parser::QuadDataContext* ctx) { auto quads = visit(ctx->quads()); auto checkAndReportVar = [&ctx](const TripleComponent& term) { if (term.isVariable()) { @@ -570,27 +589,63 @@ vector Visitor::visit(Parser::QuadDataContext* ctx) { checkAndReportVar(quad.s_); checkAndReportVar(quad.p_); checkAndReportVar(quad.o_); + if (std::holds_alternative(quad.g_)) { + reportError(ctx->quads(), "Variables are not allowed as graph names."); + } } return quads; } // ____________________________________________________________________________________ -vector Visitor::visit(Parser::QuadsContext* ctx) { - if (!ctx->quadsNotTriples().empty()) { - // Could also be default; disallow completely for now. - reportNotSupported(ctx->quadsNotTriples(0), "Named graphs are"); - } - - AD_CORRECTNESS_CHECK(ctx->triplesTemplate().size() == 1); - - auto convertTriple = - [](const std::array& triple) -> SparqlTripleSimple { +vector Visitor::transformTriplesTemplate( + Parser::TriplesTemplateContext* ctx, + const SparqlTripleSimpleWithGraph::Graph& graph) { + auto convertTriple = [&graph](const std::array& triple) + -> SparqlTripleSimpleWithGraph { return {visitGraphTerm(triple[0]), visitGraphTerm(triple[1]), - visitGraphTerm(triple[2])}; + visitGraphTerm(triple[2]), graph}; }; - return ad_utility::transform(visit(ctx->triplesTemplate(0)), convertTriple); + return ad_utility::transform(visit(ctx), convertTriple); +} + +// ____________________________________________________________________________________ +vector Visitor::visit(Parser::QuadsContext* ctx) { + // The ordering of the individual triplesTemplate and quadsNotTriples is not + // relevant and also not known. + auto triplesWithGraph = ad_utility::transform( + ctx->triplesTemplate(), [this](Parser::TriplesTemplateContext* ctx) { + return transformTriplesTemplate(ctx, std::monostate{}); + }); + std::ranges::move(visitVector(ctx->quadsNotTriples()), + std::back_inserter(triplesWithGraph)); + return ad_utility::flatten(std::move(triplesWithGraph)); +} + +// ____________________________________________________________________________________ +vector Visitor::visit( + Parser::QuadsNotTriplesContext* ctx) { + // Short circuit when the triples section is empty + if (!ctx->triplesTemplate()) { + return {}; + } + + auto graphTerm = visit(ctx->varOrIri()); + SparqlTripleSimpleWithGraph::Graph graph = graphTerm.visit( + [&ctx]( + const T& element) -> SparqlTripleSimpleWithGraph::Graph { + if constexpr (std::is_same_v || std::is_same_v) { + return element; + } else { + static_assert(std::is_same_v || + std::is_same_v); + reportError(ctx->varOrIri(), + "Only IRIs and variables are allowed as graph names."); + } + }); + + return transformTriplesTemplate(ctx->triplesTemplate(), graph); } // ____________________________________________________________________________________ @@ -959,6 +1014,15 @@ string Visitor::visit(Parser::PnameNsContext* ctx) { return prefixMap_[prefix]; } +// ____________________________________________________________________________________ +DatasetClause SparqlQleverVisitor::visit(Parser::UsingClauseContext* ctx) { + if (ctx->NAMED()) { + return {.dataset_ = visit(ctx->iri()), .isNamed_ = true}; + } else { + return {.dataset_ = visit(ctx->iri()), .isNamed_ = false}; + } +} + // ____________________________________________________________________________________ void Visitor::visit(Parser::PrologueContext* ctx) { visitVector(ctx->baseDecl()); diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.h b/src/parser/sparqlParser/SparqlQleverVisitor.h index a6eeea78d..180f916c3 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.h +++ b/src/parser/sparqlParser/SparqlQleverVisitor.h @@ -194,32 +194,31 @@ class SparqlQleverVisitor { ParsedQuery visit(Parser::Update1Context* ctx); - [[noreturn]] void visit(const Parser::LoadContext* ctx) const; + updateClause::Load visit(Parser::LoadContext* ctx); - ParsedQuery visit(Parser::ClearContext* ctx); + updateClause::Clear visit(Parser::ClearContext* ctx); - [[noreturn]] void visit(const Parser::DropContext* ctx) const; + updateClause::Drop visit(Parser::DropContext* ctx); - [[noreturn]] void visit(const Parser::CreateContext* ctx) const; + updateClause::Create visit(Parser::CreateContext* ctx); - [[noreturn]] void visit(const Parser::AddContext* ctx) const; + updateClause::Add visit(Parser::AddContext* ctx); - [[noreturn]] void visit(const Parser::MoveContext* ctx) const; + updateClause::Move visit(Parser::MoveContext* ctx); - [[noreturn]] void visit(const Parser::CopyContext* ctx) const; + updateClause::Copy visit(Parser::CopyContext* ctx); - vector visit(Parser::InsertDataContext* ctx); + updateClause::GraphUpdate visit(Parser::InsertDataContext* ctx); - vector visit(Parser::DeleteDataContext* ctx); + updateClause::GraphUpdate visit(Parser::DeleteDataContext* ctx); - std::pair, ParsedQuery::GraphPattern> visit( - Parser::DeleteWhereContext* ctx); + ParsedQuery visit(Parser::DeleteWhereContext* ctx); ParsedQuery visit(Parser::ModifyContext* ctx); - vector visit(Parser::DeleteClauseContext* ctx); + vector visit(Parser::DeleteClauseContext* ctx); - vector visit(Parser::InsertClauseContext* ctx); + vector visit(Parser::InsertClauseContext* ctx); GraphOrDefault visit(Parser::GraphOrDefaultContext* ctx); @@ -227,11 +226,19 @@ class SparqlQleverVisitor { GraphRefAll visit(Parser::GraphRefAllContext* ctx); - vector visit(Parser::QuadPatternContext* ctx); + vector visit(Parser::QuadPatternContext* ctx); - vector visit(Parser::QuadDataContext* ctx); + vector visit(Parser::QuadDataContext* ctx); - vector visit(Parser::QuadsContext* ctx); + // Parse the triples and set the graph for all of them. + vector transformTriplesTemplate( + Parser::TriplesTemplateContext* ctx, + const SparqlTripleSimpleWithGraph::Graph& graph); + + vector visit(Parser::QuadsContext* ctx); + + vector visit( + Parser::QuadsNotTriplesContext* ctx); Triples visit(Parser::TriplesTemplateContext* ctx); @@ -469,6 +476,8 @@ class SparqlQleverVisitor { string visit(Parser::PnameNsContext* ctx); + DatasetClause visit(Parser::UsingClauseContext* ctx); + private: // Helper to assign variable `startTime_` a correctly formatted time string. static std::string currentTimeAsXsdString(); diff --git a/src/parser/sparqlParser/generated/SparqlAutomatic.g4 b/src/parser/sparqlParser/generated/SparqlAutomatic.g4 index 29bcbac30..7199f36d2 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomatic.g4 +++ b/src/parser/sparqlParser/generated/SparqlAutomatic.g4 @@ -179,7 +179,7 @@ deleteClause: DELETE quadPattern ; insertClause: INSERT quadPattern ; -usingClause: USING (IRI | NAMED iri) ; +usingClause: USING (iri | NAMED iri) ; graphOrDefault: DEFAULT | GRAPH iri ; diff --git a/src/parser/sparqlParser/generated/SparqlAutomatic.interp b/src/parser/sparqlParser/generated/SparqlAutomatic.interp index 60fbf76a8..28fbd0d13 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomatic.interp +++ b/src/parser/sparqlParser/generated/SparqlAutomatic.interp @@ -515,4 +515,4 @@ pnameNs atn: -[4, 1, 175, 1647, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 1, 0, 1, 0, 3, 0, 317, 8, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 326, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 5, 2, 332, 8, 2, 10, 2, 12, 2, 335, 9, 2, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 5, 5, 346, 8, 5, 10, 5, 12, 5, 349, 9, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 3, 7, 361, 8, 7, 1, 7, 4, 7, 364, 8, 7, 11, 7, 12, 7, 365, 1, 7, 3, 7, 369, 8, 7, 1, 8, 1, 8, 3, 8, 373, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 5, 11, 386, 8, 11, 10, 11, 12, 11, 389, 9, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 395, 8, 11, 10, 11, 12, 11, 398, 9, 11, 1, 11, 1, 11, 1, 11, 3, 11, 403, 8, 11, 1, 11, 1, 11, 3, 11, 407, 8, 11, 1, 12, 1, 12, 4, 12, 411, 8, 12, 11, 12, 12, 12, 412, 1, 12, 3, 12, 416, 8, 12, 1, 12, 5, 12, 419, 8, 12, 10, 12, 12, 12, 422, 9, 12, 1, 12, 3, 12, 425, 8, 12, 1, 12, 1, 12, 1, 13, 1, 13, 5, 13, 431, 8, 13, 10, 13, 12, 13, 434, 9, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 3, 14, 442, 8, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 18, 3, 18, 452, 8, 18, 1, 18, 1, 18, 1, 19, 3, 19, 457, 8, 19, 1, 19, 3, 19, 460, 8, 19, 1, 19, 3, 19, 463, 8, 19, 1, 19, 3, 19, 466, 8, 19, 1, 20, 1, 20, 4, 20, 470, 8, 20, 11, 20, 12, 20, 471, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 480, 8, 21, 1, 21, 1, 21, 1, 21, 3, 21, 485, 8, 21, 1, 22, 1, 22, 4, 22, 489, 8, 22, 11, 22, 12, 22, 490, 1, 23, 1, 23, 1, 24, 1, 24, 3, 24, 497, 8, 24, 1, 24, 4, 24, 500, 8, 24, 11, 24, 12, 24, 501, 1, 25, 1, 25, 1, 25, 1, 25, 3, 25, 508, 8, 25, 3, 25, 510, 8, 25, 1, 26, 1, 26, 3, 26, 514, 8, 26, 1, 26, 3, 26, 517, 8, 26, 1, 26, 1, 26, 3, 26, 521, 8, 26, 1, 26, 3, 26, 524, 8, 26, 1, 26, 1, 26, 3, 26, 528, 8, 26, 1, 26, 3, 26, 531, 8, 26, 1, 26, 1, 26, 3, 26, 535, 8, 26, 1, 26, 3, 26, 538, 8, 26, 1, 26, 1, 26, 3, 26, 542, 8, 26, 1, 26, 3, 26, 545, 8, 26, 1, 26, 1, 26, 3, 26, 549, 8, 26, 1, 26, 3, 26, 552, 8, 26, 3, 26, 554, 8, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 3, 30, 567, 8, 30, 1, 31, 1, 31, 1, 31, 1, 31, 3, 31, 573, 8, 31, 3, 31, 575, 8, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 588, 8, 32, 1, 33, 1, 33, 3, 33, 592, 8, 33, 1, 33, 1, 33, 1, 33, 3, 33, 597, 8, 33, 1, 34, 1, 34, 3, 34, 601, 8, 34, 1, 34, 1, 34, 1, 35, 1, 35, 3, 35, 607, 8, 35, 1, 35, 1, 35, 1, 36, 1, 36, 3, 36, 613, 8, 36, 1, 36, 1, 36, 1, 37, 1, 37, 3, 37, 619, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 3, 38, 627, 8, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 3, 39, 635, 8, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 3, 43, 655, 8, 43, 1, 43, 1, 43, 3, 43, 659, 8, 43, 1, 43, 3, 43, 662, 8, 43, 1, 43, 5, 43, 665, 8, 43, 10, 43, 12, 43, 668, 9, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 3, 46, 683, 8, 46, 1, 47, 1, 47, 1, 47, 3, 47, 688, 8, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 3, 49, 697, 8, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 3, 52, 708, 8, 52, 1, 52, 1, 52, 3, 52, 712, 8, 52, 1, 52, 3, 52, 715, 8, 52, 5, 52, 717, 8, 52, 10, 52, 12, 52, 720, 9, 52, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 726, 8, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 3, 54, 733, 8, 54, 3, 54, 735, 8, 54, 1, 55, 1, 55, 1, 55, 3, 55, 740, 8, 55, 1, 55, 1, 55, 1, 56, 3, 56, 745, 8, 56, 1, 56, 5, 56, 748, 8, 56, 10, 56, 12, 56, 751, 9, 56, 1, 57, 1, 57, 3, 57, 755, 8, 57, 1, 57, 3, 57, 758, 8, 57, 1, 58, 1, 58, 1, 58, 3, 58, 763, 8, 58, 3, 58, 765, 8, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 775, 8, 59, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 3, 62, 786, 8, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 3, 65, 803, 8, 65, 1, 66, 1, 66, 1, 66, 5, 66, 808, 8, 66, 10, 66, 12, 66, 811, 9, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 5, 67, 818, 8, 67, 10, 67, 12, 67, 821, 9, 67, 1, 67, 3, 67, 824, 8, 67, 1, 67, 1, 67, 5, 67, 828, 8, 67, 10, 67, 12, 67, 831, 9, 67, 1, 67, 1, 67, 1, 68, 1, 68, 5, 68, 837, 8, 68, 10, 68, 12, 68, 840, 9, 68, 1, 68, 1, 68, 3, 68, 844, 8, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 3, 69, 851, 8, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 5, 71, 859, 8, 71, 10, 71, 12, 71, 862, 9, 71, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 3, 73, 870, 8, 73, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 3, 75, 878, 8, 75, 1, 75, 1, 75, 1, 75, 5, 75, 883, 8, 75, 10, 75, 12, 75, 886, 9, 75, 1, 75, 1, 75, 3, 75, 890, 8, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 5, 76, 897, 8, 76, 10, 76, 12, 76, 900, 9, 76, 1, 76, 1, 76, 3, 76, 904, 8, 76, 1, 77, 1, 77, 3, 77, 908, 8, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 3, 78, 915, 8, 78, 3, 78, 917, 8, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 3, 79, 925, 8, 79, 1, 80, 3, 80, 928, 8, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 3, 81, 936, 8, 81, 5, 81, 938, 8, 81, 10, 81, 12, 81, 941, 9, 81, 1, 82, 1, 82, 3, 82, 945, 8, 82, 1, 83, 1, 83, 1, 83, 5, 83, 950, 8, 83, 10, 83, 12, 83, 953, 9, 83, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 3, 85, 963, 8, 85, 1, 86, 3, 86, 966, 8, 86, 1, 87, 1, 87, 1, 87, 3, 87, 971, 8, 87, 5, 87, 973, 8, 87, 10, 87, 12, 87, 976, 9, 87, 1, 88, 1, 88, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 3, 92, 990, 8, 92, 1, 93, 1, 93, 1, 93, 5, 93, 995, 8, 93, 10, 93, 12, 93, 998, 9, 93, 1, 94, 1, 94, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 5, 96, 1007, 8, 96, 10, 96, 12, 96, 1010, 9, 96, 1, 97, 1, 97, 1, 97, 5, 97, 1015, 8, 97, 10, 97, 12, 97, 1018, 9, 97, 1, 98, 1, 98, 3, 98, 1022, 8, 98, 1, 99, 1, 99, 1, 99, 3, 99, 1027, 8, 99, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 3, 101, 1039, 8, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 5, 102, 1046, 8, 102, 10, 102, 12, 102, 1049, 9, 102, 3, 102, 1051, 8, 102, 1, 102, 3, 102, 1054, 8, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 103, 3, 103, 1061, 8, 103, 3, 103, 1063, 8, 103, 1, 104, 1, 104, 1, 105, 1, 105, 3, 105, 1069, 8, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 3, 107, 1077, 8, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 4, 109, 1085, 8, 109, 11, 109, 12, 109, 1086, 1, 109, 1, 109, 1, 110, 1, 110, 4, 110, 1093, 8, 110, 11, 110, 12, 110, 1094, 1, 110, 1, 110, 1, 111, 1, 111, 3, 111, 1101, 8, 111, 1, 112, 1, 112, 3, 112, 1105, 8, 112, 1, 113, 1, 113, 3, 113, 1109, 8, 113, 1, 114, 1, 114, 3, 114, 1113, 8, 114, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 3, 116, 1123, 8, 116, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 5, 118, 1130, 8, 118, 10, 118, 12, 118, 1133, 9, 118, 1, 119, 1, 119, 1, 119, 5, 119, 1138, 8, 119, 10, 119, 12, 119, 1141, 9, 119, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 3, 121, 1163, 8, 121, 1, 122, 1, 122, 1, 123, 1, 123, 5, 123, 1169, 8, 123, 10, 123, 12, 123, 1172, 9, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 3, 124, 1179, 8, 124, 1, 125, 1, 125, 1, 126, 1, 126, 1, 127, 1, 127, 3, 127, 1187, 8, 127, 1, 127, 5, 127, 1190, 8, 127, 10, 127, 12, 127, 1193, 9, 127, 1, 128, 1, 128, 5, 128, 1197, 8, 128, 10, 128, 12, 128, 1200, 9, 128, 1, 129, 1, 129, 3, 129, 1204, 8, 129, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 3, 132, 1219, 8, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 3, 133, 1228, 8, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 3, 135, 1274, 8, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 3, 135, 1488, 8, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 3, 136, 1497, 8, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 3, 138, 1513, 8, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 3, 139, 1526, 8, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 3, 142, 1540, 8, 142, 1, 142, 1, 142, 3, 142, 1544, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1550, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1558, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1566, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1574, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1582, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1590, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1597, 8, 142, 1, 142, 1, 142, 3, 142, 1601, 8, 142, 1, 143, 1, 143, 3, 143, 1605, 8, 143, 1, 144, 1, 144, 1, 144, 1, 144, 3, 144, 1611, 8, 144, 1, 145, 1, 145, 1, 145, 3, 145, 1616, 8, 145, 1, 146, 1, 146, 1, 147, 1, 147, 1, 148, 1, 148, 1, 149, 1, 149, 1, 150, 1, 150, 1, 151, 3, 151, 1629, 8, 151, 1, 151, 1, 151, 3, 151, 1633, 8, 151, 1, 152, 1, 152, 3, 152, 1637, 8, 152, 1, 153, 1, 153, 1, 154, 1, 154, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 0, 0, 157, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286, 288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310, 312, 0, 10, 1, 0, 33, 34, 1, 0, 47, 48, 2, 0, 1, 1, 13, 14, 1, 0, 145, 146, 1, 0, 149, 151, 1, 0, 152, 154, 1, 0, 155, 157, 1, 0, 28, 29, 1, 0, 159, 162, 2, 0, 144, 144, 165, 165, 1761, 0, 316, 1, 0, 0, 0, 2, 320, 1, 0, 0, 0, 4, 333, 1, 0, 0, 0, 6, 336, 1, 0, 0, 0, 8, 339, 1, 0, 0, 0, 10, 343, 1, 0, 0, 0, 12, 353, 1, 0, 0, 0, 14, 358, 1, 0, 0, 0, 16, 372, 1, 0, 0, 0, 18, 374, 1, 0, 0, 0, 20, 378, 1, 0, 0, 0, 22, 382, 1, 0, 0, 0, 24, 408, 1, 0, 0, 0, 26, 428, 1, 0, 0, 0, 28, 438, 1, 0, 0, 0, 30, 443, 1, 0, 0, 0, 32, 445, 1, 0, 0, 0, 34, 448, 1, 0, 0, 0, 36, 451, 1, 0, 0, 0, 38, 456, 1, 0, 0, 0, 40, 467, 1, 0, 0, 0, 42, 484, 1, 0, 0, 0, 44, 486, 1, 0, 0, 0, 46, 492, 1, 0, 0, 0, 48, 496, 1, 0, 0, 0, 50, 509, 1, 0, 0, 0, 52, 553, 1, 0, 0, 0, 54, 555, 1, 0, 0, 0, 56, 558, 1, 0, 0, 0, 58, 561, 1, 0, 0, 0, 60, 566, 1, 0, 0, 0, 62, 568, 1, 0, 0, 0, 64, 587, 1, 0, 0, 0, 66, 589, 1, 0, 0, 0, 68, 598, 1, 0, 0, 0, 70, 604, 1, 0, 0, 0, 72, 610, 1, 0, 0, 0, 74, 616, 1, 0, 0, 0, 76, 624, 1, 0, 0, 0, 78, 632, 1, 0, 0, 0, 80, 640, 1, 0, 0, 0, 82, 644, 1, 0, 0, 0, 84, 648, 1, 0, 0, 0, 86, 654, 1, 0, 0, 0, 88, 672, 1, 0, 0, 0, 90, 675, 1, 0, 0, 0, 92, 678, 1, 0, 0, 0, 94, 687, 1, 0, 0, 0, 96, 689, 1, 0, 0, 0, 98, 696, 1, 0, 0, 0, 100, 698, 1, 0, 0, 0, 102, 702, 1, 0, 0, 0, 104, 707, 1, 0, 0, 0, 106, 721, 1, 0, 0, 0, 108, 729, 1, 0, 0, 0, 110, 736, 1, 0, 0, 0, 112, 744, 1, 0, 0, 0, 114, 752, 1, 0, 0, 0, 116, 759, 1, 0, 0, 0, 118, 774, 1, 0, 0, 0, 120, 776, 1, 0, 0, 0, 122, 779, 1, 0, 0, 0, 124, 783, 1, 0, 0, 0, 126, 790, 1, 0, 0, 0, 128, 797, 1, 0, 0, 0, 130, 802, 1, 0, 0, 0, 132, 804, 1, 0, 0, 0, 134, 823, 1, 0, 0, 0, 136, 843, 1, 0, 0, 0, 138, 850, 1, 0, 0, 0, 140, 852, 1, 0, 0, 0, 142, 855, 1, 0, 0, 0, 144, 863, 1, 0, 0, 0, 146, 869, 1, 0, 0, 0, 148, 871, 1, 0, 0, 0, 150, 889, 1, 0, 0, 0, 152, 903, 1, 0, 0, 0, 154, 905, 1, 0, 0, 0, 156, 911, 1, 0, 0, 0, 158, 924, 1, 0, 0, 0, 160, 927, 1, 0, 0, 0, 162, 929, 1, 0, 0, 0, 164, 944, 1, 0, 0, 0, 166, 946, 1, 0, 0, 0, 168, 954, 1, 0, 0, 0, 170, 962, 1, 0, 0, 0, 172, 965, 1, 0, 0, 0, 174, 967, 1, 0, 0, 0, 176, 977, 1, 0, 0, 0, 178, 979, 1, 0, 0, 0, 180, 981, 1, 0, 0, 0, 182, 984, 1, 0, 0, 0, 184, 989, 1, 0, 0, 0, 186, 991, 1, 0, 0, 0, 188, 999, 1, 0, 0, 0, 190, 1001, 1, 0, 0, 0, 192, 1003, 1, 0, 0, 0, 194, 1011, 1, 0, 0, 0, 196, 1019, 1, 0, 0, 0, 198, 1026, 1, 0, 0, 0, 200, 1028, 1, 0, 0, 0, 202, 1038, 1, 0, 0, 0, 204, 1053, 1, 0, 0, 0, 206, 1062, 1, 0, 0, 0, 208, 1064, 1, 0, 0, 0, 210, 1068, 1, 0, 0, 0, 212, 1070, 1, 0, 0, 0, 214, 1076, 1, 0, 0, 0, 216, 1078, 1, 0, 0, 0, 218, 1082, 1, 0, 0, 0, 220, 1090, 1, 0, 0, 0, 222, 1100, 1, 0, 0, 0, 224, 1104, 1, 0, 0, 0, 226, 1108, 1, 0, 0, 0, 228, 1112, 1, 0, 0, 0, 230, 1114, 1, 0, 0, 0, 232, 1122, 1, 0, 0, 0, 234, 1124, 1, 0, 0, 0, 236, 1126, 1, 0, 0, 0, 238, 1134, 1, 0, 0, 0, 240, 1142, 1, 0, 0, 0, 242, 1144, 1, 0, 0, 0, 244, 1164, 1, 0, 0, 0, 246, 1166, 1, 0, 0, 0, 248, 1178, 1, 0, 0, 0, 250, 1180, 1, 0, 0, 0, 252, 1182, 1, 0, 0, 0, 254, 1186, 1, 0, 0, 0, 256, 1194, 1, 0, 0, 0, 258, 1203, 1, 0, 0, 0, 260, 1205, 1, 0, 0, 0, 262, 1208, 1, 0, 0, 0, 264, 1218, 1, 0, 0, 0, 266, 1227, 1, 0, 0, 0, 268, 1229, 1, 0, 0, 0, 270, 1487, 1, 0, 0, 0, 272, 1489, 1, 0, 0, 0, 274, 1500, 1, 0, 0, 0, 276, 1505, 1, 0, 0, 0, 278, 1516, 1, 0, 0, 0, 280, 1529, 1, 0, 0, 0, 282, 1532, 1, 0, 0, 0, 284, 1600, 1, 0, 0, 0, 286, 1602, 1, 0, 0, 0, 288, 1606, 1, 0, 0, 0, 290, 1615, 1, 0, 0, 0, 292, 1617, 1, 0, 0, 0, 294, 1619, 1, 0, 0, 0, 296, 1621, 1, 0, 0, 0, 298, 1623, 1, 0, 0, 0, 300, 1625, 1, 0, 0, 0, 302, 1628, 1, 0, 0, 0, 304, 1636, 1, 0, 0, 0, 306, 1638, 1, 0, 0, 0, 308, 1640, 1, 0, 0, 0, 310, 1642, 1, 0, 0, 0, 312, 1644, 1, 0, 0, 0, 314, 317, 3, 2, 1, 0, 315, 317, 3, 62, 31, 0, 316, 314, 1, 0, 0, 0, 316, 315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 319, 5, 0, 0, 1, 319, 1, 1, 0, 0, 0, 320, 325, 3, 4, 2, 0, 321, 326, 3, 10, 5, 0, 322, 326, 3, 22, 11, 0, 323, 326, 3, 24, 12, 0, 324, 326, 3, 26, 13, 0, 325, 321, 1, 0, 0, 0, 325, 322, 1, 0, 0, 0, 325, 323, 1, 0, 0, 0, 325, 324, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 328, 3, 60, 30, 0, 328, 3, 1, 0, 0, 0, 329, 332, 3, 6, 3, 0, 330, 332, 3, 8, 4, 0, 331, 329, 1, 0, 0, 0, 331, 330, 1, 0, 0, 0, 332, 335, 1, 0, 0, 0, 333, 331, 1, 0, 0, 0, 333, 334, 1, 0, 0, 0, 334, 5, 1, 0, 0, 0, 335, 333, 1, 0, 0, 0, 336, 337, 5, 30, 0, 0, 337, 338, 3, 308, 154, 0, 338, 7, 1, 0, 0, 0, 339, 340, 5, 31, 0, 0, 340, 341, 5, 142, 0, 0, 341, 342, 3, 308, 154, 0, 342, 9, 1, 0, 0, 0, 343, 347, 3, 14, 7, 0, 344, 346, 3, 28, 14, 0, 345, 344, 1, 0, 0, 0, 346, 349, 1, 0, 0, 0, 347, 345, 1, 0, 0, 0, 347, 348, 1, 0, 0, 0, 348, 350, 1, 0, 0, 0, 349, 347, 1, 0, 0, 0, 350, 351, 3, 36, 18, 0, 351, 352, 3, 38, 19, 0, 352, 11, 1, 0, 0, 0, 353, 354, 3, 14, 7, 0, 354, 355, 3, 36, 18, 0, 355, 356, 3, 38, 19, 0, 356, 357, 3, 60, 30, 0, 357, 13, 1, 0, 0, 0, 358, 360, 5, 32, 0, 0, 359, 361, 7, 0, 0, 0, 360, 359, 1, 0, 0, 0, 360, 361, 1, 0, 0, 0, 361, 368, 1, 0, 0, 0, 362, 364, 3, 16, 8, 0, 363, 362, 1, 0, 0, 0, 364, 365, 1, 0, 0, 0, 365, 363, 1, 0, 0, 0, 365, 366, 1, 0, 0, 0, 366, 369, 1, 0, 0, 0, 367, 369, 5, 1, 0, 0, 368, 363, 1, 0, 0, 0, 368, 367, 1, 0, 0, 0, 369, 15, 1, 0, 0, 0, 370, 373, 3, 230, 115, 0, 371, 373, 3, 18, 9, 0, 372, 370, 1, 0, 0, 0, 372, 371, 1, 0, 0, 0, 373, 17, 1, 0, 0, 0, 374, 375, 5, 2, 0, 0, 375, 376, 3, 20, 10, 0, 376, 377, 5, 3, 0, 0, 377, 19, 1, 0, 0, 0, 378, 379, 3, 234, 117, 0, 379, 380, 5, 35, 0, 0, 380, 381, 3, 230, 115, 0, 381, 21, 1, 0, 0, 0, 382, 406, 5, 36, 0, 0, 383, 387, 3, 154, 77, 0, 384, 386, 3, 28, 14, 0, 385, 384, 1, 0, 0, 0, 386, 389, 1, 0, 0, 0, 387, 385, 1, 0, 0, 0, 387, 388, 1, 0, 0, 0, 388, 390, 1, 0, 0, 0, 389, 387, 1, 0, 0, 0, 390, 391, 3, 36, 18, 0, 391, 392, 3, 38, 19, 0, 392, 407, 1, 0, 0, 0, 393, 395, 3, 28, 14, 0, 394, 393, 1, 0, 0, 0, 395, 398, 1, 0, 0, 0, 396, 394, 1, 0, 0, 0, 396, 397, 1, 0, 0, 0, 397, 399, 1, 0, 0, 0, 398, 396, 1, 0, 0, 0, 399, 400, 5, 37, 0, 0, 400, 402, 5, 4, 0, 0, 401, 403, 3, 108, 54, 0, 402, 401, 1, 0, 0, 0, 402, 403, 1, 0, 0, 0, 403, 404, 1, 0, 0, 0, 404, 405, 5, 5, 0, 0, 405, 407, 3, 38, 19, 0, 406, 383, 1, 0, 0, 0, 406, 396, 1, 0, 0, 0, 407, 23, 1, 0, 0, 0, 408, 415, 5, 38, 0, 0, 409, 411, 3, 228, 114, 0, 410, 409, 1, 0, 0, 0, 411, 412, 1, 0, 0, 0, 412, 410, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 416, 1, 0, 0, 0, 414, 416, 5, 1, 0, 0, 415, 410, 1, 0, 0, 0, 415, 414, 1, 0, 0, 0, 416, 420, 1, 0, 0, 0, 417, 419, 3, 28, 14, 0, 418, 417, 1, 0, 0, 0, 419, 422, 1, 0, 0, 0, 420, 418, 1, 0, 0, 0, 420, 421, 1, 0, 0, 0, 421, 424, 1, 0, 0, 0, 422, 420, 1, 0, 0, 0, 423, 425, 3, 36, 18, 0, 424, 423, 1, 0, 0, 0, 424, 425, 1, 0, 0, 0, 425, 426, 1, 0, 0, 0, 426, 427, 3, 38, 19, 0, 427, 25, 1, 0, 0, 0, 428, 432, 5, 39, 0, 0, 429, 431, 3, 28, 14, 0, 430, 429, 1, 0, 0, 0, 431, 434, 1, 0, 0, 0, 432, 430, 1, 0, 0, 0, 432, 433, 1, 0, 0, 0, 433, 435, 1, 0, 0, 0, 434, 432, 1, 0, 0, 0, 435, 436, 3, 36, 18, 0, 436, 437, 3, 38, 19, 0, 437, 27, 1, 0, 0, 0, 438, 441, 5, 40, 0, 0, 439, 442, 3, 30, 15, 0, 440, 442, 3, 32, 16, 0, 441, 439, 1, 0, 0, 0, 441, 440, 1, 0, 0, 0, 442, 29, 1, 0, 0, 0, 443, 444, 3, 34, 17, 0, 444, 31, 1, 0, 0, 0, 445, 446, 5, 41, 0, 0, 446, 447, 3, 34, 17, 0, 447, 33, 1, 0, 0, 0, 448, 449, 3, 302, 151, 0, 449, 35, 1, 0, 0, 0, 450, 452, 5, 37, 0, 0, 451, 450, 1, 0, 0, 0, 451, 452, 1, 0, 0, 0, 452, 453, 1, 0, 0, 0, 453, 454, 3, 110, 55, 0, 454, 37, 1, 0, 0, 0, 455, 457, 3, 40, 20, 0, 456, 455, 1, 0, 0, 0, 456, 457, 1, 0, 0, 0, 457, 459, 1, 0, 0, 0, 458, 460, 3, 44, 22, 0, 459, 458, 1, 0, 0, 0, 459, 460, 1, 0, 0, 0, 460, 462, 1, 0, 0, 0, 461, 463, 3, 48, 24, 0, 462, 461, 1, 0, 0, 0, 462, 463, 1, 0, 0, 0, 463, 465, 1, 0, 0, 0, 464, 466, 3, 52, 26, 0, 465, 464, 1, 0, 0, 0, 465, 466, 1, 0, 0, 0, 466, 39, 1, 0, 0, 0, 467, 469, 5, 42, 0, 0, 468, 470, 3, 42, 21, 0, 469, 468, 1, 0, 0, 0, 470, 471, 1, 0, 0, 0, 471, 469, 1, 0, 0, 0, 471, 472, 1, 0, 0, 0, 472, 41, 1, 0, 0, 0, 473, 485, 3, 270, 135, 0, 474, 485, 3, 148, 74, 0, 475, 476, 5, 2, 0, 0, 476, 479, 3, 234, 117, 0, 477, 478, 5, 35, 0, 0, 478, 480, 3, 230, 115, 0, 479, 477, 1, 0, 0, 0, 479, 480, 1, 0, 0, 0, 480, 481, 1, 0, 0, 0, 481, 482, 5, 3, 0, 0, 482, 485, 1, 0, 0, 0, 483, 485, 3, 230, 115, 0, 484, 473, 1, 0, 0, 0, 484, 474, 1, 0, 0, 0, 484, 475, 1, 0, 0, 0, 484, 483, 1, 0, 0, 0, 485, 43, 1, 0, 0, 0, 486, 488, 5, 44, 0, 0, 487, 489, 3, 46, 23, 0, 488, 487, 1, 0, 0, 0, 489, 490, 1, 0, 0, 0, 490, 488, 1, 0, 0, 0, 490, 491, 1, 0, 0, 0, 491, 45, 1, 0, 0, 0, 492, 493, 3, 146, 73, 0, 493, 47, 1, 0, 0, 0, 494, 497, 5, 45, 0, 0, 495, 497, 5, 46, 0, 0, 496, 494, 1, 0, 0, 0, 496, 495, 1, 0, 0, 0, 497, 499, 1, 0, 0, 0, 498, 500, 3, 50, 25, 0, 499, 498, 1, 0, 0, 0, 500, 501, 1, 0, 0, 0, 501, 499, 1, 0, 0, 0, 501, 502, 1, 0, 0, 0, 502, 49, 1, 0, 0, 0, 503, 504, 7, 1, 0, 0, 504, 510, 3, 268, 134, 0, 505, 508, 3, 146, 73, 0, 506, 508, 3, 230, 115, 0, 507, 505, 1, 0, 0, 0, 507, 506, 1, 0, 0, 0, 508, 510, 1, 0, 0, 0, 509, 503, 1, 0, 0, 0, 509, 507, 1, 0, 0, 0, 510, 51, 1, 0, 0, 0, 511, 513, 3, 54, 27, 0, 512, 514, 3, 56, 28, 0, 513, 512, 1, 0, 0, 0, 513, 514, 1, 0, 0, 0, 514, 516, 1, 0, 0, 0, 515, 517, 3, 58, 29, 0, 516, 515, 1, 0, 0, 0, 516, 517, 1, 0, 0, 0, 517, 554, 1, 0, 0, 0, 518, 520, 3, 54, 27, 0, 519, 521, 3, 58, 29, 0, 520, 519, 1, 0, 0, 0, 520, 521, 1, 0, 0, 0, 521, 523, 1, 0, 0, 0, 522, 524, 3, 56, 28, 0, 523, 522, 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 554, 1, 0, 0, 0, 525, 527, 3, 56, 28, 0, 526, 528, 3, 54, 27, 0, 527, 526, 1, 0, 0, 0, 527, 528, 1, 0, 0, 0, 528, 530, 1, 0, 0, 0, 529, 531, 3, 58, 29, 0, 530, 529, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 554, 1, 0, 0, 0, 532, 534, 3, 56, 28, 0, 533, 535, 3, 58, 29, 0, 534, 533, 1, 0, 0, 0, 534, 535, 1, 0, 0, 0, 535, 537, 1, 0, 0, 0, 536, 538, 3, 54, 27, 0, 537, 536, 1, 0, 0, 0, 537, 538, 1, 0, 0, 0, 538, 554, 1, 0, 0, 0, 539, 541, 3, 58, 29, 0, 540, 542, 3, 56, 28, 0, 541, 540, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 544, 1, 0, 0, 0, 543, 545, 3, 54, 27, 0, 544, 543, 1, 0, 0, 0, 544, 545, 1, 0, 0, 0, 545, 554, 1, 0, 0, 0, 546, 548, 3, 58, 29, 0, 547, 549, 3, 54, 27, 0, 548, 547, 1, 0, 0, 0, 548, 549, 1, 0, 0, 0, 549, 551, 1, 0, 0, 0, 550, 552, 3, 56, 28, 0, 551, 550, 1, 0, 0, 0, 551, 552, 1, 0, 0, 0, 552, 554, 1, 0, 0, 0, 553, 511, 1, 0, 0, 0, 553, 518, 1, 0, 0, 0, 553, 525, 1, 0, 0, 0, 553, 532, 1, 0, 0, 0, 553, 539, 1, 0, 0, 0, 553, 546, 1, 0, 0, 0, 554, 53, 1, 0, 0, 0, 555, 556, 5, 49, 0, 0, 556, 557, 3, 208, 104, 0, 557, 55, 1, 0, 0, 0, 558, 559, 5, 50, 0, 0, 559, 560, 3, 208, 104, 0, 560, 57, 1, 0, 0, 0, 561, 562, 5, 51, 0, 0, 562, 563, 3, 208, 104, 0, 563, 59, 1, 0, 0, 0, 564, 565, 5, 52, 0, 0, 565, 567, 3, 130, 65, 0, 566, 564, 1, 0, 0, 0, 566, 567, 1, 0, 0, 0, 567, 61, 1, 0, 0, 0, 568, 574, 3, 4, 2, 0, 569, 572, 3, 64, 32, 0, 570, 571, 5, 6, 0, 0, 571, 573, 3, 62, 31, 0, 572, 570, 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 575, 1, 0, 0, 0, 574, 569, 1, 0, 0, 0, 574, 575, 1, 0, 0, 0, 575, 63, 1, 0, 0, 0, 576, 588, 3, 66, 33, 0, 577, 588, 3, 68, 34, 0, 578, 588, 3, 70, 35, 0, 579, 588, 3, 74, 37, 0, 580, 588, 3, 76, 38, 0, 581, 588, 3, 78, 39, 0, 582, 588, 3, 72, 36, 0, 583, 588, 3, 80, 40, 0, 584, 588, 3, 82, 41, 0, 585, 588, 3, 84, 42, 0, 586, 588, 3, 86, 43, 0, 587, 576, 1, 0, 0, 0, 587, 577, 1, 0, 0, 0, 587, 578, 1, 0, 0, 0, 587, 579, 1, 0, 0, 0, 587, 580, 1, 0, 0, 0, 587, 581, 1, 0, 0, 0, 587, 582, 1, 0, 0, 0, 587, 583, 1, 0, 0, 0, 587, 584, 1, 0, 0, 0, 587, 585, 1, 0, 0, 0, 587, 586, 1, 0, 0, 0, 588, 65, 1, 0, 0, 0, 589, 591, 5, 53, 0, 0, 590, 592, 5, 54, 0, 0, 591, 590, 1, 0, 0, 0, 591, 592, 1, 0, 0, 0, 592, 593, 1, 0, 0, 0, 593, 596, 3, 302, 151, 0, 594, 595, 5, 55, 0, 0, 595, 597, 3, 96, 48, 0, 596, 594, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 67, 1, 0, 0, 0, 598, 600, 5, 56, 0, 0, 599, 601, 5, 54, 0, 0, 600, 599, 1, 0, 0, 0, 600, 601, 1, 0, 0, 0, 601, 602, 1, 0, 0, 0, 602, 603, 3, 98, 49, 0, 603, 69, 1, 0, 0, 0, 604, 606, 5, 57, 0, 0, 605, 607, 5, 54, 0, 0, 606, 605, 1, 0, 0, 0, 606, 607, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 609, 3, 98, 49, 0, 609, 71, 1, 0, 0, 0, 610, 612, 5, 58, 0, 0, 611, 613, 5, 54, 0, 0, 612, 611, 1, 0, 0, 0, 612, 613, 1, 0, 0, 0, 613, 614, 1, 0, 0, 0, 614, 615, 3, 96, 48, 0, 615, 73, 1, 0, 0, 0, 616, 618, 5, 59, 0, 0, 617, 619, 5, 54, 0, 0, 618, 617, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 621, 3, 94, 47, 0, 621, 622, 5, 60, 0, 0, 622, 623, 3, 94, 47, 0, 623, 75, 1, 0, 0, 0, 624, 626, 5, 62, 0, 0, 625, 627, 5, 54, 0, 0, 626, 625, 1, 0, 0, 0, 626, 627, 1, 0, 0, 0, 627, 628, 1, 0, 0, 0, 628, 629, 3, 94, 47, 0, 629, 630, 5, 60, 0, 0, 630, 631, 3, 94, 47, 0, 631, 77, 1, 0, 0, 0, 632, 634, 5, 63, 0, 0, 633, 635, 5, 54, 0, 0, 634, 633, 1, 0, 0, 0, 634, 635, 1, 0, 0, 0, 635, 636, 1, 0, 0, 0, 636, 637, 3, 94, 47, 0, 637, 638, 5, 60, 0, 0, 638, 639, 3, 94, 47, 0, 639, 79, 1, 0, 0, 0, 640, 641, 5, 64, 0, 0, 641, 642, 5, 61, 0, 0, 642, 643, 3, 102, 51, 0, 643, 81, 1, 0, 0, 0, 644, 645, 5, 65, 0, 0, 645, 646, 5, 61, 0, 0, 646, 647, 3, 102, 51, 0, 647, 83, 1, 0, 0, 0, 648, 649, 5, 65, 0, 0, 649, 650, 5, 37, 0, 0, 650, 651, 3, 100, 50, 0, 651, 85, 1, 0, 0, 0, 652, 653, 5, 66, 0, 0, 653, 655, 3, 302, 151, 0, 654, 652, 1, 0, 0, 0, 654, 655, 1, 0, 0, 0, 655, 661, 1, 0, 0, 0, 656, 658, 3, 88, 44, 0, 657, 659, 3, 90, 45, 0, 658, 657, 1, 0, 0, 0, 658, 659, 1, 0, 0, 0, 659, 662, 1, 0, 0, 0, 660, 662, 3, 90, 45, 0, 661, 656, 1, 0, 0, 0, 661, 660, 1, 0, 0, 0, 662, 666, 1, 0, 0, 0, 663, 665, 3, 92, 46, 0, 664, 663, 1, 0, 0, 0, 665, 668, 1, 0, 0, 0, 666, 664, 1, 0, 0, 0, 666, 667, 1, 0, 0, 0, 667, 669, 1, 0, 0, 0, 668, 666, 1, 0, 0, 0, 669, 670, 5, 37, 0, 0, 670, 671, 3, 110, 55, 0, 671, 87, 1, 0, 0, 0, 672, 673, 5, 65, 0, 0, 673, 674, 3, 100, 50, 0, 674, 89, 1, 0, 0, 0, 675, 676, 5, 64, 0, 0, 676, 677, 3, 100, 50, 0, 677, 91, 1, 0, 0, 0, 678, 682, 5, 67, 0, 0, 679, 683, 5, 85, 0, 0, 680, 681, 5, 41, 0, 0, 681, 683, 3, 302, 151, 0, 682, 679, 1, 0, 0, 0, 682, 680, 1, 0, 0, 0, 683, 93, 1, 0, 0, 0, 684, 688, 5, 68, 0, 0, 685, 686, 5, 69, 0, 0, 686, 688, 3, 302, 151, 0, 687, 684, 1, 0, 0, 0, 687, 685, 1, 0, 0, 0, 688, 95, 1, 0, 0, 0, 689, 690, 5, 69, 0, 0, 690, 691, 3, 302, 151, 0, 691, 97, 1, 0, 0, 0, 692, 697, 3, 96, 48, 0, 693, 697, 5, 68, 0, 0, 694, 697, 5, 41, 0, 0, 695, 697, 5, 70, 0, 0, 696, 692, 1, 0, 0, 0, 696, 693, 1, 0, 0, 0, 696, 694, 1, 0, 0, 0, 696, 695, 1, 0, 0, 0, 697, 99, 1, 0, 0, 0, 698, 699, 5, 4, 0, 0, 699, 700, 3, 104, 52, 0, 700, 701, 5, 5, 0, 0, 701, 101, 1, 0, 0, 0, 702, 703, 5, 4, 0, 0, 703, 704, 3, 104, 52, 0, 704, 705, 5, 5, 0, 0, 705, 103, 1, 0, 0, 0, 706, 708, 3, 108, 54, 0, 707, 706, 1, 0, 0, 0, 707, 708, 1, 0, 0, 0, 708, 718, 1, 0, 0, 0, 709, 711, 3, 106, 53, 0, 710, 712, 5, 7, 0, 0, 711, 710, 1, 0, 0, 0, 711, 712, 1, 0, 0, 0, 712, 714, 1, 0, 0, 0, 713, 715, 3, 108, 54, 0, 714, 713, 1, 0, 0, 0, 714, 715, 1, 0, 0, 0, 715, 717, 1, 0, 0, 0, 716, 709, 1, 0, 0, 0, 717, 720, 1, 0, 0, 0, 718, 716, 1, 0, 0, 0, 718, 719, 1, 0, 0, 0, 719, 105, 1, 0, 0, 0, 720, 718, 1, 0, 0, 0, 721, 722, 5, 69, 0, 0, 722, 723, 3, 228, 114, 0, 723, 725, 5, 4, 0, 0, 724, 726, 3, 108, 54, 0, 725, 724, 1, 0, 0, 0, 725, 726, 1, 0, 0, 0, 726, 727, 1, 0, 0, 0, 727, 728, 5, 5, 0, 0, 728, 107, 1, 0, 0, 0, 729, 734, 3, 158, 79, 0, 730, 732, 5, 7, 0, 0, 731, 733, 3, 108, 54, 0, 732, 731, 1, 0, 0, 0, 732, 733, 1, 0, 0, 0, 733, 735, 1, 0, 0, 0, 734, 730, 1, 0, 0, 0, 734, 735, 1, 0, 0, 0, 735, 109, 1, 0, 0, 0, 736, 739, 5, 4, 0, 0, 737, 740, 3, 12, 6, 0, 738, 740, 3, 112, 56, 0, 739, 737, 1, 0, 0, 0, 739, 738, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 742, 5, 5, 0, 0, 742, 111, 1, 0, 0, 0, 743, 745, 3, 116, 58, 0, 744, 743, 1, 0, 0, 0, 744, 745, 1, 0, 0, 0, 745, 749, 1, 0, 0, 0, 746, 748, 3, 114, 57, 0, 747, 746, 1, 0, 0, 0, 748, 751, 1, 0, 0, 0, 749, 747, 1, 0, 0, 0, 749, 750, 1, 0, 0, 0, 750, 113, 1, 0, 0, 0, 751, 749, 1, 0, 0, 0, 752, 754, 3, 118, 59, 0, 753, 755, 5, 7, 0, 0, 754, 753, 1, 0, 0, 0, 754, 755, 1, 0, 0, 0, 755, 757, 1, 0, 0, 0, 756, 758, 3, 116, 58, 0, 757, 756, 1, 0, 0, 0, 757, 758, 1, 0, 0, 0, 758, 115, 1, 0, 0, 0, 759, 764, 3, 170, 85, 0, 760, 762, 5, 7, 0, 0, 761, 763, 3, 116, 58, 0, 762, 761, 1, 0, 0, 0, 762, 763, 1, 0, 0, 0, 763, 765, 1, 0, 0, 0, 764, 760, 1, 0, 0, 0, 764, 765, 1, 0, 0, 0, 765, 117, 1, 0, 0, 0, 766, 775, 3, 142, 71, 0, 767, 775, 3, 120, 60, 0, 768, 775, 3, 140, 70, 0, 769, 775, 3, 122, 61, 0, 770, 775, 3, 124, 62, 0, 771, 775, 3, 144, 72, 0, 772, 775, 3, 126, 63, 0, 773, 775, 3, 128, 64, 0, 774, 766, 1, 0, 0, 0, 774, 767, 1, 0, 0, 0, 774, 768, 1, 0, 0, 0, 774, 769, 1, 0, 0, 0, 774, 770, 1, 0, 0, 0, 774, 771, 1, 0, 0, 0, 774, 772, 1, 0, 0, 0, 774, 773, 1, 0, 0, 0, 775, 119, 1, 0, 0, 0, 776, 777, 5, 71, 0, 0, 777, 778, 3, 110, 55, 0, 778, 121, 1, 0, 0, 0, 779, 780, 5, 69, 0, 0, 780, 781, 3, 228, 114, 0, 781, 782, 3, 110, 55, 0, 782, 123, 1, 0, 0, 0, 783, 785, 5, 72, 0, 0, 784, 786, 5, 54, 0, 0, 785, 784, 1, 0, 0, 0, 785, 786, 1, 0, 0, 0, 786, 787, 1, 0, 0, 0, 787, 788, 3, 228, 114, 0, 788, 789, 3, 110, 55, 0, 789, 125, 1, 0, 0, 0, 790, 791, 5, 73, 0, 0, 791, 792, 5, 2, 0, 0, 792, 793, 3, 234, 117, 0, 793, 794, 5, 35, 0, 0, 794, 795, 3, 230, 115, 0, 795, 796, 5, 3, 0, 0, 796, 127, 1, 0, 0, 0, 797, 798, 5, 52, 0, 0, 798, 799, 3, 130, 65, 0, 799, 129, 1, 0, 0, 0, 800, 803, 3, 132, 66, 0, 801, 803, 3, 134, 67, 0, 802, 800, 1, 0, 0, 0, 802, 801, 1, 0, 0, 0, 803, 131, 1, 0, 0, 0, 804, 805, 3, 230, 115, 0, 805, 809, 5, 4, 0, 0, 806, 808, 3, 138, 69, 0, 807, 806, 1, 0, 0, 0, 808, 811, 1, 0, 0, 0, 809, 807, 1, 0, 0, 0, 809, 810, 1, 0, 0, 0, 810, 812, 1, 0, 0, 0, 811, 809, 1, 0, 0, 0, 812, 813, 5, 5, 0, 0, 813, 133, 1, 0, 0, 0, 814, 824, 5, 164, 0, 0, 815, 819, 5, 2, 0, 0, 816, 818, 3, 230, 115, 0, 817, 816, 1, 0, 0, 0, 818, 821, 1, 0, 0, 0, 819, 817, 1, 0, 0, 0, 819, 820, 1, 0, 0, 0, 820, 822, 1, 0, 0, 0, 821, 819, 1, 0, 0, 0, 822, 824, 5, 3, 0, 0, 823, 814, 1, 0, 0, 0, 823, 815, 1, 0, 0, 0, 824, 825, 1, 0, 0, 0, 825, 829, 5, 4, 0, 0, 826, 828, 3, 136, 68, 0, 827, 826, 1, 0, 0, 0, 828, 831, 1, 0, 0, 0, 829, 827, 1, 0, 0, 0, 829, 830, 1, 0, 0, 0, 830, 832, 1, 0, 0, 0, 831, 829, 1, 0, 0, 0, 832, 833, 5, 5, 0, 0, 833, 135, 1, 0, 0, 0, 834, 838, 5, 2, 0, 0, 835, 837, 3, 138, 69, 0, 836, 835, 1, 0, 0, 0, 837, 840, 1, 0, 0, 0, 838, 836, 1, 0, 0, 0, 838, 839, 1, 0, 0, 0, 839, 841, 1, 0, 0, 0, 840, 838, 1, 0, 0, 0, 841, 844, 5, 3, 0, 0, 842, 844, 5, 164, 0, 0, 843, 834, 1, 0, 0, 0, 843, 842, 1, 0, 0, 0, 844, 137, 1, 0, 0, 0, 845, 851, 3, 302, 151, 0, 846, 851, 3, 288, 144, 0, 847, 851, 3, 290, 145, 0, 848, 851, 3, 298, 149, 0, 849, 851, 5, 74, 0, 0, 850, 845, 1, 0, 0, 0, 850, 846, 1, 0, 0, 0, 850, 847, 1, 0, 0, 0, 850, 848, 1, 0, 0, 0, 850, 849, 1, 0, 0, 0, 851, 139, 1, 0, 0, 0, 852, 853, 5, 75, 0, 0, 853, 854, 3, 110, 55, 0, 854, 141, 1, 0, 0, 0, 855, 860, 3, 110, 55, 0, 856, 857, 5, 76, 0, 0, 857, 859, 3, 110, 55, 0, 858, 856, 1, 0, 0, 0, 859, 862, 1, 0, 0, 0, 860, 858, 1, 0, 0, 0, 860, 861, 1, 0, 0, 0, 861, 143, 1, 0, 0, 0, 862, 860, 1, 0, 0, 0, 863, 864, 5, 77, 0, 0, 864, 865, 3, 146, 73, 0, 865, 145, 1, 0, 0, 0, 866, 870, 3, 268, 134, 0, 867, 870, 3, 270, 135, 0, 868, 870, 3, 148, 74, 0, 869, 866, 1, 0, 0, 0, 869, 867, 1, 0, 0, 0, 869, 868, 1, 0, 0, 0, 870, 147, 1, 0, 0, 0, 871, 872, 3, 302, 151, 0, 872, 873, 3, 150, 75, 0, 873, 149, 1, 0, 0, 0, 874, 890, 5, 164, 0, 0, 875, 877, 5, 2, 0, 0, 876, 878, 5, 33, 0, 0, 877, 876, 1, 0, 0, 0, 877, 878, 1, 0, 0, 0, 878, 879, 1, 0, 0, 0, 879, 884, 3, 234, 117, 0, 880, 881, 5, 8, 0, 0, 881, 883, 3, 234, 117, 0, 882, 880, 1, 0, 0, 0, 883, 886, 1, 0, 0, 0, 884, 882, 1, 0, 0, 0, 884, 885, 1, 0, 0, 0, 885, 887, 1, 0, 0, 0, 886, 884, 1, 0, 0, 0, 887, 888, 5, 3, 0, 0, 888, 890, 1, 0, 0, 0, 889, 874, 1, 0, 0, 0, 889, 875, 1, 0, 0, 0, 890, 151, 1, 0, 0, 0, 891, 904, 5, 164, 0, 0, 892, 893, 5, 2, 0, 0, 893, 898, 3, 234, 117, 0, 894, 895, 5, 8, 0, 0, 895, 897, 3, 234, 117, 0, 896, 894, 1, 0, 0, 0, 897, 900, 1, 0, 0, 0, 898, 896, 1, 0, 0, 0, 898, 899, 1, 0, 0, 0, 899, 901, 1, 0, 0, 0, 900, 898, 1, 0, 0, 0, 901, 902, 5, 3, 0, 0, 902, 904, 1, 0, 0, 0, 903, 891, 1, 0, 0, 0, 903, 892, 1, 0, 0, 0, 904, 153, 1, 0, 0, 0, 905, 907, 5, 4, 0, 0, 906, 908, 3, 156, 78, 0, 907, 906, 1, 0, 0, 0, 907, 908, 1, 0, 0, 0, 908, 909, 1, 0, 0, 0, 909, 910, 5, 5, 0, 0, 910, 155, 1, 0, 0, 0, 911, 916, 3, 158, 79, 0, 912, 914, 5, 7, 0, 0, 913, 915, 3, 156, 78, 0, 914, 913, 1, 0, 0, 0, 914, 915, 1, 0, 0, 0, 915, 917, 1, 0, 0, 0, 916, 912, 1, 0, 0, 0, 916, 917, 1, 0, 0, 0, 917, 157, 1, 0, 0, 0, 918, 919, 3, 226, 113, 0, 919, 920, 3, 162, 81, 0, 920, 925, 1, 0, 0, 0, 921, 922, 3, 210, 105, 0, 922, 923, 3, 160, 80, 0, 923, 925, 1, 0, 0, 0, 924, 918, 1, 0, 0, 0, 924, 921, 1, 0, 0, 0, 925, 159, 1, 0, 0, 0, 926, 928, 3, 162, 81, 0, 927, 926, 1, 0, 0, 0, 927, 928, 1, 0, 0, 0, 928, 161, 1, 0, 0, 0, 929, 930, 3, 164, 82, 0, 930, 939, 3, 166, 83, 0, 931, 935, 5, 6, 0, 0, 932, 933, 3, 164, 82, 0, 933, 934, 3, 166, 83, 0, 934, 936, 1, 0, 0, 0, 935, 932, 1, 0, 0, 0, 935, 936, 1, 0, 0, 0, 936, 938, 1, 0, 0, 0, 937, 931, 1, 0, 0, 0, 938, 941, 1, 0, 0, 0, 939, 937, 1, 0, 0, 0, 939, 940, 1, 0, 0, 0, 940, 163, 1, 0, 0, 0, 941, 939, 1, 0, 0, 0, 942, 945, 3, 228, 114, 0, 943, 945, 5, 9, 0, 0, 944, 942, 1, 0, 0, 0, 944, 943, 1, 0, 0, 0, 945, 165, 1, 0, 0, 0, 946, 951, 3, 168, 84, 0, 947, 948, 5, 8, 0, 0, 948, 950, 3, 168, 84, 0, 949, 947, 1, 0, 0, 0, 950, 953, 1, 0, 0, 0, 951, 949, 1, 0, 0, 0, 951, 952, 1, 0, 0, 0, 952, 167, 1, 0, 0, 0, 953, 951, 1, 0, 0, 0, 954, 955, 3, 222, 111, 0, 955, 169, 1, 0, 0, 0, 956, 957, 3, 226, 113, 0, 957, 958, 3, 174, 87, 0, 958, 963, 1, 0, 0, 0, 959, 960, 3, 214, 107, 0, 960, 961, 3, 172, 86, 0, 961, 963, 1, 0, 0, 0, 962, 956, 1, 0, 0, 0, 962, 959, 1, 0, 0, 0, 963, 171, 1, 0, 0, 0, 964, 966, 3, 174, 87, 0, 965, 964, 1, 0, 0, 0, 965, 966, 1, 0, 0, 0, 966, 173, 1, 0, 0, 0, 967, 974, 3, 182, 91, 0, 968, 970, 5, 6, 0, 0, 969, 971, 3, 180, 90, 0, 970, 969, 1, 0, 0, 0, 970, 971, 1, 0, 0, 0, 971, 973, 1, 0, 0, 0, 972, 968, 1, 0, 0, 0, 973, 976, 1, 0, 0, 0, 974, 972, 1, 0, 0, 0, 974, 975, 1, 0, 0, 0, 975, 175, 1, 0, 0, 0, 976, 974, 1, 0, 0, 0, 977, 978, 3, 190, 95, 0, 978, 177, 1, 0, 0, 0, 979, 980, 3, 230, 115, 0, 980, 179, 1, 0, 0, 0, 981, 982, 3, 184, 92, 0, 982, 983, 3, 166, 83, 0, 983, 181, 1, 0, 0, 0, 984, 985, 3, 184, 92, 0, 985, 986, 3, 186, 93, 0, 986, 183, 1, 0, 0, 0, 987, 990, 3, 176, 88, 0, 988, 990, 3, 178, 89, 0, 989, 987, 1, 0, 0, 0, 989, 988, 1, 0, 0, 0, 990, 185, 1, 0, 0, 0, 991, 996, 3, 188, 94, 0, 992, 993, 5, 8, 0, 0, 993, 995, 3, 188, 94, 0, 994, 992, 1, 0, 0, 0, 995, 998, 1, 0, 0, 0, 996, 994, 1, 0, 0, 0, 996, 997, 1, 0, 0, 0, 997, 187, 1, 0, 0, 0, 998, 996, 1, 0, 0, 0, 999, 1000, 3, 224, 112, 0, 1000, 189, 1, 0, 0, 0, 1001, 1002, 3, 192, 96, 0, 1002, 191, 1, 0, 0, 0, 1003, 1008, 3, 194, 97, 0, 1004, 1005, 5, 10, 0, 0, 1005, 1007, 3, 194, 97, 0, 1006, 1004, 1, 0, 0, 0, 1007, 1010, 1, 0, 0, 0, 1008, 1006, 1, 0, 0, 0, 1008, 1009, 1, 0, 0, 0, 1009, 193, 1, 0, 0, 0, 1010, 1008, 1, 0, 0, 0, 1011, 1016, 3, 198, 99, 0, 1012, 1013, 5, 11, 0, 0, 1013, 1015, 3, 198, 99, 0, 1014, 1012, 1, 0, 0, 0, 1015, 1018, 1, 0, 0, 0, 1016, 1014, 1, 0, 0, 0, 1016, 1017, 1, 0, 0, 0, 1017, 195, 1, 0, 0, 0, 1018, 1016, 1, 0, 0, 0, 1019, 1021, 3, 202, 101, 0, 1020, 1022, 3, 200, 100, 0, 1021, 1020, 1, 0, 0, 0, 1021, 1022, 1, 0, 0, 0, 1022, 197, 1, 0, 0, 0, 1023, 1027, 3, 196, 98, 0, 1024, 1025, 5, 12, 0, 0, 1025, 1027, 3, 196, 98, 0, 1026, 1023, 1, 0, 0, 0, 1026, 1024, 1, 0, 0, 0, 1027, 199, 1, 0, 0, 0, 1028, 1029, 7, 2, 0, 0, 1029, 201, 1, 0, 0, 0, 1030, 1039, 3, 302, 151, 0, 1031, 1039, 5, 9, 0, 0, 1032, 1033, 5, 15, 0, 0, 1033, 1039, 3, 204, 102, 0, 1034, 1035, 5, 2, 0, 0, 1035, 1036, 3, 190, 95, 0, 1036, 1037, 5, 3, 0, 0, 1037, 1039, 1, 0, 0, 0, 1038, 1030, 1, 0, 0, 0, 1038, 1031, 1, 0, 0, 0, 1038, 1032, 1, 0, 0, 0, 1038, 1034, 1, 0, 0, 0, 1039, 203, 1, 0, 0, 0, 1040, 1054, 3, 206, 103, 0, 1041, 1050, 5, 2, 0, 0, 1042, 1047, 3, 206, 103, 0, 1043, 1044, 5, 10, 0, 0, 1044, 1046, 3, 206, 103, 0, 1045, 1043, 1, 0, 0, 0, 1046, 1049, 1, 0, 0, 0, 1047, 1045, 1, 0, 0, 0, 1047, 1048, 1, 0, 0, 0, 1048, 1051, 1, 0, 0, 0, 1049, 1047, 1, 0, 0, 0, 1050, 1042, 1, 0, 0, 0, 1050, 1051, 1, 0, 0, 0, 1051, 1052, 1, 0, 0, 0, 1052, 1054, 5, 3, 0, 0, 1053, 1040, 1, 0, 0, 0, 1053, 1041, 1, 0, 0, 0, 1054, 205, 1, 0, 0, 0, 1055, 1063, 3, 302, 151, 0, 1056, 1063, 5, 9, 0, 0, 1057, 1060, 5, 12, 0, 0, 1058, 1061, 3, 302, 151, 0, 1059, 1061, 5, 9, 0, 0, 1060, 1058, 1, 0, 0, 0, 1060, 1059, 1, 0, 0, 0, 1061, 1063, 1, 0, 0, 0, 1062, 1055, 1, 0, 0, 0, 1062, 1056, 1, 0, 0, 0, 1062, 1057, 1, 0, 0, 0, 1063, 207, 1, 0, 0, 0, 1064, 1065, 5, 149, 0, 0, 1065, 209, 1, 0, 0, 0, 1066, 1069, 3, 218, 109, 0, 1067, 1069, 3, 212, 106, 0, 1068, 1066, 1, 0, 0, 0, 1068, 1067, 1, 0, 0, 0, 1069, 211, 1, 0, 0, 0, 1070, 1071, 5, 16, 0, 0, 1071, 1072, 3, 162, 81, 0, 1072, 1073, 5, 17, 0, 0, 1073, 213, 1, 0, 0, 0, 1074, 1077, 3, 220, 110, 0, 1075, 1077, 3, 216, 108, 0, 1076, 1074, 1, 0, 0, 0, 1076, 1075, 1, 0, 0, 0, 1077, 215, 1, 0, 0, 0, 1078, 1079, 5, 16, 0, 0, 1079, 1080, 3, 174, 87, 0, 1080, 1081, 5, 17, 0, 0, 1081, 217, 1, 0, 0, 0, 1082, 1084, 5, 2, 0, 0, 1083, 1085, 3, 222, 111, 0, 1084, 1083, 1, 0, 0, 0, 1085, 1086, 1, 0, 0, 0, 1086, 1084, 1, 0, 0, 0, 1086, 1087, 1, 0, 0, 0, 1087, 1088, 1, 0, 0, 0, 1088, 1089, 5, 3, 0, 0, 1089, 219, 1, 0, 0, 0, 1090, 1092, 5, 2, 0, 0, 1091, 1093, 3, 224, 112, 0, 1092, 1091, 1, 0, 0, 0, 1093, 1094, 1, 0, 0, 0, 1094, 1092, 1, 0, 0, 0, 1094, 1095, 1, 0, 0, 0, 1095, 1096, 1, 0, 0, 0, 1096, 1097, 5, 3, 0, 0, 1097, 221, 1, 0, 0, 0, 1098, 1101, 3, 226, 113, 0, 1099, 1101, 3, 210, 105, 0, 1100, 1098, 1, 0, 0, 0, 1100, 1099, 1, 0, 0, 0, 1101, 223, 1, 0, 0, 0, 1102, 1105, 3, 226, 113, 0, 1103, 1105, 3, 214, 107, 0, 1104, 1102, 1, 0, 0, 0, 1104, 1103, 1, 0, 0, 0, 1105, 225, 1, 0, 0, 0, 1106, 1109, 3, 230, 115, 0, 1107, 1109, 3, 232, 116, 0, 1108, 1106, 1, 0, 0, 0, 1108, 1107, 1, 0, 0, 0, 1109, 227, 1, 0, 0, 0, 1110, 1113, 3, 230, 115, 0, 1111, 1113, 3, 302, 151, 0, 1112, 1110, 1, 0, 0, 0, 1112, 1111, 1, 0, 0, 0, 1113, 229, 1, 0, 0, 0, 1114, 1115, 7, 3, 0, 0, 1115, 231, 1, 0, 0, 0, 1116, 1123, 3, 302, 151, 0, 1117, 1123, 3, 288, 144, 0, 1118, 1123, 3, 290, 145, 0, 1119, 1123, 3, 298, 149, 0, 1120, 1123, 3, 306, 153, 0, 1121, 1123, 5, 164, 0, 0, 1122, 1116, 1, 0, 0, 0, 1122, 1117, 1, 0, 0, 0, 1122, 1118, 1, 0, 0, 0, 1122, 1119, 1, 0, 0, 0, 1122, 1120, 1, 0, 0, 0, 1122, 1121, 1, 0, 0, 0, 1123, 233, 1, 0, 0, 0, 1124, 1125, 3, 236, 118, 0, 1125, 235, 1, 0, 0, 0, 1126, 1131, 3, 238, 119, 0, 1127, 1128, 5, 18, 0, 0, 1128, 1130, 3, 238, 119, 0, 1129, 1127, 1, 0, 0, 0, 1130, 1133, 1, 0, 0, 0, 1131, 1129, 1, 0, 0, 0, 1131, 1132, 1, 0, 0, 0, 1132, 237, 1, 0, 0, 0, 1133, 1131, 1, 0, 0, 0, 1134, 1139, 3, 240, 120, 0, 1135, 1136, 5, 19, 0, 0, 1136, 1138, 3, 240, 120, 0, 1137, 1135, 1, 0, 0, 0, 1138, 1141, 1, 0, 0, 0, 1139, 1137, 1, 0, 0, 0, 1139, 1140, 1, 0, 0, 0, 1140, 239, 1, 0, 0, 0, 1141, 1139, 1, 0, 0, 0, 1142, 1143, 3, 242, 121, 0, 1143, 241, 1, 0, 0, 0, 1144, 1162, 3, 244, 122, 0, 1145, 1146, 5, 20, 0, 0, 1146, 1163, 3, 244, 122, 0, 1147, 1148, 5, 21, 0, 0, 1148, 1163, 3, 244, 122, 0, 1149, 1150, 5, 22, 0, 0, 1150, 1163, 3, 244, 122, 0, 1151, 1152, 5, 23, 0, 0, 1152, 1163, 3, 244, 122, 0, 1153, 1154, 5, 24, 0, 0, 1154, 1163, 3, 244, 122, 0, 1155, 1156, 5, 25, 0, 0, 1156, 1163, 3, 244, 122, 0, 1157, 1158, 5, 79, 0, 0, 1158, 1163, 3, 152, 76, 0, 1159, 1160, 5, 78, 0, 0, 1160, 1161, 5, 79, 0, 0, 1161, 1163, 3, 152, 76, 0, 1162, 1145, 1, 0, 0, 0, 1162, 1147, 1, 0, 0, 0, 1162, 1149, 1, 0, 0, 0, 1162, 1151, 1, 0, 0, 0, 1162, 1153, 1, 0, 0, 0, 1162, 1155, 1, 0, 0, 0, 1162, 1157, 1, 0, 0, 0, 1162, 1159, 1, 0, 0, 0, 1162, 1163, 1, 0, 0, 0, 1163, 243, 1, 0, 0, 0, 1164, 1165, 3, 246, 123, 0, 1165, 245, 1, 0, 0, 0, 1166, 1170, 3, 256, 128, 0, 1167, 1169, 3, 248, 124, 0, 1168, 1167, 1, 0, 0, 0, 1169, 1172, 1, 0, 0, 0, 1170, 1168, 1, 0, 0, 0, 1170, 1171, 1, 0, 0, 0, 1171, 247, 1, 0, 0, 0, 1172, 1170, 1, 0, 0, 0, 1173, 1174, 5, 13, 0, 0, 1174, 1179, 3, 250, 125, 0, 1175, 1176, 5, 26, 0, 0, 1176, 1179, 3, 252, 126, 0, 1177, 1179, 3, 254, 127, 0, 1178, 1173, 1, 0, 0, 0, 1178, 1175, 1, 0, 0, 0, 1178, 1177, 1, 0, 0, 0, 1179, 249, 1, 0, 0, 0, 1180, 1181, 3, 256, 128, 0, 1181, 251, 1, 0, 0, 0, 1182, 1183, 3, 256, 128, 0, 1183, 253, 1, 0, 0, 0, 1184, 1187, 3, 294, 147, 0, 1185, 1187, 3, 296, 148, 0, 1186, 1184, 1, 0, 0, 0, 1186, 1185, 1, 0, 0, 0, 1187, 1191, 1, 0, 0, 0, 1188, 1190, 3, 258, 129, 0, 1189, 1188, 1, 0, 0, 0, 1190, 1193, 1, 0, 0, 0, 1191, 1189, 1, 0, 0, 0, 1191, 1192, 1, 0, 0, 0, 1192, 255, 1, 0, 0, 0, 1193, 1191, 1, 0, 0, 0, 1194, 1198, 3, 264, 132, 0, 1195, 1197, 3, 258, 129, 0, 1196, 1195, 1, 0, 0, 0, 1197, 1200, 1, 0, 0, 0, 1198, 1196, 1, 0, 0, 0, 1198, 1199, 1, 0, 0, 0, 1199, 257, 1, 0, 0, 0, 1200, 1198, 1, 0, 0, 0, 1201, 1204, 3, 260, 130, 0, 1202, 1204, 3, 262, 131, 0, 1203, 1201, 1, 0, 0, 0, 1203, 1202, 1, 0, 0, 0, 1204, 259, 1, 0, 0, 0, 1205, 1206, 5, 1, 0, 0, 1206, 1207, 3, 264, 132, 0, 1207, 261, 1, 0, 0, 0, 1208, 1209, 5, 11, 0, 0, 1209, 1210, 3, 264, 132, 0, 1210, 263, 1, 0, 0, 0, 1211, 1212, 5, 15, 0, 0, 1212, 1219, 3, 266, 133, 0, 1213, 1214, 5, 13, 0, 0, 1214, 1219, 3, 266, 133, 0, 1215, 1216, 5, 26, 0, 0, 1216, 1219, 3, 266, 133, 0, 1217, 1219, 3, 266, 133, 0, 1218, 1211, 1, 0, 0, 0, 1218, 1213, 1, 0, 0, 0, 1218, 1215, 1, 0, 0, 0, 1218, 1217, 1, 0, 0, 0, 1219, 265, 1, 0, 0, 0, 1220, 1228, 3, 268, 134, 0, 1221, 1228, 3, 270, 135, 0, 1222, 1228, 3, 286, 143, 0, 1223, 1228, 3, 288, 144, 0, 1224, 1228, 3, 290, 145, 0, 1225, 1228, 3, 298, 149, 0, 1226, 1228, 3, 230, 115, 0, 1227, 1220, 1, 0, 0, 0, 1227, 1221, 1, 0, 0, 0, 1227, 1222, 1, 0, 0, 0, 1227, 1223, 1, 0, 0, 0, 1227, 1224, 1, 0, 0, 0, 1227, 1225, 1, 0, 0, 0, 1227, 1226, 1, 0, 0, 0, 1228, 267, 1, 0, 0, 0, 1229, 1230, 5, 2, 0, 0, 1230, 1231, 3, 234, 117, 0, 1231, 1232, 5, 3, 0, 0, 1232, 269, 1, 0, 0, 0, 1233, 1488, 3, 284, 142, 0, 1234, 1235, 5, 80, 0, 0, 1235, 1236, 5, 2, 0, 0, 1236, 1237, 3, 234, 117, 0, 1237, 1238, 5, 3, 0, 0, 1238, 1488, 1, 0, 0, 0, 1239, 1488, 3, 274, 137, 0, 1240, 1241, 5, 82, 0, 0, 1241, 1242, 5, 2, 0, 0, 1242, 1243, 3, 234, 117, 0, 1243, 1244, 5, 8, 0, 0, 1244, 1245, 3, 234, 117, 0, 1245, 1246, 5, 3, 0, 0, 1246, 1488, 1, 0, 0, 0, 1247, 1248, 5, 83, 0, 0, 1248, 1249, 5, 2, 0, 0, 1249, 1250, 3, 234, 117, 0, 1250, 1251, 5, 3, 0, 0, 1251, 1488, 1, 0, 0, 0, 1252, 1253, 5, 84, 0, 0, 1253, 1254, 5, 2, 0, 0, 1254, 1255, 3, 230, 115, 0, 1255, 1256, 5, 3, 0, 0, 1256, 1488, 1, 0, 0, 0, 1257, 1258, 5, 85, 0, 0, 1258, 1259, 5, 2, 0, 0, 1259, 1260, 3, 234, 117, 0, 1260, 1261, 5, 3, 0, 0, 1261, 1488, 1, 0, 0, 0, 1262, 1263, 5, 86, 0, 0, 1263, 1264, 5, 2, 0, 0, 1264, 1265, 3, 234, 117, 0, 1265, 1266, 5, 3, 0, 0, 1266, 1488, 1, 0, 0, 0, 1267, 1273, 5, 87, 0, 0, 1268, 1269, 5, 2, 0, 0, 1269, 1270, 3, 234, 117, 0, 1270, 1271, 5, 3, 0, 0, 1271, 1274, 1, 0, 0, 0, 1272, 1274, 5, 164, 0, 0, 1273, 1268, 1, 0, 0, 0, 1273, 1272, 1, 0, 0, 0, 1274, 1488, 1, 0, 0, 0, 1275, 1276, 5, 88, 0, 0, 1276, 1488, 5, 164, 0, 0, 1277, 1278, 5, 89, 0, 0, 1278, 1279, 5, 2, 0, 0, 1279, 1280, 3, 234, 117, 0, 1280, 1281, 5, 3, 0, 0, 1281, 1488, 1, 0, 0, 0, 1282, 1283, 5, 90, 0, 0, 1283, 1284, 5, 2, 0, 0, 1284, 1285, 3, 234, 117, 0, 1285, 1286, 5, 3, 0, 0, 1286, 1488, 1, 0, 0, 0, 1287, 1288, 5, 91, 0, 0, 1288, 1289, 5, 2, 0, 0, 1289, 1290, 3, 234, 117, 0, 1290, 1291, 5, 3, 0, 0, 1291, 1488, 1, 0, 0, 0, 1292, 1293, 5, 92, 0, 0, 1293, 1294, 5, 2, 0, 0, 1294, 1295, 3, 234, 117, 0, 1295, 1296, 5, 3, 0, 0, 1296, 1488, 1, 0, 0, 0, 1297, 1298, 5, 93, 0, 0, 1298, 1488, 3, 152, 76, 0, 1299, 1488, 3, 276, 138, 0, 1300, 1301, 5, 94, 0, 0, 1301, 1302, 5, 2, 0, 0, 1302, 1303, 3, 234, 117, 0, 1303, 1304, 5, 3, 0, 0, 1304, 1488, 1, 0, 0, 0, 1305, 1488, 3, 278, 139, 0, 1306, 1307, 5, 95, 0, 0, 1307, 1308, 5, 2, 0, 0, 1308, 1309, 3, 234, 117, 0, 1309, 1310, 5, 3, 0, 0, 1310, 1488, 1, 0, 0, 0, 1311, 1312, 5, 96, 0, 0, 1312, 1313, 5, 2, 0, 0, 1313, 1314, 3, 234, 117, 0, 1314, 1315, 5, 3, 0, 0, 1315, 1488, 1, 0, 0, 0, 1316, 1317, 5, 97, 0, 0, 1317, 1318, 5, 2, 0, 0, 1318, 1319, 3, 234, 117, 0, 1319, 1320, 5, 3, 0, 0, 1320, 1488, 1, 0, 0, 0, 1321, 1322, 5, 99, 0, 0, 1322, 1323, 5, 2, 0, 0, 1323, 1324, 3, 234, 117, 0, 1324, 1325, 5, 8, 0, 0, 1325, 1326, 3, 234, 117, 0, 1326, 1327, 5, 3, 0, 0, 1327, 1488, 1, 0, 0, 0, 1328, 1329, 5, 100, 0, 0, 1329, 1330, 5, 2, 0, 0, 1330, 1331, 3, 234, 117, 0, 1331, 1332, 5, 8, 0, 0, 1332, 1333, 3, 234, 117, 0, 1333, 1334, 5, 3, 0, 0, 1334, 1488, 1, 0, 0, 0, 1335, 1336, 5, 101, 0, 0, 1336, 1337, 5, 2, 0, 0, 1337, 1338, 3, 234, 117, 0, 1338, 1339, 5, 8, 0, 0, 1339, 1340, 3, 234, 117, 0, 1340, 1341, 5, 3, 0, 0, 1341, 1488, 1, 0, 0, 0, 1342, 1343, 5, 102, 0, 0, 1343, 1344, 5, 2, 0, 0, 1344, 1345, 3, 234, 117, 0, 1345, 1346, 5, 8, 0, 0, 1346, 1347, 3, 234, 117, 0, 1347, 1348, 5, 3, 0, 0, 1348, 1488, 1, 0, 0, 0, 1349, 1350, 5, 103, 0, 0, 1350, 1351, 5, 2, 0, 0, 1351, 1352, 3, 234, 117, 0, 1352, 1353, 5, 8, 0, 0, 1353, 1354, 3, 234, 117, 0, 1354, 1355, 5, 3, 0, 0, 1355, 1488, 1, 0, 0, 0, 1356, 1357, 5, 104, 0, 0, 1357, 1358, 5, 2, 0, 0, 1358, 1359, 3, 234, 117, 0, 1359, 1360, 5, 3, 0, 0, 1360, 1488, 1, 0, 0, 0, 1361, 1362, 5, 105, 0, 0, 1362, 1363, 5, 2, 0, 0, 1363, 1364, 3, 234, 117, 0, 1364, 1365, 5, 3, 0, 0, 1365, 1488, 1, 0, 0, 0, 1366, 1367, 5, 106, 0, 0, 1367, 1368, 5, 2, 0, 0, 1368, 1369, 3, 234, 117, 0, 1369, 1370, 5, 3, 0, 0, 1370, 1488, 1, 0, 0, 0, 1371, 1372, 5, 107, 0, 0, 1372, 1373, 5, 2, 0, 0, 1373, 1374, 3, 234, 117, 0, 1374, 1375, 5, 3, 0, 0, 1375, 1488, 1, 0, 0, 0, 1376, 1377, 5, 108, 0, 0, 1377, 1378, 5, 2, 0, 0, 1378, 1379, 3, 234, 117, 0, 1379, 1380, 5, 3, 0, 0, 1380, 1488, 1, 0, 0, 0, 1381, 1382, 5, 109, 0, 0, 1382, 1383, 5, 2, 0, 0, 1383, 1384, 3, 234, 117, 0, 1384, 1385, 5, 3, 0, 0, 1385, 1488, 1, 0, 0, 0, 1386, 1387, 5, 110, 0, 0, 1387, 1388, 5, 2, 0, 0, 1388, 1389, 3, 234, 117, 0, 1389, 1390, 5, 3, 0, 0, 1390, 1488, 1, 0, 0, 0, 1391, 1392, 5, 111, 0, 0, 1392, 1393, 5, 2, 0, 0, 1393, 1394, 3, 234, 117, 0, 1394, 1395, 5, 3, 0, 0, 1395, 1488, 1, 0, 0, 0, 1396, 1397, 5, 112, 0, 0, 1397, 1488, 5, 164, 0, 0, 1398, 1399, 5, 113, 0, 0, 1399, 1488, 5, 164, 0, 0, 1400, 1401, 5, 114, 0, 0, 1401, 1488, 5, 164, 0, 0, 1402, 1403, 5, 119, 0, 0, 1403, 1404, 5, 2, 0, 0, 1404, 1405, 3, 234, 117, 0, 1405, 1406, 5, 3, 0, 0, 1406, 1488, 1, 0, 0, 0, 1407, 1408, 5, 115, 0, 0, 1408, 1409, 5, 2, 0, 0, 1409, 1410, 3, 234, 117, 0, 1410, 1411, 5, 3, 0, 0, 1411, 1488, 1, 0, 0, 0, 1412, 1413, 5, 116, 0, 0, 1413, 1414, 5, 2, 0, 0, 1414, 1415, 3, 234, 117, 0, 1415, 1416, 5, 3, 0, 0, 1416, 1488, 1, 0, 0, 0, 1417, 1418, 5, 117, 0, 0, 1418, 1419, 5, 2, 0, 0, 1419, 1420, 3, 234, 117, 0, 1420, 1421, 5, 3, 0, 0, 1421, 1488, 1, 0, 0, 0, 1422, 1423, 5, 118, 0, 0, 1423, 1424, 5, 2, 0, 0, 1424, 1425, 3, 234, 117, 0, 1425, 1426, 5, 3, 0, 0, 1426, 1488, 1, 0, 0, 0, 1427, 1428, 5, 120, 0, 0, 1428, 1488, 3, 152, 76, 0, 1429, 1430, 5, 121, 0, 0, 1430, 1431, 5, 2, 0, 0, 1431, 1432, 3, 234, 117, 0, 1432, 1433, 5, 8, 0, 0, 1433, 1434, 3, 234, 117, 0, 1434, 1435, 5, 8, 0, 0, 1435, 1436, 3, 234, 117, 0, 1436, 1437, 5, 3, 0, 0, 1437, 1488, 1, 0, 0, 0, 1438, 1439, 5, 122, 0, 0, 1439, 1440, 5, 2, 0, 0, 1440, 1441, 3, 234, 117, 0, 1441, 1442, 5, 8, 0, 0, 1442, 1443, 3, 234, 117, 0, 1443, 1444, 5, 3, 0, 0, 1444, 1488, 1, 0, 0, 0, 1445, 1446, 5, 123, 0, 0, 1446, 1447, 5, 2, 0, 0, 1447, 1448, 3, 234, 117, 0, 1448, 1449, 5, 8, 0, 0, 1449, 1450, 3, 234, 117, 0, 1450, 1451, 5, 3, 0, 0, 1451, 1488, 1, 0, 0, 0, 1452, 1453, 5, 124, 0, 0, 1453, 1454, 5, 2, 0, 0, 1454, 1455, 3, 234, 117, 0, 1455, 1456, 5, 8, 0, 0, 1456, 1457, 3, 234, 117, 0, 1457, 1458, 5, 3, 0, 0, 1458, 1488, 1, 0, 0, 0, 1459, 1460, 5, 125, 0, 0, 1460, 1461, 5, 2, 0, 0, 1461, 1462, 3, 234, 117, 0, 1462, 1463, 5, 3, 0, 0, 1463, 1488, 1, 0, 0, 0, 1464, 1465, 5, 126, 0, 0, 1465, 1466, 5, 2, 0, 0, 1466, 1467, 3, 234, 117, 0, 1467, 1468, 5, 3, 0, 0, 1468, 1488, 1, 0, 0, 0, 1469, 1470, 5, 127, 0, 0, 1470, 1471, 5, 2, 0, 0, 1471, 1472, 3, 234, 117, 0, 1472, 1473, 5, 3, 0, 0, 1473, 1488, 1, 0, 0, 0, 1474, 1475, 5, 128, 0, 0, 1475, 1476, 5, 2, 0, 0, 1476, 1477, 3, 234, 117, 0, 1477, 1478, 5, 3, 0, 0, 1478, 1488, 1, 0, 0, 0, 1479, 1480, 5, 129, 0, 0, 1480, 1481, 5, 2, 0, 0, 1481, 1482, 3, 234, 117, 0, 1482, 1483, 5, 3, 0, 0, 1483, 1488, 1, 0, 0, 0, 1484, 1488, 3, 272, 136, 0, 1485, 1488, 3, 280, 140, 0, 1486, 1488, 3, 282, 141, 0, 1487, 1233, 1, 0, 0, 0, 1487, 1234, 1, 0, 0, 0, 1487, 1239, 1, 0, 0, 0, 1487, 1240, 1, 0, 0, 0, 1487, 1247, 1, 0, 0, 0, 1487, 1252, 1, 0, 0, 0, 1487, 1257, 1, 0, 0, 0, 1487, 1262, 1, 0, 0, 0, 1487, 1267, 1, 0, 0, 0, 1487, 1275, 1, 0, 0, 0, 1487, 1277, 1, 0, 0, 0, 1487, 1282, 1, 0, 0, 0, 1487, 1287, 1, 0, 0, 0, 1487, 1292, 1, 0, 0, 0, 1487, 1297, 1, 0, 0, 0, 1487, 1299, 1, 0, 0, 0, 1487, 1300, 1, 0, 0, 0, 1487, 1305, 1, 0, 0, 0, 1487, 1306, 1, 0, 0, 0, 1487, 1311, 1, 0, 0, 0, 1487, 1316, 1, 0, 0, 0, 1487, 1321, 1, 0, 0, 0, 1487, 1328, 1, 0, 0, 0, 1487, 1335, 1, 0, 0, 0, 1487, 1342, 1, 0, 0, 0, 1487, 1349, 1, 0, 0, 0, 1487, 1356, 1, 0, 0, 0, 1487, 1361, 1, 0, 0, 0, 1487, 1366, 1, 0, 0, 0, 1487, 1371, 1, 0, 0, 0, 1487, 1376, 1, 0, 0, 0, 1487, 1381, 1, 0, 0, 0, 1487, 1386, 1, 0, 0, 0, 1487, 1391, 1, 0, 0, 0, 1487, 1396, 1, 0, 0, 0, 1487, 1398, 1, 0, 0, 0, 1487, 1400, 1, 0, 0, 0, 1487, 1402, 1, 0, 0, 0, 1487, 1407, 1, 0, 0, 0, 1487, 1412, 1, 0, 0, 0, 1487, 1417, 1, 0, 0, 0, 1487, 1422, 1, 0, 0, 0, 1487, 1427, 1, 0, 0, 0, 1487, 1429, 1, 0, 0, 0, 1487, 1438, 1, 0, 0, 0, 1487, 1445, 1, 0, 0, 0, 1487, 1452, 1, 0, 0, 0, 1487, 1459, 1, 0, 0, 0, 1487, 1464, 1, 0, 0, 0, 1487, 1469, 1, 0, 0, 0, 1487, 1474, 1, 0, 0, 0, 1487, 1479, 1, 0, 0, 0, 1487, 1484, 1, 0, 0, 0, 1487, 1485, 1, 0, 0, 0, 1487, 1486, 1, 0, 0, 0, 1488, 271, 1, 0, 0, 0, 1489, 1490, 5, 130, 0, 0, 1490, 1491, 5, 2, 0, 0, 1491, 1492, 3, 234, 117, 0, 1492, 1493, 5, 8, 0, 0, 1493, 1496, 3, 234, 117, 0, 1494, 1495, 5, 8, 0, 0, 1495, 1497, 3, 234, 117, 0, 1496, 1494, 1, 0, 0, 0, 1496, 1497, 1, 0, 0, 0, 1497, 1498, 1, 0, 0, 0, 1498, 1499, 5, 3, 0, 0, 1499, 273, 1, 0, 0, 0, 1500, 1501, 5, 81, 0, 0, 1501, 1502, 5, 2, 0, 0, 1502, 1503, 3, 234, 117, 0, 1503, 1504, 5, 3, 0, 0, 1504, 275, 1, 0, 0, 0, 1505, 1506, 5, 131, 0, 0, 1506, 1507, 5, 2, 0, 0, 1507, 1508, 3, 234, 117, 0, 1508, 1509, 5, 8, 0, 0, 1509, 1512, 3, 234, 117, 0, 1510, 1511, 5, 8, 0, 0, 1511, 1513, 3, 234, 117, 0, 1512, 1510, 1, 0, 0, 0, 1512, 1513, 1, 0, 0, 0, 1513, 1514, 1, 0, 0, 0, 1514, 1515, 5, 3, 0, 0, 1515, 277, 1, 0, 0, 0, 1516, 1517, 5, 132, 0, 0, 1517, 1518, 5, 2, 0, 0, 1518, 1519, 3, 234, 117, 0, 1519, 1520, 5, 8, 0, 0, 1520, 1521, 3, 234, 117, 0, 1521, 1522, 5, 8, 0, 0, 1522, 1525, 3, 234, 117, 0, 1523, 1524, 5, 8, 0, 0, 1524, 1526, 3, 234, 117, 0, 1525, 1523, 1, 0, 0, 0, 1525, 1526, 1, 0, 0, 0, 1526, 1527, 1, 0, 0, 0, 1527, 1528, 5, 3, 0, 0, 1528, 279, 1, 0, 0, 0, 1529, 1530, 5, 133, 0, 0, 1530, 1531, 3, 110, 55, 0, 1531, 281, 1, 0, 0, 0, 1532, 1533, 5, 78, 0, 0, 1533, 1534, 5, 133, 0, 0, 1534, 1535, 3, 110, 55, 0, 1535, 283, 1, 0, 0, 0, 1536, 1537, 5, 134, 0, 0, 1537, 1539, 5, 2, 0, 0, 1538, 1540, 5, 33, 0, 0, 1539, 1538, 1, 0, 0, 0, 1539, 1540, 1, 0, 0, 0, 1540, 1543, 1, 0, 0, 0, 1541, 1544, 5, 1, 0, 0, 1542, 1544, 3, 234, 117, 0, 1543, 1541, 1, 0, 0, 0, 1543, 1542, 1, 0, 0, 0, 1544, 1545, 1, 0, 0, 0, 1545, 1601, 5, 3, 0, 0, 1546, 1547, 5, 135, 0, 0, 1547, 1549, 5, 2, 0, 0, 1548, 1550, 5, 33, 0, 0, 1549, 1548, 1, 0, 0, 0, 1549, 1550, 1, 0, 0, 0, 1550, 1551, 1, 0, 0, 0, 1551, 1552, 3, 234, 117, 0, 1552, 1553, 5, 3, 0, 0, 1553, 1601, 1, 0, 0, 0, 1554, 1555, 5, 136, 0, 0, 1555, 1557, 5, 2, 0, 0, 1556, 1558, 5, 33, 0, 0, 1557, 1556, 1, 0, 0, 0, 1557, 1558, 1, 0, 0, 0, 1558, 1559, 1, 0, 0, 0, 1559, 1560, 3, 234, 117, 0, 1560, 1561, 5, 3, 0, 0, 1561, 1601, 1, 0, 0, 0, 1562, 1563, 5, 137, 0, 0, 1563, 1565, 5, 2, 0, 0, 1564, 1566, 5, 33, 0, 0, 1565, 1564, 1, 0, 0, 0, 1565, 1566, 1, 0, 0, 0, 1566, 1567, 1, 0, 0, 0, 1567, 1568, 3, 234, 117, 0, 1568, 1569, 5, 3, 0, 0, 1569, 1601, 1, 0, 0, 0, 1570, 1571, 5, 138, 0, 0, 1571, 1573, 5, 2, 0, 0, 1572, 1574, 5, 33, 0, 0, 1573, 1572, 1, 0, 0, 0, 1573, 1574, 1, 0, 0, 0, 1574, 1575, 1, 0, 0, 0, 1575, 1576, 3, 234, 117, 0, 1576, 1577, 5, 3, 0, 0, 1577, 1601, 1, 0, 0, 0, 1578, 1579, 5, 139, 0, 0, 1579, 1581, 5, 2, 0, 0, 1580, 1582, 5, 33, 0, 0, 1581, 1580, 1, 0, 0, 0, 1581, 1582, 1, 0, 0, 0, 1582, 1583, 1, 0, 0, 0, 1583, 1584, 3, 234, 117, 0, 1584, 1585, 5, 3, 0, 0, 1585, 1601, 1, 0, 0, 0, 1586, 1587, 5, 43, 0, 0, 1587, 1589, 5, 2, 0, 0, 1588, 1590, 5, 33, 0, 0, 1589, 1588, 1, 0, 0, 0, 1589, 1590, 1, 0, 0, 0, 1590, 1591, 1, 0, 0, 0, 1591, 1596, 3, 234, 117, 0, 1592, 1593, 5, 6, 0, 0, 1593, 1594, 5, 140, 0, 0, 1594, 1595, 5, 20, 0, 0, 1595, 1597, 3, 300, 150, 0, 1596, 1592, 1, 0, 0, 0, 1596, 1597, 1, 0, 0, 0, 1597, 1598, 1, 0, 0, 0, 1598, 1599, 5, 3, 0, 0, 1599, 1601, 1, 0, 0, 0, 1600, 1536, 1, 0, 0, 0, 1600, 1546, 1, 0, 0, 0, 1600, 1554, 1, 0, 0, 0, 1600, 1562, 1, 0, 0, 0, 1600, 1570, 1, 0, 0, 0, 1600, 1578, 1, 0, 0, 0, 1600, 1586, 1, 0, 0, 0, 1601, 285, 1, 0, 0, 0, 1602, 1604, 3, 302, 151, 0, 1603, 1605, 3, 150, 75, 0, 1604, 1603, 1, 0, 0, 0, 1604, 1605, 1, 0, 0, 0, 1605, 287, 1, 0, 0, 0, 1606, 1610, 3, 300, 150, 0, 1607, 1611, 5, 147, 0, 0, 1608, 1609, 5, 27, 0, 0, 1609, 1611, 3, 302, 151, 0, 1610, 1607, 1, 0, 0, 0, 1610, 1608, 1, 0, 0, 0, 1610, 1611, 1, 0, 0, 0, 1611, 289, 1, 0, 0, 0, 1612, 1616, 3, 292, 146, 0, 1613, 1616, 3, 294, 147, 0, 1614, 1616, 3, 296, 148, 0, 1615, 1612, 1, 0, 0, 0, 1615, 1613, 1, 0, 0, 0, 1615, 1614, 1, 0, 0, 0, 1616, 291, 1, 0, 0, 0, 1617, 1618, 7, 4, 0, 0, 1618, 293, 1, 0, 0, 0, 1619, 1620, 7, 5, 0, 0, 1620, 295, 1, 0, 0, 0, 1621, 1622, 7, 6, 0, 0, 1622, 297, 1, 0, 0, 0, 1623, 1624, 7, 7, 0, 0, 1624, 299, 1, 0, 0, 0, 1625, 1626, 7, 8, 0, 0, 1626, 301, 1, 0, 0, 0, 1627, 1629, 5, 148, 0, 0, 1628, 1627, 1, 0, 0, 0, 1628, 1629, 1, 0, 0, 0, 1629, 1632, 1, 0, 0, 0, 1630, 1633, 3, 308, 154, 0, 1631, 1633, 3, 304, 152, 0, 1632, 1630, 1, 0, 0, 0, 1632, 1631, 1, 0, 0, 0, 1633, 303, 1, 0, 0, 0, 1634, 1637, 3, 310, 155, 0, 1635, 1637, 3, 312, 156, 0, 1636, 1634, 1, 0, 0, 0, 1636, 1635, 1, 0, 0, 0, 1637, 305, 1, 0, 0, 0, 1638, 1639, 7, 9, 0, 0, 1639, 307, 1, 0, 0, 0, 1640, 1641, 5, 141, 0, 0, 1641, 309, 1, 0, 0, 0, 1642, 1643, 5, 143, 0, 0, 1643, 311, 1, 0, 0, 0, 1644, 1645, 5, 142, 0, 0, 1645, 313, 1, 0, 0, 0, 161, 316, 325, 331, 333, 347, 360, 365, 368, 372, 387, 396, 402, 406, 412, 415, 420, 424, 432, 441, 451, 456, 459, 462, 465, 471, 479, 484, 490, 496, 501, 507, 509, 513, 516, 520, 523, 527, 530, 534, 537, 541, 544, 548, 551, 553, 566, 572, 574, 587, 591, 596, 600, 606, 612, 618, 626, 634, 654, 658, 661, 666, 682, 687, 696, 707, 711, 714, 718, 725, 732, 734, 739, 744, 749, 754, 757, 762, 764, 774, 785, 802, 809, 819, 823, 829, 838, 843, 850, 860, 869, 877, 884, 889, 898, 903, 907, 914, 916, 924, 927, 935, 939, 944, 951, 962, 965, 970, 974, 989, 996, 1008, 1016, 1021, 1026, 1038, 1047, 1050, 1053, 1060, 1062, 1068, 1076, 1086, 1094, 1100, 1104, 1108, 1112, 1122, 1131, 1139, 1162, 1170, 1178, 1186, 1191, 1198, 1203, 1218, 1227, 1273, 1487, 1496, 1512, 1525, 1539, 1543, 1549, 1557, 1565, 1573, 1581, 1589, 1596, 1600, 1604, 1610, 1615, 1628, 1632, 1636] \ No newline at end of file +[4, 1, 175, 1647, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 1, 0, 1, 0, 3, 0, 317, 8, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 326, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 5, 2, 332, 8, 2, 10, 2, 12, 2, 335, 9, 2, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 5, 5, 346, 8, 5, 10, 5, 12, 5, 349, 9, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 3, 7, 361, 8, 7, 1, 7, 4, 7, 364, 8, 7, 11, 7, 12, 7, 365, 1, 7, 3, 7, 369, 8, 7, 1, 8, 1, 8, 3, 8, 373, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 5, 11, 386, 8, 11, 10, 11, 12, 11, 389, 9, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 395, 8, 11, 10, 11, 12, 11, 398, 9, 11, 1, 11, 1, 11, 1, 11, 3, 11, 403, 8, 11, 1, 11, 1, 11, 3, 11, 407, 8, 11, 1, 12, 1, 12, 4, 12, 411, 8, 12, 11, 12, 12, 12, 412, 1, 12, 3, 12, 416, 8, 12, 1, 12, 5, 12, 419, 8, 12, 10, 12, 12, 12, 422, 9, 12, 1, 12, 3, 12, 425, 8, 12, 1, 12, 1, 12, 1, 13, 1, 13, 5, 13, 431, 8, 13, 10, 13, 12, 13, 434, 9, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 3, 14, 442, 8, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 18, 3, 18, 452, 8, 18, 1, 18, 1, 18, 1, 19, 3, 19, 457, 8, 19, 1, 19, 3, 19, 460, 8, 19, 1, 19, 3, 19, 463, 8, 19, 1, 19, 3, 19, 466, 8, 19, 1, 20, 1, 20, 4, 20, 470, 8, 20, 11, 20, 12, 20, 471, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 480, 8, 21, 1, 21, 1, 21, 1, 21, 3, 21, 485, 8, 21, 1, 22, 1, 22, 4, 22, 489, 8, 22, 11, 22, 12, 22, 490, 1, 23, 1, 23, 1, 24, 1, 24, 3, 24, 497, 8, 24, 1, 24, 4, 24, 500, 8, 24, 11, 24, 12, 24, 501, 1, 25, 1, 25, 1, 25, 1, 25, 3, 25, 508, 8, 25, 3, 25, 510, 8, 25, 1, 26, 1, 26, 3, 26, 514, 8, 26, 1, 26, 3, 26, 517, 8, 26, 1, 26, 1, 26, 3, 26, 521, 8, 26, 1, 26, 3, 26, 524, 8, 26, 1, 26, 1, 26, 3, 26, 528, 8, 26, 1, 26, 3, 26, 531, 8, 26, 1, 26, 1, 26, 3, 26, 535, 8, 26, 1, 26, 3, 26, 538, 8, 26, 1, 26, 1, 26, 3, 26, 542, 8, 26, 1, 26, 3, 26, 545, 8, 26, 1, 26, 1, 26, 3, 26, 549, 8, 26, 1, 26, 3, 26, 552, 8, 26, 3, 26, 554, 8, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 3, 30, 567, 8, 30, 1, 31, 1, 31, 1, 31, 1, 31, 3, 31, 573, 8, 31, 3, 31, 575, 8, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 588, 8, 32, 1, 33, 1, 33, 3, 33, 592, 8, 33, 1, 33, 1, 33, 1, 33, 3, 33, 597, 8, 33, 1, 34, 1, 34, 3, 34, 601, 8, 34, 1, 34, 1, 34, 1, 35, 1, 35, 3, 35, 607, 8, 35, 1, 35, 1, 35, 1, 36, 1, 36, 3, 36, 613, 8, 36, 1, 36, 1, 36, 1, 37, 1, 37, 3, 37, 619, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 3, 38, 627, 8, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 3, 39, 635, 8, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 3, 43, 655, 8, 43, 1, 43, 1, 43, 3, 43, 659, 8, 43, 1, 43, 3, 43, 662, 8, 43, 1, 43, 5, 43, 665, 8, 43, 10, 43, 12, 43, 668, 9, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 3, 46, 683, 8, 46, 1, 47, 1, 47, 1, 47, 3, 47, 688, 8, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 3, 49, 697, 8, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 3, 52, 708, 8, 52, 1, 52, 1, 52, 3, 52, 712, 8, 52, 1, 52, 3, 52, 715, 8, 52, 5, 52, 717, 8, 52, 10, 52, 12, 52, 720, 9, 52, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 726, 8, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 3, 54, 733, 8, 54, 3, 54, 735, 8, 54, 1, 55, 1, 55, 1, 55, 3, 55, 740, 8, 55, 1, 55, 1, 55, 1, 56, 3, 56, 745, 8, 56, 1, 56, 5, 56, 748, 8, 56, 10, 56, 12, 56, 751, 9, 56, 1, 57, 1, 57, 3, 57, 755, 8, 57, 1, 57, 3, 57, 758, 8, 57, 1, 58, 1, 58, 1, 58, 3, 58, 763, 8, 58, 3, 58, 765, 8, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 775, 8, 59, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 3, 62, 786, 8, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 3, 65, 803, 8, 65, 1, 66, 1, 66, 1, 66, 5, 66, 808, 8, 66, 10, 66, 12, 66, 811, 9, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 5, 67, 818, 8, 67, 10, 67, 12, 67, 821, 9, 67, 1, 67, 3, 67, 824, 8, 67, 1, 67, 1, 67, 5, 67, 828, 8, 67, 10, 67, 12, 67, 831, 9, 67, 1, 67, 1, 67, 1, 68, 1, 68, 5, 68, 837, 8, 68, 10, 68, 12, 68, 840, 9, 68, 1, 68, 1, 68, 3, 68, 844, 8, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 3, 69, 851, 8, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 5, 71, 859, 8, 71, 10, 71, 12, 71, 862, 9, 71, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 3, 73, 870, 8, 73, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 3, 75, 878, 8, 75, 1, 75, 1, 75, 1, 75, 5, 75, 883, 8, 75, 10, 75, 12, 75, 886, 9, 75, 1, 75, 1, 75, 3, 75, 890, 8, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 5, 76, 897, 8, 76, 10, 76, 12, 76, 900, 9, 76, 1, 76, 1, 76, 3, 76, 904, 8, 76, 1, 77, 1, 77, 3, 77, 908, 8, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 3, 78, 915, 8, 78, 3, 78, 917, 8, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 3, 79, 925, 8, 79, 1, 80, 3, 80, 928, 8, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 3, 81, 936, 8, 81, 5, 81, 938, 8, 81, 10, 81, 12, 81, 941, 9, 81, 1, 82, 1, 82, 3, 82, 945, 8, 82, 1, 83, 1, 83, 1, 83, 5, 83, 950, 8, 83, 10, 83, 12, 83, 953, 9, 83, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 3, 85, 963, 8, 85, 1, 86, 3, 86, 966, 8, 86, 1, 87, 1, 87, 1, 87, 3, 87, 971, 8, 87, 5, 87, 973, 8, 87, 10, 87, 12, 87, 976, 9, 87, 1, 88, 1, 88, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 3, 92, 990, 8, 92, 1, 93, 1, 93, 1, 93, 5, 93, 995, 8, 93, 10, 93, 12, 93, 998, 9, 93, 1, 94, 1, 94, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 5, 96, 1007, 8, 96, 10, 96, 12, 96, 1010, 9, 96, 1, 97, 1, 97, 1, 97, 5, 97, 1015, 8, 97, 10, 97, 12, 97, 1018, 9, 97, 1, 98, 1, 98, 3, 98, 1022, 8, 98, 1, 99, 1, 99, 1, 99, 3, 99, 1027, 8, 99, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 3, 101, 1039, 8, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 5, 102, 1046, 8, 102, 10, 102, 12, 102, 1049, 9, 102, 3, 102, 1051, 8, 102, 1, 102, 3, 102, 1054, 8, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 103, 3, 103, 1061, 8, 103, 3, 103, 1063, 8, 103, 1, 104, 1, 104, 1, 105, 1, 105, 3, 105, 1069, 8, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 3, 107, 1077, 8, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 4, 109, 1085, 8, 109, 11, 109, 12, 109, 1086, 1, 109, 1, 109, 1, 110, 1, 110, 4, 110, 1093, 8, 110, 11, 110, 12, 110, 1094, 1, 110, 1, 110, 1, 111, 1, 111, 3, 111, 1101, 8, 111, 1, 112, 1, 112, 3, 112, 1105, 8, 112, 1, 113, 1, 113, 3, 113, 1109, 8, 113, 1, 114, 1, 114, 3, 114, 1113, 8, 114, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 3, 116, 1123, 8, 116, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 5, 118, 1130, 8, 118, 10, 118, 12, 118, 1133, 9, 118, 1, 119, 1, 119, 1, 119, 5, 119, 1138, 8, 119, 10, 119, 12, 119, 1141, 9, 119, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 3, 121, 1163, 8, 121, 1, 122, 1, 122, 1, 123, 1, 123, 5, 123, 1169, 8, 123, 10, 123, 12, 123, 1172, 9, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 3, 124, 1179, 8, 124, 1, 125, 1, 125, 1, 126, 1, 126, 1, 127, 1, 127, 3, 127, 1187, 8, 127, 1, 127, 5, 127, 1190, 8, 127, 10, 127, 12, 127, 1193, 9, 127, 1, 128, 1, 128, 5, 128, 1197, 8, 128, 10, 128, 12, 128, 1200, 9, 128, 1, 129, 1, 129, 3, 129, 1204, 8, 129, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 3, 132, 1219, 8, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 3, 133, 1228, 8, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 3, 135, 1274, 8, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 3, 135, 1488, 8, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 3, 136, 1497, 8, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 3, 138, 1513, 8, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 3, 139, 1526, 8, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 3, 142, 1540, 8, 142, 1, 142, 1, 142, 3, 142, 1544, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1550, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1558, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1566, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1574, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1582, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1590, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1597, 8, 142, 1, 142, 1, 142, 3, 142, 1601, 8, 142, 1, 143, 1, 143, 3, 143, 1605, 8, 143, 1, 144, 1, 144, 1, 144, 1, 144, 3, 144, 1611, 8, 144, 1, 145, 1, 145, 1, 145, 3, 145, 1616, 8, 145, 1, 146, 1, 146, 1, 147, 1, 147, 1, 148, 1, 148, 1, 149, 1, 149, 1, 150, 1, 150, 1, 151, 3, 151, 1629, 8, 151, 1, 151, 1, 151, 3, 151, 1633, 8, 151, 1, 152, 1, 152, 3, 152, 1637, 8, 152, 1, 153, 1, 153, 1, 154, 1, 154, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 0, 0, 157, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286, 288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310, 312, 0, 10, 1, 0, 33, 34, 1, 0, 47, 48, 2, 0, 1, 1, 13, 14, 1, 0, 145, 146, 1, 0, 149, 151, 1, 0, 152, 154, 1, 0, 155, 157, 1, 0, 28, 29, 1, 0, 159, 162, 2, 0, 144, 144, 165, 165, 1761, 0, 316, 1, 0, 0, 0, 2, 320, 1, 0, 0, 0, 4, 333, 1, 0, 0, 0, 6, 336, 1, 0, 0, 0, 8, 339, 1, 0, 0, 0, 10, 343, 1, 0, 0, 0, 12, 353, 1, 0, 0, 0, 14, 358, 1, 0, 0, 0, 16, 372, 1, 0, 0, 0, 18, 374, 1, 0, 0, 0, 20, 378, 1, 0, 0, 0, 22, 382, 1, 0, 0, 0, 24, 408, 1, 0, 0, 0, 26, 428, 1, 0, 0, 0, 28, 438, 1, 0, 0, 0, 30, 443, 1, 0, 0, 0, 32, 445, 1, 0, 0, 0, 34, 448, 1, 0, 0, 0, 36, 451, 1, 0, 0, 0, 38, 456, 1, 0, 0, 0, 40, 467, 1, 0, 0, 0, 42, 484, 1, 0, 0, 0, 44, 486, 1, 0, 0, 0, 46, 492, 1, 0, 0, 0, 48, 496, 1, 0, 0, 0, 50, 509, 1, 0, 0, 0, 52, 553, 1, 0, 0, 0, 54, 555, 1, 0, 0, 0, 56, 558, 1, 0, 0, 0, 58, 561, 1, 0, 0, 0, 60, 566, 1, 0, 0, 0, 62, 568, 1, 0, 0, 0, 64, 587, 1, 0, 0, 0, 66, 589, 1, 0, 0, 0, 68, 598, 1, 0, 0, 0, 70, 604, 1, 0, 0, 0, 72, 610, 1, 0, 0, 0, 74, 616, 1, 0, 0, 0, 76, 624, 1, 0, 0, 0, 78, 632, 1, 0, 0, 0, 80, 640, 1, 0, 0, 0, 82, 644, 1, 0, 0, 0, 84, 648, 1, 0, 0, 0, 86, 654, 1, 0, 0, 0, 88, 672, 1, 0, 0, 0, 90, 675, 1, 0, 0, 0, 92, 678, 1, 0, 0, 0, 94, 687, 1, 0, 0, 0, 96, 689, 1, 0, 0, 0, 98, 696, 1, 0, 0, 0, 100, 698, 1, 0, 0, 0, 102, 702, 1, 0, 0, 0, 104, 707, 1, 0, 0, 0, 106, 721, 1, 0, 0, 0, 108, 729, 1, 0, 0, 0, 110, 736, 1, 0, 0, 0, 112, 744, 1, 0, 0, 0, 114, 752, 1, 0, 0, 0, 116, 759, 1, 0, 0, 0, 118, 774, 1, 0, 0, 0, 120, 776, 1, 0, 0, 0, 122, 779, 1, 0, 0, 0, 124, 783, 1, 0, 0, 0, 126, 790, 1, 0, 0, 0, 128, 797, 1, 0, 0, 0, 130, 802, 1, 0, 0, 0, 132, 804, 1, 0, 0, 0, 134, 823, 1, 0, 0, 0, 136, 843, 1, 0, 0, 0, 138, 850, 1, 0, 0, 0, 140, 852, 1, 0, 0, 0, 142, 855, 1, 0, 0, 0, 144, 863, 1, 0, 0, 0, 146, 869, 1, 0, 0, 0, 148, 871, 1, 0, 0, 0, 150, 889, 1, 0, 0, 0, 152, 903, 1, 0, 0, 0, 154, 905, 1, 0, 0, 0, 156, 911, 1, 0, 0, 0, 158, 924, 1, 0, 0, 0, 160, 927, 1, 0, 0, 0, 162, 929, 1, 0, 0, 0, 164, 944, 1, 0, 0, 0, 166, 946, 1, 0, 0, 0, 168, 954, 1, 0, 0, 0, 170, 962, 1, 0, 0, 0, 172, 965, 1, 0, 0, 0, 174, 967, 1, 0, 0, 0, 176, 977, 1, 0, 0, 0, 178, 979, 1, 0, 0, 0, 180, 981, 1, 0, 0, 0, 182, 984, 1, 0, 0, 0, 184, 989, 1, 0, 0, 0, 186, 991, 1, 0, 0, 0, 188, 999, 1, 0, 0, 0, 190, 1001, 1, 0, 0, 0, 192, 1003, 1, 0, 0, 0, 194, 1011, 1, 0, 0, 0, 196, 1019, 1, 0, 0, 0, 198, 1026, 1, 0, 0, 0, 200, 1028, 1, 0, 0, 0, 202, 1038, 1, 0, 0, 0, 204, 1053, 1, 0, 0, 0, 206, 1062, 1, 0, 0, 0, 208, 1064, 1, 0, 0, 0, 210, 1068, 1, 0, 0, 0, 212, 1070, 1, 0, 0, 0, 214, 1076, 1, 0, 0, 0, 216, 1078, 1, 0, 0, 0, 218, 1082, 1, 0, 0, 0, 220, 1090, 1, 0, 0, 0, 222, 1100, 1, 0, 0, 0, 224, 1104, 1, 0, 0, 0, 226, 1108, 1, 0, 0, 0, 228, 1112, 1, 0, 0, 0, 230, 1114, 1, 0, 0, 0, 232, 1122, 1, 0, 0, 0, 234, 1124, 1, 0, 0, 0, 236, 1126, 1, 0, 0, 0, 238, 1134, 1, 0, 0, 0, 240, 1142, 1, 0, 0, 0, 242, 1144, 1, 0, 0, 0, 244, 1164, 1, 0, 0, 0, 246, 1166, 1, 0, 0, 0, 248, 1178, 1, 0, 0, 0, 250, 1180, 1, 0, 0, 0, 252, 1182, 1, 0, 0, 0, 254, 1186, 1, 0, 0, 0, 256, 1194, 1, 0, 0, 0, 258, 1203, 1, 0, 0, 0, 260, 1205, 1, 0, 0, 0, 262, 1208, 1, 0, 0, 0, 264, 1218, 1, 0, 0, 0, 266, 1227, 1, 0, 0, 0, 268, 1229, 1, 0, 0, 0, 270, 1487, 1, 0, 0, 0, 272, 1489, 1, 0, 0, 0, 274, 1500, 1, 0, 0, 0, 276, 1505, 1, 0, 0, 0, 278, 1516, 1, 0, 0, 0, 280, 1529, 1, 0, 0, 0, 282, 1532, 1, 0, 0, 0, 284, 1600, 1, 0, 0, 0, 286, 1602, 1, 0, 0, 0, 288, 1606, 1, 0, 0, 0, 290, 1615, 1, 0, 0, 0, 292, 1617, 1, 0, 0, 0, 294, 1619, 1, 0, 0, 0, 296, 1621, 1, 0, 0, 0, 298, 1623, 1, 0, 0, 0, 300, 1625, 1, 0, 0, 0, 302, 1628, 1, 0, 0, 0, 304, 1636, 1, 0, 0, 0, 306, 1638, 1, 0, 0, 0, 308, 1640, 1, 0, 0, 0, 310, 1642, 1, 0, 0, 0, 312, 1644, 1, 0, 0, 0, 314, 317, 3, 2, 1, 0, 315, 317, 3, 62, 31, 0, 316, 314, 1, 0, 0, 0, 316, 315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 319, 5, 0, 0, 1, 319, 1, 1, 0, 0, 0, 320, 325, 3, 4, 2, 0, 321, 326, 3, 10, 5, 0, 322, 326, 3, 22, 11, 0, 323, 326, 3, 24, 12, 0, 324, 326, 3, 26, 13, 0, 325, 321, 1, 0, 0, 0, 325, 322, 1, 0, 0, 0, 325, 323, 1, 0, 0, 0, 325, 324, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 328, 3, 60, 30, 0, 328, 3, 1, 0, 0, 0, 329, 332, 3, 6, 3, 0, 330, 332, 3, 8, 4, 0, 331, 329, 1, 0, 0, 0, 331, 330, 1, 0, 0, 0, 332, 335, 1, 0, 0, 0, 333, 331, 1, 0, 0, 0, 333, 334, 1, 0, 0, 0, 334, 5, 1, 0, 0, 0, 335, 333, 1, 0, 0, 0, 336, 337, 5, 30, 0, 0, 337, 338, 3, 308, 154, 0, 338, 7, 1, 0, 0, 0, 339, 340, 5, 31, 0, 0, 340, 341, 5, 142, 0, 0, 341, 342, 3, 308, 154, 0, 342, 9, 1, 0, 0, 0, 343, 347, 3, 14, 7, 0, 344, 346, 3, 28, 14, 0, 345, 344, 1, 0, 0, 0, 346, 349, 1, 0, 0, 0, 347, 345, 1, 0, 0, 0, 347, 348, 1, 0, 0, 0, 348, 350, 1, 0, 0, 0, 349, 347, 1, 0, 0, 0, 350, 351, 3, 36, 18, 0, 351, 352, 3, 38, 19, 0, 352, 11, 1, 0, 0, 0, 353, 354, 3, 14, 7, 0, 354, 355, 3, 36, 18, 0, 355, 356, 3, 38, 19, 0, 356, 357, 3, 60, 30, 0, 357, 13, 1, 0, 0, 0, 358, 360, 5, 32, 0, 0, 359, 361, 7, 0, 0, 0, 360, 359, 1, 0, 0, 0, 360, 361, 1, 0, 0, 0, 361, 368, 1, 0, 0, 0, 362, 364, 3, 16, 8, 0, 363, 362, 1, 0, 0, 0, 364, 365, 1, 0, 0, 0, 365, 363, 1, 0, 0, 0, 365, 366, 1, 0, 0, 0, 366, 369, 1, 0, 0, 0, 367, 369, 5, 1, 0, 0, 368, 363, 1, 0, 0, 0, 368, 367, 1, 0, 0, 0, 369, 15, 1, 0, 0, 0, 370, 373, 3, 230, 115, 0, 371, 373, 3, 18, 9, 0, 372, 370, 1, 0, 0, 0, 372, 371, 1, 0, 0, 0, 373, 17, 1, 0, 0, 0, 374, 375, 5, 2, 0, 0, 375, 376, 3, 20, 10, 0, 376, 377, 5, 3, 0, 0, 377, 19, 1, 0, 0, 0, 378, 379, 3, 234, 117, 0, 379, 380, 5, 35, 0, 0, 380, 381, 3, 230, 115, 0, 381, 21, 1, 0, 0, 0, 382, 406, 5, 36, 0, 0, 383, 387, 3, 154, 77, 0, 384, 386, 3, 28, 14, 0, 385, 384, 1, 0, 0, 0, 386, 389, 1, 0, 0, 0, 387, 385, 1, 0, 0, 0, 387, 388, 1, 0, 0, 0, 388, 390, 1, 0, 0, 0, 389, 387, 1, 0, 0, 0, 390, 391, 3, 36, 18, 0, 391, 392, 3, 38, 19, 0, 392, 407, 1, 0, 0, 0, 393, 395, 3, 28, 14, 0, 394, 393, 1, 0, 0, 0, 395, 398, 1, 0, 0, 0, 396, 394, 1, 0, 0, 0, 396, 397, 1, 0, 0, 0, 397, 399, 1, 0, 0, 0, 398, 396, 1, 0, 0, 0, 399, 400, 5, 37, 0, 0, 400, 402, 5, 4, 0, 0, 401, 403, 3, 108, 54, 0, 402, 401, 1, 0, 0, 0, 402, 403, 1, 0, 0, 0, 403, 404, 1, 0, 0, 0, 404, 405, 5, 5, 0, 0, 405, 407, 3, 38, 19, 0, 406, 383, 1, 0, 0, 0, 406, 396, 1, 0, 0, 0, 407, 23, 1, 0, 0, 0, 408, 415, 5, 38, 0, 0, 409, 411, 3, 228, 114, 0, 410, 409, 1, 0, 0, 0, 411, 412, 1, 0, 0, 0, 412, 410, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 416, 1, 0, 0, 0, 414, 416, 5, 1, 0, 0, 415, 410, 1, 0, 0, 0, 415, 414, 1, 0, 0, 0, 416, 420, 1, 0, 0, 0, 417, 419, 3, 28, 14, 0, 418, 417, 1, 0, 0, 0, 419, 422, 1, 0, 0, 0, 420, 418, 1, 0, 0, 0, 420, 421, 1, 0, 0, 0, 421, 424, 1, 0, 0, 0, 422, 420, 1, 0, 0, 0, 423, 425, 3, 36, 18, 0, 424, 423, 1, 0, 0, 0, 424, 425, 1, 0, 0, 0, 425, 426, 1, 0, 0, 0, 426, 427, 3, 38, 19, 0, 427, 25, 1, 0, 0, 0, 428, 432, 5, 39, 0, 0, 429, 431, 3, 28, 14, 0, 430, 429, 1, 0, 0, 0, 431, 434, 1, 0, 0, 0, 432, 430, 1, 0, 0, 0, 432, 433, 1, 0, 0, 0, 433, 435, 1, 0, 0, 0, 434, 432, 1, 0, 0, 0, 435, 436, 3, 36, 18, 0, 436, 437, 3, 38, 19, 0, 437, 27, 1, 0, 0, 0, 438, 441, 5, 40, 0, 0, 439, 442, 3, 30, 15, 0, 440, 442, 3, 32, 16, 0, 441, 439, 1, 0, 0, 0, 441, 440, 1, 0, 0, 0, 442, 29, 1, 0, 0, 0, 443, 444, 3, 34, 17, 0, 444, 31, 1, 0, 0, 0, 445, 446, 5, 41, 0, 0, 446, 447, 3, 34, 17, 0, 447, 33, 1, 0, 0, 0, 448, 449, 3, 302, 151, 0, 449, 35, 1, 0, 0, 0, 450, 452, 5, 37, 0, 0, 451, 450, 1, 0, 0, 0, 451, 452, 1, 0, 0, 0, 452, 453, 1, 0, 0, 0, 453, 454, 3, 110, 55, 0, 454, 37, 1, 0, 0, 0, 455, 457, 3, 40, 20, 0, 456, 455, 1, 0, 0, 0, 456, 457, 1, 0, 0, 0, 457, 459, 1, 0, 0, 0, 458, 460, 3, 44, 22, 0, 459, 458, 1, 0, 0, 0, 459, 460, 1, 0, 0, 0, 460, 462, 1, 0, 0, 0, 461, 463, 3, 48, 24, 0, 462, 461, 1, 0, 0, 0, 462, 463, 1, 0, 0, 0, 463, 465, 1, 0, 0, 0, 464, 466, 3, 52, 26, 0, 465, 464, 1, 0, 0, 0, 465, 466, 1, 0, 0, 0, 466, 39, 1, 0, 0, 0, 467, 469, 5, 42, 0, 0, 468, 470, 3, 42, 21, 0, 469, 468, 1, 0, 0, 0, 470, 471, 1, 0, 0, 0, 471, 469, 1, 0, 0, 0, 471, 472, 1, 0, 0, 0, 472, 41, 1, 0, 0, 0, 473, 485, 3, 270, 135, 0, 474, 485, 3, 148, 74, 0, 475, 476, 5, 2, 0, 0, 476, 479, 3, 234, 117, 0, 477, 478, 5, 35, 0, 0, 478, 480, 3, 230, 115, 0, 479, 477, 1, 0, 0, 0, 479, 480, 1, 0, 0, 0, 480, 481, 1, 0, 0, 0, 481, 482, 5, 3, 0, 0, 482, 485, 1, 0, 0, 0, 483, 485, 3, 230, 115, 0, 484, 473, 1, 0, 0, 0, 484, 474, 1, 0, 0, 0, 484, 475, 1, 0, 0, 0, 484, 483, 1, 0, 0, 0, 485, 43, 1, 0, 0, 0, 486, 488, 5, 44, 0, 0, 487, 489, 3, 46, 23, 0, 488, 487, 1, 0, 0, 0, 489, 490, 1, 0, 0, 0, 490, 488, 1, 0, 0, 0, 490, 491, 1, 0, 0, 0, 491, 45, 1, 0, 0, 0, 492, 493, 3, 146, 73, 0, 493, 47, 1, 0, 0, 0, 494, 497, 5, 45, 0, 0, 495, 497, 5, 46, 0, 0, 496, 494, 1, 0, 0, 0, 496, 495, 1, 0, 0, 0, 497, 499, 1, 0, 0, 0, 498, 500, 3, 50, 25, 0, 499, 498, 1, 0, 0, 0, 500, 501, 1, 0, 0, 0, 501, 499, 1, 0, 0, 0, 501, 502, 1, 0, 0, 0, 502, 49, 1, 0, 0, 0, 503, 504, 7, 1, 0, 0, 504, 510, 3, 268, 134, 0, 505, 508, 3, 146, 73, 0, 506, 508, 3, 230, 115, 0, 507, 505, 1, 0, 0, 0, 507, 506, 1, 0, 0, 0, 508, 510, 1, 0, 0, 0, 509, 503, 1, 0, 0, 0, 509, 507, 1, 0, 0, 0, 510, 51, 1, 0, 0, 0, 511, 513, 3, 54, 27, 0, 512, 514, 3, 56, 28, 0, 513, 512, 1, 0, 0, 0, 513, 514, 1, 0, 0, 0, 514, 516, 1, 0, 0, 0, 515, 517, 3, 58, 29, 0, 516, 515, 1, 0, 0, 0, 516, 517, 1, 0, 0, 0, 517, 554, 1, 0, 0, 0, 518, 520, 3, 54, 27, 0, 519, 521, 3, 58, 29, 0, 520, 519, 1, 0, 0, 0, 520, 521, 1, 0, 0, 0, 521, 523, 1, 0, 0, 0, 522, 524, 3, 56, 28, 0, 523, 522, 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 554, 1, 0, 0, 0, 525, 527, 3, 56, 28, 0, 526, 528, 3, 54, 27, 0, 527, 526, 1, 0, 0, 0, 527, 528, 1, 0, 0, 0, 528, 530, 1, 0, 0, 0, 529, 531, 3, 58, 29, 0, 530, 529, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 554, 1, 0, 0, 0, 532, 534, 3, 56, 28, 0, 533, 535, 3, 58, 29, 0, 534, 533, 1, 0, 0, 0, 534, 535, 1, 0, 0, 0, 535, 537, 1, 0, 0, 0, 536, 538, 3, 54, 27, 0, 537, 536, 1, 0, 0, 0, 537, 538, 1, 0, 0, 0, 538, 554, 1, 0, 0, 0, 539, 541, 3, 58, 29, 0, 540, 542, 3, 56, 28, 0, 541, 540, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 544, 1, 0, 0, 0, 543, 545, 3, 54, 27, 0, 544, 543, 1, 0, 0, 0, 544, 545, 1, 0, 0, 0, 545, 554, 1, 0, 0, 0, 546, 548, 3, 58, 29, 0, 547, 549, 3, 54, 27, 0, 548, 547, 1, 0, 0, 0, 548, 549, 1, 0, 0, 0, 549, 551, 1, 0, 0, 0, 550, 552, 3, 56, 28, 0, 551, 550, 1, 0, 0, 0, 551, 552, 1, 0, 0, 0, 552, 554, 1, 0, 0, 0, 553, 511, 1, 0, 0, 0, 553, 518, 1, 0, 0, 0, 553, 525, 1, 0, 0, 0, 553, 532, 1, 0, 0, 0, 553, 539, 1, 0, 0, 0, 553, 546, 1, 0, 0, 0, 554, 53, 1, 0, 0, 0, 555, 556, 5, 49, 0, 0, 556, 557, 3, 208, 104, 0, 557, 55, 1, 0, 0, 0, 558, 559, 5, 50, 0, 0, 559, 560, 3, 208, 104, 0, 560, 57, 1, 0, 0, 0, 561, 562, 5, 51, 0, 0, 562, 563, 3, 208, 104, 0, 563, 59, 1, 0, 0, 0, 564, 565, 5, 52, 0, 0, 565, 567, 3, 130, 65, 0, 566, 564, 1, 0, 0, 0, 566, 567, 1, 0, 0, 0, 567, 61, 1, 0, 0, 0, 568, 574, 3, 4, 2, 0, 569, 572, 3, 64, 32, 0, 570, 571, 5, 6, 0, 0, 571, 573, 3, 62, 31, 0, 572, 570, 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 575, 1, 0, 0, 0, 574, 569, 1, 0, 0, 0, 574, 575, 1, 0, 0, 0, 575, 63, 1, 0, 0, 0, 576, 588, 3, 66, 33, 0, 577, 588, 3, 68, 34, 0, 578, 588, 3, 70, 35, 0, 579, 588, 3, 74, 37, 0, 580, 588, 3, 76, 38, 0, 581, 588, 3, 78, 39, 0, 582, 588, 3, 72, 36, 0, 583, 588, 3, 80, 40, 0, 584, 588, 3, 82, 41, 0, 585, 588, 3, 84, 42, 0, 586, 588, 3, 86, 43, 0, 587, 576, 1, 0, 0, 0, 587, 577, 1, 0, 0, 0, 587, 578, 1, 0, 0, 0, 587, 579, 1, 0, 0, 0, 587, 580, 1, 0, 0, 0, 587, 581, 1, 0, 0, 0, 587, 582, 1, 0, 0, 0, 587, 583, 1, 0, 0, 0, 587, 584, 1, 0, 0, 0, 587, 585, 1, 0, 0, 0, 587, 586, 1, 0, 0, 0, 588, 65, 1, 0, 0, 0, 589, 591, 5, 53, 0, 0, 590, 592, 5, 54, 0, 0, 591, 590, 1, 0, 0, 0, 591, 592, 1, 0, 0, 0, 592, 593, 1, 0, 0, 0, 593, 596, 3, 302, 151, 0, 594, 595, 5, 55, 0, 0, 595, 597, 3, 96, 48, 0, 596, 594, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 67, 1, 0, 0, 0, 598, 600, 5, 56, 0, 0, 599, 601, 5, 54, 0, 0, 600, 599, 1, 0, 0, 0, 600, 601, 1, 0, 0, 0, 601, 602, 1, 0, 0, 0, 602, 603, 3, 98, 49, 0, 603, 69, 1, 0, 0, 0, 604, 606, 5, 57, 0, 0, 605, 607, 5, 54, 0, 0, 606, 605, 1, 0, 0, 0, 606, 607, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 609, 3, 98, 49, 0, 609, 71, 1, 0, 0, 0, 610, 612, 5, 58, 0, 0, 611, 613, 5, 54, 0, 0, 612, 611, 1, 0, 0, 0, 612, 613, 1, 0, 0, 0, 613, 614, 1, 0, 0, 0, 614, 615, 3, 96, 48, 0, 615, 73, 1, 0, 0, 0, 616, 618, 5, 59, 0, 0, 617, 619, 5, 54, 0, 0, 618, 617, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 621, 3, 94, 47, 0, 621, 622, 5, 60, 0, 0, 622, 623, 3, 94, 47, 0, 623, 75, 1, 0, 0, 0, 624, 626, 5, 62, 0, 0, 625, 627, 5, 54, 0, 0, 626, 625, 1, 0, 0, 0, 626, 627, 1, 0, 0, 0, 627, 628, 1, 0, 0, 0, 628, 629, 3, 94, 47, 0, 629, 630, 5, 60, 0, 0, 630, 631, 3, 94, 47, 0, 631, 77, 1, 0, 0, 0, 632, 634, 5, 63, 0, 0, 633, 635, 5, 54, 0, 0, 634, 633, 1, 0, 0, 0, 634, 635, 1, 0, 0, 0, 635, 636, 1, 0, 0, 0, 636, 637, 3, 94, 47, 0, 637, 638, 5, 60, 0, 0, 638, 639, 3, 94, 47, 0, 639, 79, 1, 0, 0, 0, 640, 641, 5, 64, 0, 0, 641, 642, 5, 61, 0, 0, 642, 643, 3, 102, 51, 0, 643, 81, 1, 0, 0, 0, 644, 645, 5, 65, 0, 0, 645, 646, 5, 61, 0, 0, 646, 647, 3, 102, 51, 0, 647, 83, 1, 0, 0, 0, 648, 649, 5, 65, 0, 0, 649, 650, 5, 37, 0, 0, 650, 651, 3, 100, 50, 0, 651, 85, 1, 0, 0, 0, 652, 653, 5, 66, 0, 0, 653, 655, 3, 302, 151, 0, 654, 652, 1, 0, 0, 0, 654, 655, 1, 0, 0, 0, 655, 661, 1, 0, 0, 0, 656, 658, 3, 88, 44, 0, 657, 659, 3, 90, 45, 0, 658, 657, 1, 0, 0, 0, 658, 659, 1, 0, 0, 0, 659, 662, 1, 0, 0, 0, 660, 662, 3, 90, 45, 0, 661, 656, 1, 0, 0, 0, 661, 660, 1, 0, 0, 0, 662, 666, 1, 0, 0, 0, 663, 665, 3, 92, 46, 0, 664, 663, 1, 0, 0, 0, 665, 668, 1, 0, 0, 0, 666, 664, 1, 0, 0, 0, 666, 667, 1, 0, 0, 0, 667, 669, 1, 0, 0, 0, 668, 666, 1, 0, 0, 0, 669, 670, 5, 37, 0, 0, 670, 671, 3, 110, 55, 0, 671, 87, 1, 0, 0, 0, 672, 673, 5, 65, 0, 0, 673, 674, 3, 100, 50, 0, 674, 89, 1, 0, 0, 0, 675, 676, 5, 64, 0, 0, 676, 677, 3, 100, 50, 0, 677, 91, 1, 0, 0, 0, 678, 682, 5, 67, 0, 0, 679, 683, 3, 302, 151, 0, 680, 681, 5, 41, 0, 0, 681, 683, 3, 302, 151, 0, 682, 679, 1, 0, 0, 0, 682, 680, 1, 0, 0, 0, 683, 93, 1, 0, 0, 0, 684, 688, 5, 68, 0, 0, 685, 686, 5, 69, 0, 0, 686, 688, 3, 302, 151, 0, 687, 684, 1, 0, 0, 0, 687, 685, 1, 0, 0, 0, 688, 95, 1, 0, 0, 0, 689, 690, 5, 69, 0, 0, 690, 691, 3, 302, 151, 0, 691, 97, 1, 0, 0, 0, 692, 697, 3, 96, 48, 0, 693, 697, 5, 68, 0, 0, 694, 697, 5, 41, 0, 0, 695, 697, 5, 70, 0, 0, 696, 692, 1, 0, 0, 0, 696, 693, 1, 0, 0, 0, 696, 694, 1, 0, 0, 0, 696, 695, 1, 0, 0, 0, 697, 99, 1, 0, 0, 0, 698, 699, 5, 4, 0, 0, 699, 700, 3, 104, 52, 0, 700, 701, 5, 5, 0, 0, 701, 101, 1, 0, 0, 0, 702, 703, 5, 4, 0, 0, 703, 704, 3, 104, 52, 0, 704, 705, 5, 5, 0, 0, 705, 103, 1, 0, 0, 0, 706, 708, 3, 108, 54, 0, 707, 706, 1, 0, 0, 0, 707, 708, 1, 0, 0, 0, 708, 718, 1, 0, 0, 0, 709, 711, 3, 106, 53, 0, 710, 712, 5, 7, 0, 0, 711, 710, 1, 0, 0, 0, 711, 712, 1, 0, 0, 0, 712, 714, 1, 0, 0, 0, 713, 715, 3, 108, 54, 0, 714, 713, 1, 0, 0, 0, 714, 715, 1, 0, 0, 0, 715, 717, 1, 0, 0, 0, 716, 709, 1, 0, 0, 0, 717, 720, 1, 0, 0, 0, 718, 716, 1, 0, 0, 0, 718, 719, 1, 0, 0, 0, 719, 105, 1, 0, 0, 0, 720, 718, 1, 0, 0, 0, 721, 722, 5, 69, 0, 0, 722, 723, 3, 228, 114, 0, 723, 725, 5, 4, 0, 0, 724, 726, 3, 108, 54, 0, 725, 724, 1, 0, 0, 0, 725, 726, 1, 0, 0, 0, 726, 727, 1, 0, 0, 0, 727, 728, 5, 5, 0, 0, 728, 107, 1, 0, 0, 0, 729, 734, 3, 158, 79, 0, 730, 732, 5, 7, 0, 0, 731, 733, 3, 108, 54, 0, 732, 731, 1, 0, 0, 0, 732, 733, 1, 0, 0, 0, 733, 735, 1, 0, 0, 0, 734, 730, 1, 0, 0, 0, 734, 735, 1, 0, 0, 0, 735, 109, 1, 0, 0, 0, 736, 739, 5, 4, 0, 0, 737, 740, 3, 12, 6, 0, 738, 740, 3, 112, 56, 0, 739, 737, 1, 0, 0, 0, 739, 738, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 742, 5, 5, 0, 0, 742, 111, 1, 0, 0, 0, 743, 745, 3, 116, 58, 0, 744, 743, 1, 0, 0, 0, 744, 745, 1, 0, 0, 0, 745, 749, 1, 0, 0, 0, 746, 748, 3, 114, 57, 0, 747, 746, 1, 0, 0, 0, 748, 751, 1, 0, 0, 0, 749, 747, 1, 0, 0, 0, 749, 750, 1, 0, 0, 0, 750, 113, 1, 0, 0, 0, 751, 749, 1, 0, 0, 0, 752, 754, 3, 118, 59, 0, 753, 755, 5, 7, 0, 0, 754, 753, 1, 0, 0, 0, 754, 755, 1, 0, 0, 0, 755, 757, 1, 0, 0, 0, 756, 758, 3, 116, 58, 0, 757, 756, 1, 0, 0, 0, 757, 758, 1, 0, 0, 0, 758, 115, 1, 0, 0, 0, 759, 764, 3, 170, 85, 0, 760, 762, 5, 7, 0, 0, 761, 763, 3, 116, 58, 0, 762, 761, 1, 0, 0, 0, 762, 763, 1, 0, 0, 0, 763, 765, 1, 0, 0, 0, 764, 760, 1, 0, 0, 0, 764, 765, 1, 0, 0, 0, 765, 117, 1, 0, 0, 0, 766, 775, 3, 142, 71, 0, 767, 775, 3, 120, 60, 0, 768, 775, 3, 140, 70, 0, 769, 775, 3, 122, 61, 0, 770, 775, 3, 124, 62, 0, 771, 775, 3, 144, 72, 0, 772, 775, 3, 126, 63, 0, 773, 775, 3, 128, 64, 0, 774, 766, 1, 0, 0, 0, 774, 767, 1, 0, 0, 0, 774, 768, 1, 0, 0, 0, 774, 769, 1, 0, 0, 0, 774, 770, 1, 0, 0, 0, 774, 771, 1, 0, 0, 0, 774, 772, 1, 0, 0, 0, 774, 773, 1, 0, 0, 0, 775, 119, 1, 0, 0, 0, 776, 777, 5, 71, 0, 0, 777, 778, 3, 110, 55, 0, 778, 121, 1, 0, 0, 0, 779, 780, 5, 69, 0, 0, 780, 781, 3, 228, 114, 0, 781, 782, 3, 110, 55, 0, 782, 123, 1, 0, 0, 0, 783, 785, 5, 72, 0, 0, 784, 786, 5, 54, 0, 0, 785, 784, 1, 0, 0, 0, 785, 786, 1, 0, 0, 0, 786, 787, 1, 0, 0, 0, 787, 788, 3, 228, 114, 0, 788, 789, 3, 110, 55, 0, 789, 125, 1, 0, 0, 0, 790, 791, 5, 73, 0, 0, 791, 792, 5, 2, 0, 0, 792, 793, 3, 234, 117, 0, 793, 794, 5, 35, 0, 0, 794, 795, 3, 230, 115, 0, 795, 796, 5, 3, 0, 0, 796, 127, 1, 0, 0, 0, 797, 798, 5, 52, 0, 0, 798, 799, 3, 130, 65, 0, 799, 129, 1, 0, 0, 0, 800, 803, 3, 132, 66, 0, 801, 803, 3, 134, 67, 0, 802, 800, 1, 0, 0, 0, 802, 801, 1, 0, 0, 0, 803, 131, 1, 0, 0, 0, 804, 805, 3, 230, 115, 0, 805, 809, 5, 4, 0, 0, 806, 808, 3, 138, 69, 0, 807, 806, 1, 0, 0, 0, 808, 811, 1, 0, 0, 0, 809, 807, 1, 0, 0, 0, 809, 810, 1, 0, 0, 0, 810, 812, 1, 0, 0, 0, 811, 809, 1, 0, 0, 0, 812, 813, 5, 5, 0, 0, 813, 133, 1, 0, 0, 0, 814, 824, 5, 164, 0, 0, 815, 819, 5, 2, 0, 0, 816, 818, 3, 230, 115, 0, 817, 816, 1, 0, 0, 0, 818, 821, 1, 0, 0, 0, 819, 817, 1, 0, 0, 0, 819, 820, 1, 0, 0, 0, 820, 822, 1, 0, 0, 0, 821, 819, 1, 0, 0, 0, 822, 824, 5, 3, 0, 0, 823, 814, 1, 0, 0, 0, 823, 815, 1, 0, 0, 0, 824, 825, 1, 0, 0, 0, 825, 829, 5, 4, 0, 0, 826, 828, 3, 136, 68, 0, 827, 826, 1, 0, 0, 0, 828, 831, 1, 0, 0, 0, 829, 827, 1, 0, 0, 0, 829, 830, 1, 0, 0, 0, 830, 832, 1, 0, 0, 0, 831, 829, 1, 0, 0, 0, 832, 833, 5, 5, 0, 0, 833, 135, 1, 0, 0, 0, 834, 838, 5, 2, 0, 0, 835, 837, 3, 138, 69, 0, 836, 835, 1, 0, 0, 0, 837, 840, 1, 0, 0, 0, 838, 836, 1, 0, 0, 0, 838, 839, 1, 0, 0, 0, 839, 841, 1, 0, 0, 0, 840, 838, 1, 0, 0, 0, 841, 844, 5, 3, 0, 0, 842, 844, 5, 164, 0, 0, 843, 834, 1, 0, 0, 0, 843, 842, 1, 0, 0, 0, 844, 137, 1, 0, 0, 0, 845, 851, 3, 302, 151, 0, 846, 851, 3, 288, 144, 0, 847, 851, 3, 290, 145, 0, 848, 851, 3, 298, 149, 0, 849, 851, 5, 74, 0, 0, 850, 845, 1, 0, 0, 0, 850, 846, 1, 0, 0, 0, 850, 847, 1, 0, 0, 0, 850, 848, 1, 0, 0, 0, 850, 849, 1, 0, 0, 0, 851, 139, 1, 0, 0, 0, 852, 853, 5, 75, 0, 0, 853, 854, 3, 110, 55, 0, 854, 141, 1, 0, 0, 0, 855, 860, 3, 110, 55, 0, 856, 857, 5, 76, 0, 0, 857, 859, 3, 110, 55, 0, 858, 856, 1, 0, 0, 0, 859, 862, 1, 0, 0, 0, 860, 858, 1, 0, 0, 0, 860, 861, 1, 0, 0, 0, 861, 143, 1, 0, 0, 0, 862, 860, 1, 0, 0, 0, 863, 864, 5, 77, 0, 0, 864, 865, 3, 146, 73, 0, 865, 145, 1, 0, 0, 0, 866, 870, 3, 268, 134, 0, 867, 870, 3, 270, 135, 0, 868, 870, 3, 148, 74, 0, 869, 866, 1, 0, 0, 0, 869, 867, 1, 0, 0, 0, 869, 868, 1, 0, 0, 0, 870, 147, 1, 0, 0, 0, 871, 872, 3, 302, 151, 0, 872, 873, 3, 150, 75, 0, 873, 149, 1, 0, 0, 0, 874, 890, 5, 164, 0, 0, 875, 877, 5, 2, 0, 0, 876, 878, 5, 33, 0, 0, 877, 876, 1, 0, 0, 0, 877, 878, 1, 0, 0, 0, 878, 879, 1, 0, 0, 0, 879, 884, 3, 234, 117, 0, 880, 881, 5, 8, 0, 0, 881, 883, 3, 234, 117, 0, 882, 880, 1, 0, 0, 0, 883, 886, 1, 0, 0, 0, 884, 882, 1, 0, 0, 0, 884, 885, 1, 0, 0, 0, 885, 887, 1, 0, 0, 0, 886, 884, 1, 0, 0, 0, 887, 888, 5, 3, 0, 0, 888, 890, 1, 0, 0, 0, 889, 874, 1, 0, 0, 0, 889, 875, 1, 0, 0, 0, 890, 151, 1, 0, 0, 0, 891, 904, 5, 164, 0, 0, 892, 893, 5, 2, 0, 0, 893, 898, 3, 234, 117, 0, 894, 895, 5, 8, 0, 0, 895, 897, 3, 234, 117, 0, 896, 894, 1, 0, 0, 0, 897, 900, 1, 0, 0, 0, 898, 896, 1, 0, 0, 0, 898, 899, 1, 0, 0, 0, 899, 901, 1, 0, 0, 0, 900, 898, 1, 0, 0, 0, 901, 902, 5, 3, 0, 0, 902, 904, 1, 0, 0, 0, 903, 891, 1, 0, 0, 0, 903, 892, 1, 0, 0, 0, 904, 153, 1, 0, 0, 0, 905, 907, 5, 4, 0, 0, 906, 908, 3, 156, 78, 0, 907, 906, 1, 0, 0, 0, 907, 908, 1, 0, 0, 0, 908, 909, 1, 0, 0, 0, 909, 910, 5, 5, 0, 0, 910, 155, 1, 0, 0, 0, 911, 916, 3, 158, 79, 0, 912, 914, 5, 7, 0, 0, 913, 915, 3, 156, 78, 0, 914, 913, 1, 0, 0, 0, 914, 915, 1, 0, 0, 0, 915, 917, 1, 0, 0, 0, 916, 912, 1, 0, 0, 0, 916, 917, 1, 0, 0, 0, 917, 157, 1, 0, 0, 0, 918, 919, 3, 226, 113, 0, 919, 920, 3, 162, 81, 0, 920, 925, 1, 0, 0, 0, 921, 922, 3, 210, 105, 0, 922, 923, 3, 160, 80, 0, 923, 925, 1, 0, 0, 0, 924, 918, 1, 0, 0, 0, 924, 921, 1, 0, 0, 0, 925, 159, 1, 0, 0, 0, 926, 928, 3, 162, 81, 0, 927, 926, 1, 0, 0, 0, 927, 928, 1, 0, 0, 0, 928, 161, 1, 0, 0, 0, 929, 930, 3, 164, 82, 0, 930, 939, 3, 166, 83, 0, 931, 935, 5, 6, 0, 0, 932, 933, 3, 164, 82, 0, 933, 934, 3, 166, 83, 0, 934, 936, 1, 0, 0, 0, 935, 932, 1, 0, 0, 0, 935, 936, 1, 0, 0, 0, 936, 938, 1, 0, 0, 0, 937, 931, 1, 0, 0, 0, 938, 941, 1, 0, 0, 0, 939, 937, 1, 0, 0, 0, 939, 940, 1, 0, 0, 0, 940, 163, 1, 0, 0, 0, 941, 939, 1, 0, 0, 0, 942, 945, 3, 228, 114, 0, 943, 945, 5, 9, 0, 0, 944, 942, 1, 0, 0, 0, 944, 943, 1, 0, 0, 0, 945, 165, 1, 0, 0, 0, 946, 951, 3, 168, 84, 0, 947, 948, 5, 8, 0, 0, 948, 950, 3, 168, 84, 0, 949, 947, 1, 0, 0, 0, 950, 953, 1, 0, 0, 0, 951, 949, 1, 0, 0, 0, 951, 952, 1, 0, 0, 0, 952, 167, 1, 0, 0, 0, 953, 951, 1, 0, 0, 0, 954, 955, 3, 222, 111, 0, 955, 169, 1, 0, 0, 0, 956, 957, 3, 226, 113, 0, 957, 958, 3, 174, 87, 0, 958, 963, 1, 0, 0, 0, 959, 960, 3, 214, 107, 0, 960, 961, 3, 172, 86, 0, 961, 963, 1, 0, 0, 0, 962, 956, 1, 0, 0, 0, 962, 959, 1, 0, 0, 0, 963, 171, 1, 0, 0, 0, 964, 966, 3, 174, 87, 0, 965, 964, 1, 0, 0, 0, 965, 966, 1, 0, 0, 0, 966, 173, 1, 0, 0, 0, 967, 974, 3, 182, 91, 0, 968, 970, 5, 6, 0, 0, 969, 971, 3, 180, 90, 0, 970, 969, 1, 0, 0, 0, 970, 971, 1, 0, 0, 0, 971, 973, 1, 0, 0, 0, 972, 968, 1, 0, 0, 0, 973, 976, 1, 0, 0, 0, 974, 972, 1, 0, 0, 0, 974, 975, 1, 0, 0, 0, 975, 175, 1, 0, 0, 0, 976, 974, 1, 0, 0, 0, 977, 978, 3, 190, 95, 0, 978, 177, 1, 0, 0, 0, 979, 980, 3, 230, 115, 0, 980, 179, 1, 0, 0, 0, 981, 982, 3, 184, 92, 0, 982, 983, 3, 166, 83, 0, 983, 181, 1, 0, 0, 0, 984, 985, 3, 184, 92, 0, 985, 986, 3, 186, 93, 0, 986, 183, 1, 0, 0, 0, 987, 990, 3, 176, 88, 0, 988, 990, 3, 178, 89, 0, 989, 987, 1, 0, 0, 0, 989, 988, 1, 0, 0, 0, 990, 185, 1, 0, 0, 0, 991, 996, 3, 188, 94, 0, 992, 993, 5, 8, 0, 0, 993, 995, 3, 188, 94, 0, 994, 992, 1, 0, 0, 0, 995, 998, 1, 0, 0, 0, 996, 994, 1, 0, 0, 0, 996, 997, 1, 0, 0, 0, 997, 187, 1, 0, 0, 0, 998, 996, 1, 0, 0, 0, 999, 1000, 3, 224, 112, 0, 1000, 189, 1, 0, 0, 0, 1001, 1002, 3, 192, 96, 0, 1002, 191, 1, 0, 0, 0, 1003, 1008, 3, 194, 97, 0, 1004, 1005, 5, 10, 0, 0, 1005, 1007, 3, 194, 97, 0, 1006, 1004, 1, 0, 0, 0, 1007, 1010, 1, 0, 0, 0, 1008, 1006, 1, 0, 0, 0, 1008, 1009, 1, 0, 0, 0, 1009, 193, 1, 0, 0, 0, 1010, 1008, 1, 0, 0, 0, 1011, 1016, 3, 198, 99, 0, 1012, 1013, 5, 11, 0, 0, 1013, 1015, 3, 198, 99, 0, 1014, 1012, 1, 0, 0, 0, 1015, 1018, 1, 0, 0, 0, 1016, 1014, 1, 0, 0, 0, 1016, 1017, 1, 0, 0, 0, 1017, 195, 1, 0, 0, 0, 1018, 1016, 1, 0, 0, 0, 1019, 1021, 3, 202, 101, 0, 1020, 1022, 3, 200, 100, 0, 1021, 1020, 1, 0, 0, 0, 1021, 1022, 1, 0, 0, 0, 1022, 197, 1, 0, 0, 0, 1023, 1027, 3, 196, 98, 0, 1024, 1025, 5, 12, 0, 0, 1025, 1027, 3, 196, 98, 0, 1026, 1023, 1, 0, 0, 0, 1026, 1024, 1, 0, 0, 0, 1027, 199, 1, 0, 0, 0, 1028, 1029, 7, 2, 0, 0, 1029, 201, 1, 0, 0, 0, 1030, 1039, 3, 302, 151, 0, 1031, 1039, 5, 9, 0, 0, 1032, 1033, 5, 15, 0, 0, 1033, 1039, 3, 204, 102, 0, 1034, 1035, 5, 2, 0, 0, 1035, 1036, 3, 190, 95, 0, 1036, 1037, 5, 3, 0, 0, 1037, 1039, 1, 0, 0, 0, 1038, 1030, 1, 0, 0, 0, 1038, 1031, 1, 0, 0, 0, 1038, 1032, 1, 0, 0, 0, 1038, 1034, 1, 0, 0, 0, 1039, 203, 1, 0, 0, 0, 1040, 1054, 3, 206, 103, 0, 1041, 1050, 5, 2, 0, 0, 1042, 1047, 3, 206, 103, 0, 1043, 1044, 5, 10, 0, 0, 1044, 1046, 3, 206, 103, 0, 1045, 1043, 1, 0, 0, 0, 1046, 1049, 1, 0, 0, 0, 1047, 1045, 1, 0, 0, 0, 1047, 1048, 1, 0, 0, 0, 1048, 1051, 1, 0, 0, 0, 1049, 1047, 1, 0, 0, 0, 1050, 1042, 1, 0, 0, 0, 1050, 1051, 1, 0, 0, 0, 1051, 1052, 1, 0, 0, 0, 1052, 1054, 5, 3, 0, 0, 1053, 1040, 1, 0, 0, 0, 1053, 1041, 1, 0, 0, 0, 1054, 205, 1, 0, 0, 0, 1055, 1063, 3, 302, 151, 0, 1056, 1063, 5, 9, 0, 0, 1057, 1060, 5, 12, 0, 0, 1058, 1061, 3, 302, 151, 0, 1059, 1061, 5, 9, 0, 0, 1060, 1058, 1, 0, 0, 0, 1060, 1059, 1, 0, 0, 0, 1061, 1063, 1, 0, 0, 0, 1062, 1055, 1, 0, 0, 0, 1062, 1056, 1, 0, 0, 0, 1062, 1057, 1, 0, 0, 0, 1063, 207, 1, 0, 0, 0, 1064, 1065, 5, 149, 0, 0, 1065, 209, 1, 0, 0, 0, 1066, 1069, 3, 218, 109, 0, 1067, 1069, 3, 212, 106, 0, 1068, 1066, 1, 0, 0, 0, 1068, 1067, 1, 0, 0, 0, 1069, 211, 1, 0, 0, 0, 1070, 1071, 5, 16, 0, 0, 1071, 1072, 3, 162, 81, 0, 1072, 1073, 5, 17, 0, 0, 1073, 213, 1, 0, 0, 0, 1074, 1077, 3, 220, 110, 0, 1075, 1077, 3, 216, 108, 0, 1076, 1074, 1, 0, 0, 0, 1076, 1075, 1, 0, 0, 0, 1077, 215, 1, 0, 0, 0, 1078, 1079, 5, 16, 0, 0, 1079, 1080, 3, 174, 87, 0, 1080, 1081, 5, 17, 0, 0, 1081, 217, 1, 0, 0, 0, 1082, 1084, 5, 2, 0, 0, 1083, 1085, 3, 222, 111, 0, 1084, 1083, 1, 0, 0, 0, 1085, 1086, 1, 0, 0, 0, 1086, 1084, 1, 0, 0, 0, 1086, 1087, 1, 0, 0, 0, 1087, 1088, 1, 0, 0, 0, 1088, 1089, 5, 3, 0, 0, 1089, 219, 1, 0, 0, 0, 1090, 1092, 5, 2, 0, 0, 1091, 1093, 3, 224, 112, 0, 1092, 1091, 1, 0, 0, 0, 1093, 1094, 1, 0, 0, 0, 1094, 1092, 1, 0, 0, 0, 1094, 1095, 1, 0, 0, 0, 1095, 1096, 1, 0, 0, 0, 1096, 1097, 5, 3, 0, 0, 1097, 221, 1, 0, 0, 0, 1098, 1101, 3, 226, 113, 0, 1099, 1101, 3, 210, 105, 0, 1100, 1098, 1, 0, 0, 0, 1100, 1099, 1, 0, 0, 0, 1101, 223, 1, 0, 0, 0, 1102, 1105, 3, 226, 113, 0, 1103, 1105, 3, 214, 107, 0, 1104, 1102, 1, 0, 0, 0, 1104, 1103, 1, 0, 0, 0, 1105, 225, 1, 0, 0, 0, 1106, 1109, 3, 230, 115, 0, 1107, 1109, 3, 232, 116, 0, 1108, 1106, 1, 0, 0, 0, 1108, 1107, 1, 0, 0, 0, 1109, 227, 1, 0, 0, 0, 1110, 1113, 3, 230, 115, 0, 1111, 1113, 3, 302, 151, 0, 1112, 1110, 1, 0, 0, 0, 1112, 1111, 1, 0, 0, 0, 1113, 229, 1, 0, 0, 0, 1114, 1115, 7, 3, 0, 0, 1115, 231, 1, 0, 0, 0, 1116, 1123, 3, 302, 151, 0, 1117, 1123, 3, 288, 144, 0, 1118, 1123, 3, 290, 145, 0, 1119, 1123, 3, 298, 149, 0, 1120, 1123, 3, 306, 153, 0, 1121, 1123, 5, 164, 0, 0, 1122, 1116, 1, 0, 0, 0, 1122, 1117, 1, 0, 0, 0, 1122, 1118, 1, 0, 0, 0, 1122, 1119, 1, 0, 0, 0, 1122, 1120, 1, 0, 0, 0, 1122, 1121, 1, 0, 0, 0, 1123, 233, 1, 0, 0, 0, 1124, 1125, 3, 236, 118, 0, 1125, 235, 1, 0, 0, 0, 1126, 1131, 3, 238, 119, 0, 1127, 1128, 5, 18, 0, 0, 1128, 1130, 3, 238, 119, 0, 1129, 1127, 1, 0, 0, 0, 1130, 1133, 1, 0, 0, 0, 1131, 1129, 1, 0, 0, 0, 1131, 1132, 1, 0, 0, 0, 1132, 237, 1, 0, 0, 0, 1133, 1131, 1, 0, 0, 0, 1134, 1139, 3, 240, 120, 0, 1135, 1136, 5, 19, 0, 0, 1136, 1138, 3, 240, 120, 0, 1137, 1135, 1, 0, 0, 0, 1138, 1141, 1, 0, 0, 0, 1139, 1137, 1, 0, 0, 0, 1139, 1140, 1, 0, 0, 0, 1140, 239, 1, 0, 0, 0, 1141, 1139, 1, 0, 0, 0, 1142, 1143, 3, 242, 121, 0, 1143, 241, 1, 0, 0, 0, 1144, 1162, 3, 244, 122, 0, 1145, 1146, 5, 20, 0, 0, 1146, 1163, 3, 244, 122, 0, 1147, 1148, 5, 21, 0, 0, 1148, 1163, 3, 244, 122, 0, 1149, 1150, 5, 22, 0, 0, 1150, 1163, 3, 244, 122, 0, 1151, 1152, 5, 23, 0, 0, 1152, 1163, 3, 244, 122, 0, 1153, 1154, 5, 24, 0, 0, 1154, 1163, 3, 244, 122, 0, 1155, 1156, 5, 25, 0, 0, 1156, 1163, 3, 244, 122, 0, 1157, 1158, 5, 79, 0, 0, 1158, 1163, 3, 152, 76, 0, 1159, 1160, 5, 78, 0, 0, 1160, 1161, 5, 79, 0, 0, 1161, 1163, 3, 152, 76, 0, 1162, 1145, 1, 0, 0, 0, 1162, 1147, 1, 0, 0, 0, 1162, 1149, 1, 0, 0, 0, 1162, 1151, 1, 0, 0, 0, 1162, 1153, 1, 0, 0, 0, 1162, 1155, 1, 0, 0, 0, 1162, 1157, 1, 0, 0, 0, 1162, 1159, 1, 0, 0, 0, 1162, 1163, 1, 0, 0, 0, 1163, 243, 1, 0, 0, 0, 1164, 1165, 3, 246, 123, 0, 1165, 245, 1, 0, 0, 0, 1166, 1170, 3, 256, 128, 0, 1167, 1169, 3, 248, 124, 0, 1168, 1167, 1, 0, 0, 0, 1169, 1172, 1, 0, 0, 0, 1170, 1168, 1, 0, 0, 0, 1170, 1171, 1, 0, 0, 0, 1171, 247, 1, 0, 0, 0, 1172, 1170, 1, 0, 0, 0, 1173, 1174, 5, 13, 0, 0, 1174, 1179, 3, 250, 125, 0, 1175, 1176, 5, 26, 0, 0, 1176, 1179, 3, 252, 126, 0, 1177, 1179, 3, 254, 127, 0, 1178, 1173, 1, 0, 0, 0, 1178, 1175, 1, 0, 0, 0, 1178, 1177, 1, 0, 0, 0, 1179, 249, 1, 0, 0, 0, 1180, 1181, 3, 256, 128, 0, 1181, 251, 1, 0, 0, 0, 1182, 1183, 3, 256, 128, 0, 1183, 253, 1, 0, 0, 0, 1184, 1187, 3, 294, 147, 0, 1185, 1187, 3, 296, 148, 0, 1186, 1184, 1, 0, 0, 0, 1186, 1185, 1, 0, 0, 0, 1187, 1191, 1, 0, 0, 0, 1188, 1190, 3, 258, 129, 0, 1189, 1188, 1, 0, 0, 0, 1190, 1193, 1, 0, 0, 0, 1191, 1189, 1, 0, 0, 0, 1191, 1192, 1, 0, 0, 0, 1192, 255, 1, 0, 0, 0, 1193, 1191, 1, 0, 0, 0, 1194, 1198, 3, 264, 132, 0, 1195, 1197, 3, 258, 129, 0, 1196, 1195, 1, 0, 0, 0, 1197, 1200, 1, 0, 0, 0, 1198, 1196, 1, 0, 0, 0, 1198, 1199, 1, 0, 0, 0, 1199, 257, 1, 0, 0, 0, 1200, 1198, 1, 0, 0, 0, 1201, 1204, 3, 260, 130, 0, 1202, 1204, 3, 262, 131, 0, 1203, 1201, 1, 0, 0, 0, 1203, 1202, 1, 0, 0, 0, 1204, 259, 1, 0, 0, 0, 1205, 1206, 5, 1, 0, 0, 1206, 1207, 3, 264, 132, 0, 1207, 261, 1, 0, 0, 0, 1208, 1209, 5, 11, 0, 0, 1209, 1210, 3, 264, 132, 0, 1210, 263, 1, 0, 0, 0, 1211, 1212, 5, 15, 0, 0, 1212, 1219, 3, 266, 133, 0, 1213, 1214, 5, 13, 0, 0, 1214, 1219, 3, 266, 133, 0, 1215, 1216, 5, 26, 0, 0, 1216, 1219, 3, 266, 133, 0, 1217, 1219, 3, 266, 133, 0, 1218, 1211, 1, 0, 0, 0, 1218, 1213, 1, 0, 0, 0, 1218, 1215, 1, 0, 0, 0, 1218, 1217, 1, 0, 0, 0, 1219, 265, 1, 0, 0, 0, 1220, 1228, 3, 268, 134, 0, 1221, 1228, 3, 270, 135, 0, 1222, 1228, 3, 286, 143, 0, 1223, 1228, 3, 288, 144, 0, 1224, 1228, 3, 290, 145, 0, 1225, 1228, 3, 298, 149, 0, 1226, 1228, 3, 230, 115, 0, 1227, 1220, 1, 0, 0, 0, 1227, 1221, 1, 0, 0, 0, 1227, 1222, 1, 0, 0, 0, 1227, 1223, 1, 0, 0, 0, 1227, 1224, 1, 0, 0, 0, 1227, 1225, 1, 0, 0, 0, 1227, 1226, 1, 0, 0, 0, 1228, 267, 1, 0, 0, 0, 1229, 1230, 5, 2, 0, 0, 1230, 1231, 3, 234, 117, 0, 1231, 1232, 5, 3, 0, 0, 1232, 269, 1, 0, 0, 0, 1233, 1488, 3, 284, 142, 0, 1234, 1235, 5, 80, 0, 0, 1235, 1236, 5, 2, 0, 0, 1236, 1237, 3, 234, 117, 0, 1237, 1238, 5, 3, 0, 0, 1238, 1488, 1, 0, 0, 0, 1239, 1488, 3, 274, 137, 0, 1240, 1241, 5, 82, 0, 0, 1241, 1242, 5, 2, 0, 0, 1242, 1243, 3, 234, 117, 0, 1243, 1244, 5, 8, 0, 0, 1244, 1245, 3, 234, 117, 0, 1245, 1246, 5, 3, 0, 0, 1246, 1488, 1, 0, 0, 0, 1247, 1248, 5, 83, 0, 0, 1248, 1249, 5, 2, 0, 0, 1249, 1250, 3, 234, 117, 0, 1250, 1251, 5, 3, 0, 0, 1251, 1488, 1, 0, 0, 0, 1252, 1253, 5, 84, 0, 0, 1253, 1254, 5, 2, 0, 0, 1254, 1255, 3, 230, 115, 0, 1255, 1256, 5, 3, 0, 0, 1256, 1488, 1, 0, 0, 0, 1257, 1258, 5, 85, 0, 0, 1258, 1259, 5, 2, 0, 0, 1259, 1260, 3, 234, 117, 0, 1260, 1261, 5, 3, 0, 0, 1261, 1488, 1, 0, 0, 0, 1262, 1263, 5, 86, 0, 0, 1263, 1264, 5, 2, 0, 0, 1264, 1265, 3, 234, 117, 0, 1265, 1266, 5, 3, 0, 0, 1266, 1488, 1, 0, 0, 0, 1267, 1273, 5, 87, 0, 0, 1268, 1269, 5, 2, 0, 0, 1269, 1270, 3, 234, 117, 0, 1270, 1271, 5, 3, 0, 0, 1271, 1274, 1, 0, 0, 0, 1272, 1274, 5, 164, 0, 0, 1273, 1268, 1, 0, 0, 0, 1273, 1272, 1, 0, 0, 0, 1274, 1488, 1, 0, 0, 0, 1275, 1276, 5, 88, 0, 0, 1276, 1488, 5, 164, 0, 0, 1277, 1278, 5, 89, 0, 0, 1278, 1279, 5, 2, 0, 0, 1279, 1280, 3, 234, 117, 0, 1280, 1281, 5, 3, 0, 0, 1281, 1488, 1, 0, 0, 0, 1282, 1283, 5, 90, 0, 0, 1283, 1284, 5, 2, 0, 0, 1284, 1285, 3, 234, 117, 0, 1285, 1286, 5, 3, 0, 0, 1286, 1488, 1, 0, 0, 0, 1287, 1288, 5, 91, 0, 0, 1288, 1289, 5, 2, 0, 0, 1289, 1290, 3, 234, 117, 0, 1290, 1291, 5, 3, 0, 0, 1291, 1488, 1, 0, 0, 0, 1292, 1293, 5, 92, 0, 0, 1293, 1294, 5, 2, 0, 0, 1294, 1295, 3, 234, 117, 0, 1295, 1296, 5, 3, 0, 0, 1296, 1488, 1, 0, 0, 0, 1297, 1298, 5, 93, 0, 0, 1298, 1488, 3, 152, 76, 0, 1299, 1488, 3, 276, 138, 0, 1300, 1301, 5, 94, 0, 0, 1301, 1302, 5, 2, 0, 0, 1302, 1303, 3, 234, 117, 0, 1303, 1304, 5, 3, 0, 0, 1304, 1488, 1, 0, 0, 0, 1305, 1488, 3, 278, 139, 0, 1306, 1307, 5, 95, 0, 0, 1307, 1308, 5, 2, 0, 0, 1308, 1309, 3, 234, 117, 0, 1309, 1310, 5, 3, 0, 0, 1310, 1488, 1, 0, 0, 0, 1311, 1312, 5, 96, 0, 0, 1312, 1313, 5, 2, 0, 0, 1313, 1314, 3, 234, 117, 0, 1314, 1315, 5, 3, 0, 0, 1315, 1488, 1, 0, 0, 0, 1316, 1317, 5, 97, 0, 0, 1317, 1318, 5, 2, 0, 0, 1318, 1319, 3, 234, 117, 0, 1319, 1320, 5, 3, 0, 0, 1320, 1488, 1, 0, 0, 0, 1321, 1322, 5, 99, 0, 0, 1322, 1323, 5, 2, 0, 0, 1323, 1324, 3, 234, 117, 0, 1324, 1325, 5, 8, 0, 0, 1325, 1326, 3, 234, 117, 0, 1326, 1327, 5, 3, 0, 0, 1327, 1488, 1, 0, 0, 0, 1328, 1329, 5, 100, 0, 0, 1329, 1330, 5, 2, 0, 0, 1330, 1331, 3, 234, 117, 0, 1331, 1332, 5, 8, 0, 0, 1332, 1333, 3, 234, 117, 0, 1333, 1334, 5, 3, 0, 0, 1334, 1488, 1, 0, 0, 0, 1335, 1336, 5, 101, 0, 0, 1336, 1337, 5, 2, 0, 0, 1337, 1338, 3, 234, 117, 0, 1338, 1339, 5, 8, 0, 0, 1339, 1340, 3, 234, 117, 0, 1340, 1341, 5, 3, 0, 0, 1341, 1488, 1, 0, 0, 0, 1342, 1343, 5, 102, 0, 0, 1343, 1344, 5, 2, 0, 0, 1344, 1345, 3, 234, 117, 0, 1345, 1346, 5, 8, 0, 0, 1346, 1347, 3, 234, 117, 0, 1347, 1348, 5, 3, 0, 0, 1348, 1488, 1, 0, 0, 0, 1349, 1350, 5, 103, 0, 0, 1350, 1351, 5, 2, 0, 0, 1351, 1352, 3, 234, 117, 0, 1352, 1353, 5, 8, 0, 0, 1353, 1354, 3, 234, 117, 0, 1354, 1355, 5, 3, 0, 0, 1355, 1488, 1, 0, 0, 0, 1356, 1357, 5, 104, 0, 0, 1357, 1358, 5, 2, 0, 0, 1358, 1359, 3, 234, 117, 0, 1359, 1360, 5, 3, 0, 0, 1360, 1488, 1, 0, 0, 0, 1361, 1362, 5, 105, 0, 0, 1362, 1363, 5, 2, 0, 0, 1363, 1364, 3, 234, 117, 0, 1364, 1365, 5, 3, 0, 0, 1365, 1488, 1, 0, 0, 0, 1366, 1367, 5, 106, 0, 0, 1367, 1368, 5, 2, 0, 0, 1368, 1369, 3, 234, 117, 0, 1369, 1370, 5, 3, 0, 0, 1370, 1488, 1, 0, 0, 0, 1371, 1372, 5, 107, 0, 0, 1372, 1373, 5, 2, 0, 0, 1373, 1374, 3, 234, 117, 0, 1374, 1375, 5, 3, 0, 0, 1375, 1488, 1, 0, 0, 0, 1376, 1377, 5, 108, 0, 0, 1377, 1378, 5, 2, 0, 0, 1378, 1379, 3, 234, 117, 0, 1379, 1380, 5, 3, 0, 0, 1380, 1488, 1, 0, 0, 0, 1381, 1382, 5, 109, 0, 0, 1382, 1383, 5, 2, 0, 0, 1383, 1384, 3, 234, 117, 0, 1384, 1385, 5, 3, 0, 0, 1385, 1488, 1, 0, 0, 0, 1386, 1387, 5, 110, 0, 0, 1387, 1388, 5, 2, 0, 0, 1388, 1389, 3, 234, 117, 0, 1389, 1390, 5, 3, 0, 0, 1390, 1488, 1, 0, 0, 0, 1391, 1392, 5, 111, 0, 0, 1392, 1393, 5, 2, 0, 0, 1393, 1394, 3, 234, 117, 0, 1394, 1395, 5, 3, 0, 0, 1395, 1488, 1, 0, 0, 0, 1396, 1397, 5, 112, 0, 0, 1397, 1488, 5, 164, 0, 0, 1398, 1399, 5, 113, 0, 0, 1399, 1488, 5, 164, 0, 0, 1400, 1401, 5, 114, 0, 0, 1401, 1488, 5, 164, 0, 0, 1402, 1403, 5, 119, 0, 0, 1403, 1404, 5, 2, 0, 0, 1404, 1405, 3, 234, 117, 0, 1405, 1406, 5, 3, 0, 0, 1406, 1488, 1, 0, 0, 0, 1407, 1408, 5, 115, 0, 0, 1408, 1409, 5, 2, 0, 0, 1409, 1410, 3, 234, 117, 0, 1410, 1411, 5, 3, 0, 0, 1411, 1488, 1, 0, 0, 0, 1412, 1413, 5, 116, 0, 0, 1413, 1414, 5, 2, 0, 0, 1414, 1415, 3, 234, 117, 0, 1415, 1416, 5, 3, 0, 0, 1416, 1488, 1, 0, 0, 0, 1417, 1418, 5, 117, 0, 0, 1418, 1419, 5, 2, 0, 0, 1419, 1420, 3, 234, 117, 0, 1420, 1421, 5, 3, 0, 0, 1421, 1488, 1, 0, 0, 0, 1422, 1423, 5, 118, 0, 0, 1423, 1424, 5, 2, 0, 0, 1424, 1425, 3, 234, 117, 0, 1425, 1426, 5, 3, 0, 0, 1426, 1488, 1, 0, 0, 0, 1427, 1428, 5, 120, 0, 0, 1428, 1488, 3, 152, 76, 0, 1429, 1430, 5, 121, 0, 0, 1430, 1431, 5, 2, 0, 0, 1431, 1432, 3, 234, 117, 0, 1432, 1433, 5, 8, 0, 0, 1433, 1434, 3, 234, 117, 0, 1434, 1435, 5, 8, 0, 0, 1435, 1436, 3, 234, 117, 0, 1436, 1437, 5, 3, 0, 0, 1437, 1488, 1, 0, 0, 0, 1438, 1439, 5, 122, 0, 0, 1439, 1440, 5, 2, 0, 0, 1440, 1441, 3, 234, 117, 0, 1441, 1442, 5, 8, 0, 0, 1442, 1443, 3, 234, 117, 0, 1443, 1444, 5, 3, 0, 0, 1444, 1488, 1, 0, 0, 0, 1445, 1446, 5, 123, 0, 0, 1446, 1447, 5, 2, 0, 0, 1447, 1448, 3, 234, 117, 0, 1448, 1449, 5, 8, 0, 0, 1449, 1450, 3, 234, 117, 0, 1450, 1451, 5, 3, 0, 0, 1451, 1488, 1, 0, 0, 0, 1452, 1453, 5, 124, 0, 0, 1453, 1454, 5, 2, 0, 0, 1454, 1455, 3, 234, 117, 0, 1455, 1456, 5, 8, 0, 0, 1456, 1457, 3, 234, 117, 0, 1457, 1458, 5, 3, 0, 0, 1458, 1488, 1, 0, 0, 0, 1459, 1460, 5, 125, 0, 0, 1460, 1461, 5, 2, 0, 0, 1461, 1462, 3, 234, 117, 0, 1462, 1463, 5, 3, 0, 0, 1463, 1488, 1, 0, 0, 0, 1464, 1465, 5, 126, 0, 0, 1465, 1466, 5, 2, 0, 0, 1466, 1467, 3, 234, 117, 0, 1467, 1468, 5, 3, 0, 0, 1468, 1488, 1, 0, 0, 0, 1469, 1470, 5, 127, 0, 0, 1470, 1471, 5, 2, 0, 0, 1471, 1472, 3, 234, 117, 0, 1472, 1473, 5, 3, 0, 0, 1473, 1488, 1, 0, 0, 0, 1474, 1475, 5, 128, 0, 0, 1475, 1476, 5, 2, 0, 0, 1476, 1477, 3, 234, 117, 0, 1477, 1478, 5, 3, 0, 0, 1478, 1488, 1, 0, 0, 0, 1479, 1480, 5, 129, 0, 0, 1480, 1481, 5, 2, 0, 0, 1481, 1482, 3, 234, 117, 0, 1482, 1483, 5, 3, 0, 0, 1483, 1488, 1, 0, 0, 0, 1484, 1488, 3, 272, 136, 0, 1485, 1488, 3, 280, 140, 0, 1486, 1488, 3, 282, 141, 0, 1487, 1233, 1, 0, 0, 0, 1487, 1234, 1, 0, 0, 0, 1487, 1239, 1, 0, 0, 0, 1487, 1240, 1, 0, 0, 0, 1487, 1247, 1, 0, 0, 0, 1487, 1252, 1, 0, 0, 0, 1487, 1257, 1, 0, 0, 0, 1487, 1262, 1, 0, 0, 0, 1487, 1267, 1, 0, 0, 0, 1487, 1275, 1, 0, 0, 0, 1487, 1277, 1, 0, 0, 0, 1487, 1282, 1, 0, 0, 0, 1487, 1287, 1, 0, 0, 0, 1487, 1292, 1, 0, 0, 0, 1487, 1297, 1, 0, 0, 0, 1487, 1299, 1, 0, 0, 0, 1487, 1300, 1, 0, 0, 0, 1487, 1305, 1, 0, 0, 0, 1487, 1306, 1, 0, 0, 0, 1487, 1311, 1, 0, 0, 0, 1487, 1316, 1, 0, 0, 0, 1487, 1321, 1, 0, 0, 0, 1487, 1328, 1, 0, 0, 0, 1487, 1335, 1, 0, 0, 0, 1487, 1342, 1, 0, 0, 0, 1487, 1349, 1, 0, 0, 0, 1487, 1356, 1, 0, 0, 0, 1487, 1361, 1, 0, 0, 0, 1487, 1366, 1, 0, 0, 0, 1487, 1371, 1, 0, 0, 0, 1487, 1376, 1, 0, 0, 0, 1487, 1381, 1, 0, 0, 0, 1487, 1386, 1, 0, 0, 0, 1487, 1391, 1, 0, 0, 0, 1487, 1396, 1, 0, 0, 0, 1487, 1398, 1, 0, 0, 0, 1487, 1400, 1, 0, 0, 0, 1487, 1402, 1, 0, 0, 0, 1487, 1407, 1, 0, 0, 0, 1487, 1412, 1, 0, 0, 0, 1487, 1417, 1, 0, 0, 0, 1487, 1422, 1, 0, 0, 0, 1487, 1427, 1, 0, 0, 0, 1487, 1429, 1, 0, 0, 0, 1487, 1438, 1, 0, 0, 0, 1487, 1445, 1, 0, 0, 0, 1487, 1452, 1, 0, 0, 0, 1487, 1459, 1, 0, 0, 0, 1487, 1464, 1, 0, 0, 0, 1487, 1469, 1, 0, 0, 0, 1487, 1474, 1, 0, 0, 0, 1487, 1479, 1, 0, 0, 0, 1487, 1484, 1, 0, 0, 0, 1487, 1485, 1, 0, 0, 0, 1487, 1486, 1, 0, 0, 0, 1488, 271, 1, 0, 0, 0, 1489, 1490, 5, 130, 0, 0, 1490, 1491, 5, 2, 0, 0, 1491, 1492, 3, 234, 117, 0, 1492, 1493, 5, 8, 0, 0, 1493, 1496, 3, 234, 117, 0, 1494, 1495, 5, 8, 0, 0, 1495, 1497, 3, 234, 117, 0, 1496, 1494, 1, 0, 0, 0, 1496, 1497, 1, 0, 0, 0, 1497, 1498, 1, 0, 0, 0, 1498, 1499, 5, 3, 0, 0, 1499, 273, 1, 0, 0, 0, 1500, 1501, 5, 81, 0, 0, 1501, 1502, 5, 2, 0, 0, 1502, 1503, 3, 234, 117, 0, 1503, 1504, 5, 3, 0, 0, 1504, 275, 1, 0, 0, 0, 1505, 1506, 5, 131, 0, 0, 1506, 1507, 5, 2, 0, 0, 1507, 1508, 3, 234, 117, 0, 1508, 1509, 5, 8, 0, 0, 1509, 1512, 3, 234, 117, 0, 1510, 1511, 5, 8, 0, 0, 1511, 1513, 3, 234, 117, 0, 1512, 1510, 1, 0, 0, 0, 1512, 1513, 1, 0, 0, 0, 1513, 1514, 1, 0, 0, 0, 1514, 1515, 5, 3, 0, 0, 1515, 277, 1, 0, 0, 0, 1516, 1517, 5, 132, 0, 0, 1517, 1518, 5, 2, 0, 0, 1518, 1519, 3, 234, 117, 0, 1519, 1520, 5, 8, 0, 0, 1520, 1521, 3, 234, 117, 0, 1521, 1522, 5, 8, 0, 0, 1522, 1525, 3, 234, 117, 0, 1523, 1524, 5, 8, 0, 0, 1524, 1526, 3, 234, 117, 0, 1525, 1523, 1, 0, 0, 0, 1525, 1526, 1, 0, 0, 0, 1526, 1527, 1, 0, 0, 0, 1527, 1528, 5, 3, 0, 0, 1528, 279, 1, 0, 0, 0, 1529, 1530, 5, 133, 0, 0, 1530, 1531, 3, 110, 55, 0, 1531, 281, 1, 0, 0, 0, 1532, 1533, 5, 78, 0, 0, 1533, 1534, 5, 133, 0, 0, 1534, 1535, 3, 110, 55, 0, 1535, 283, 1, 0, 0, 0, 1536, 1537, 5, 134, 0, 0, 1537, 1539, 5, 2, 0, 0, 1538, 1540, 5, 33, 0, 0, 1539, 1538, 1, 0, 0, 0, 1539, 1540, 1, 0, 0, 0, 1540, 1543, 1, 0, 0, 0, 1541, 1544, 5, 1, 0, 0, 1542, 1544, 3, 234, 117, 0, 1543, 1541, 1, 0, 0, 0, 1543, 1542, 1, 0, 0, 0, 1544, 1545, 1, 0, 0, 0, 1545, 1601, 5, 3, 0, 0, 1546, 1547, 5, 135, 0, 0, 1547, 1549, 5, 2, 0, 0, 1548, 1550, 5, 33, 0, 0, 1549, 1548, 1, 0, 0, 0, 1549, 1550, 1, 0, 0, 0, 1550, 1551, 1, 0, 0, 0, 1551, 1552, 3, 234, 117, 0, 1552, 1553, 5, 3, 0, 0, 1553, 1601, 1, 0, 0, 0, 1554, 1555, 5, 136, 0, 0, 1555, 1557, 5, 2, 0, 0, 1556, 1558, 5, 33, 0, 0, 1557, 1556, 1, 0, 0, 0, 1557, 1558, 1, 0, 0, 0, 1558, 1559, 1, 0, 0, 0, 1559, 1560, 3, 234, 117, 0, 1560, 1561, 5, 3, 0, 0, 1561, 1601, 1, 0, 0, 0, 1562, 1563, 5, 137, 0, 0, 1563, 1565, 5, 2, 0, 0, 1564, 1566, 5, 33, 0, 0, 1565, 1564, 1, 0, 0, 0, 1565, 1566, 1, 0, 0, 0, 1566, 1567, 1, 0, 0, 0, 1567, 1568, 3, 234, 117, 0, 1568, 1569, 5, 3, 0, 0, 1569, 1601, 1, 0, 0, 0, 1570, 1571, 5, 138, 0, 0, 1571, 1573, 5, 2, 0, 0, 1572, 1574, 5, 33, 0, 0, 1573, 1572, 1, 0, 0, 0, 1573, 1574, 1, 0, 0, 0, 1574, 1575, 1, 0, 0, 0, 1575, 1576, 3, 234, 117, 0, 1576, 1577, 5, 3, 0, 0, 1577, 1601, 1, 0, 0, 0, 1578, 1579, 5, 139, 0, 0, 1579, 1581, 5, 2, 0, 0, 1580, 1582, 5, 33, 0, 0, 1581, 1580, 1, 0, 0, 0, 1581, 1582, 1, 0, 0, 0, 1582, 1583, 1, 0, 0, 0, 1583, 1584, 3, 234, 117, 0, 1584, 1585, 5, 3, 0, 0, 1585, 1601, 1, 0, 0, 0, 1586, 1587, 5, 43, 0, 0, 1587, 1589, 5, 2, 0, 0, 1588, 1590, 5, 33, 0, 0, 1589, 1588, 1, 0, 0, 0, 1589, 1590, 1, 0, 0, 0, 1590, 1591, 1, 0, 0, 0, 1591, 1596, 3, 234, 117, 0, 1592, 1593, 5, 6, 0, 0, 1593, 1594, 5, 140, 0, 0, 1594, 1595, 5, 20, 0, 0, 1595, 1597, 3, 300, 150, 0, 1596, 1592, 1, 0, 0, 0, 1596, 1597, 1, 0, 0, 0, 1597, 1598, 1, 0, 0, 0, 1598, 1599, 5, 3, 0, 0, 1599, 1601, 1, 0, 0, 0, 1600, 1536, 1, 0, 0, 0, 1600, 1546, 1, 0, 0, 0, 1600, 1554, 1, 0, 0, 0, 1600, 1562, 1, 0, 0, 0, 1600, 1570, 1, 0, 0, 0, 1600, 1578, 1, 0, 0, 0, 1600, 1586, 1, 0, 0, 0, 1601, 285, 1, 0, 0, 0, 1602, 1604, 3, 302, 151, 0, 1603, 1605, 3, 150, 75, 0, 1604, 1603, 1, 0, 0, 0, 1604, 1605, 1, 0, 0, 0, 1605, 287, 1, 0, 0, 0, 1606, 1610, 3, 300, 150, 0, 1607, 1611, 5, 147, 0, 0, 1608, 1609, 5, 27, 0, 0, 1609, 1611, 3, 302, 151, 0, 1610, 1607, 1, 0, 0, 0, 1610, 1608, 1, 0, 0, 0, 1610, 1611, 1, 0, 0, 0, 1611, 289, 1, 0, 0, 0, 1612, 1616, 3, 292, 146, 0, 1613, 1616, 3, 294, 147, 0, 1614, 1616, 3, 296, 148, 0, 1615, 1612, 1, 0, 0, 0, 1615, 1613, 1, 0, 0, 0, 1615, 1614, 1, 0, 0, 0, 1616, 291, 1, 0, 0, 0, 1617, 1618, 7, 4, 0, 0, 1618, 293, 1, 0, 0, 0, 1619, 1620, 7, 5, 0, 0, 1620, 295, 1, 0, 0, 0, 1621, 1622, 7, 6, 0, 0, 1622, 297, 1, 0, 0, 0, 1623, 1624, 7, 7, 0, 0, 1624, 299, 1, 0, 0, 0, 1625, 1626, 7, 8, 0, 0, 1626, 301, 1, 0, 0, 0, 1627, 1629, 5, 148, 0, 0, 1628, 1627, 1, 0, 0, 0, 1628, 1629, 1, 0, 0, 0, 1629, 1632, 1, 0, 0, 0, 1630, 1633, 3, 308, 154, 0, 1631, 1633, 3, 304, 152, 0, 1632, 1630, 1, 0, 0, 0, 1632, 1631, 1, 0, 0, 0, 1633, 303, 1, 0, 0, 0, 1634, 1637, 3, 310, 155, 0, 1635, 1637, 3, 312, 156, 0, 1636, 1634, 1, 0, 0, 0, 1636, 1635, 1, 0, 0, 0, 1637, 305, 1, 0, 0, 0, 1638, 1639, 7, 9, 0, 0, 1639, 307, 1, 0, 0, 0, 1640, 1641, 5, 141, 0, 0, 1641, 309, 1, 0, 0, 0, 1642, 1643, 5, 143, 0, 0, 1643, 311, 1, 0, 0, 0, 1644, 1645, 5, 142, 0, 0, 1645, 313, 1, 0, 0, 0, 161, 316, 325, 331, 333, 347, 360, 365, 368, 372, 387, 396, 402, 406, 412, 415, 420, 424, 432, 441, 451, 456, 459, 462, 465, 471, 479, 484, 490, 496, 501, 507, 509, 513, 516, 520, 523, 527, 530, 534, 537, 541, 544, 548, 551, 553, 566, 572, 574, 587, 591, 596, 600, 606, 612, 618, 626, 634, 654, 658, 661, 666, 682, 687, 696, 707, 711, 714, 718, 725, 732, 734, 739, 744, 749, 754, 757, 762, 764, 774, 785, 802, 809, 819, 823, 829, 838, 843, 850, 860, 869, 877, 884, 889, 898, 903, 907, 914, 916, 924, 927, 935, 939, 944, 951, 962, 965, 970, 974, 989, 996, 1008, 1016, 1021, 1026, 1038, 1047, 1050, 1053, 1060, 1062, 1068, 1076, 1086, 1094, 1100, 1104, 1108, 1112, 1122, 1131, 1139, 1162, 1170, 1178, 1186, 1191, 1198, 1203, 1218, 1227, 1273, 1487, 1496, 1512, 1525, 1539, 1543, 1549, 1557, 1565, 1573, 1581, 1589, 1596, 1600, 1604, 1610, 1615, 1628, 1632, 1636] \ No newline at end of file diff --git a/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp b/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp index 49cc4e504..7468aee71 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp +++ b/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp @@ -994,7 +994,7 @@ void sparqlautomaticParserInitialize() { 5, 65, 0, 0, 673, 674, 3, 100, 50, 0, 674, 89, 1, 0, 0, 0, 675, 676, 5, 64, 0, 0, 676, 677, 3, 100, 50, 0, 677, 91, 1, 0, 0, 0, 678, 682, - 5, 67, 0, 0, 679, 683, 5, 85, 0, 0, 680, 681, + 5, 67, 0, 0, 679, 683, 3, 302, 151, 0, 680, 681, 5, 41, 0, 0, 681, 683, 3, 302, 151, 0, 682, 679, 1, 0, 0, 0, 682, 680, 1, 0, 0, 0, 683, 93, 1, 0, 0, 0, 684, 688, 5, 68, 0, 0, 685, 686, @@ -6236,19 +6236,15 @@ tree::TerminalNode* SparqlAutomaticParser::UsingClauseContext::USING() { return getToken(SparqlAutomaticParser::USING, 0); } -tree::TerminalNode* SparqlAutomaticParser::UsingClauseContext::IRI() { - return getToken(SparqlAutomaticParser::IRI, 0); +SparqlAutomaticParser::IriContext* +SparqlAutomaticParser::UsingClauseContext::iri() { + return getRuleContext(0); } tree::TerminalNode* SparqlAutomaticParser::UsingClauseContext::NAMED() { return getToken(SparqlAutomaticParser::NAMED, 0); } -SparqlAutomaticParser::IriContext* -SparqlAutomaticParser::UsingClauseContext::iri() { - return getRuleContext(0); -} - size_t SparqlAutomaticParser::UsingClauseContext::getRuleIndex() const { return SparqlAutomaticParser::RuleUsingClause; } @@ -6293,9 +6289,12 @@ SparqlAutomaticParser::usingClause() { setState(682); _errHandler->sync(this); switch (_input->LA(1)) { - case SparqlAutomaticParser::IRI: { + case SparqlAutomaticParser::IRI_REF: + case SparqlAutomaticParser::PNAME_NS: + case SparqlAutomaticParser::PNAME_LN: + case SparqlAutomaticParser::PREFIX_LANGTAG: { setState(679); - match(SparqlAutomaticParser::IRI); + iri(); break; } diff --git a/src/parser/sparqlParser/generated/SparqlAutomaticParser.h b/src/parser/sparqlParser/generated/SparqlAutomaticParser.h index e89aba231..9e2449ee6 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomaticParser.h +++ b/src/parser/sparqlParser/generated/SparqlAutomaticParser.h @@ -1313,9 +1313,8 @@ class SparqlAutomaticParser : public antlr4::Parser { UsingClauseContext(antlr4::ParserRuleContext* parent, size_t invokingState); virtual size_t getRuleIndex() const override; antlr4::tree::TerminalNode* USING(); - antlr4::tree::TerminalNode* IRI(); - antlr4::tree::TerminalNode* NAMED(); IriContext* iri(); + antlr4::tree::TerminalNode* NAMED(); virtual void enterRule(antlr4::tree::ParseTreeListener* listener) override; virtual void exitRule(antlr4::tree::ParseTreeListener* listener) override; diff --git a/test/SparqlAntlrParserTest.cpp b/test/SparqlAntlrParserTest.cpp index ba180406f..e19a6a2d3 100644 --- a/test/SparqlAntlrParserTest.cpp +++ b/test/SparqlAntlrParserTest.cpp @@ -1058,14 +1058,10 @@ TEST(SparqlParser, SelectQuery) { }; expectSelectQuery("SELECT * WHERE { ?a ?foo }", selectABarFooMatcher()); - Graphs defaultGraphs; - defaultGraphs.emplace(); - defaultGraphs->insert(TripleComponent::Iri::fromIriref("")); - Graphs namedGraphs; - namedGraphs.emplace(); - namedGraphs->insert(TripleComponent::Iri::fromIriref("")); - expectSelectQuery("SELECT * FROM FROM NAMED WHERE { ?a ?foo }", - selectABarFooMatcher(defaultGraphs, namedGraphs)); + expectSelectQuery( + "SELECT * FROM FROM NAMED WHERE { ?a ?foo }", + selectABarFooMatcher(m::Graphs{TripleComponent::Iri::fromIriref("")}, + m::Graphs{TripleComponent::Iri::fromIriref("")})); expectSelectQuery("SELECT * WHERE { ?x ?y ?z }", testing::AllOf(m::SelectQuery(m::AsteriskSelect(), @@ -1240,11 +1236,10 @@ TEST(SparqlParser, ConstructQuery) { m::ConstructQuery({{Var{"?a"}, Iri{""}, Var{"?b"}}}, m::GraphPattern())); // CONSTRUCT with datasets. - using Graphs = ad_utility::HashSet; expectConstructQuery( "CONSTRUCT { } FROM FROM NAMED FROM NAMED WHERE { }", - m::ConstructQuery({}, m::GraphPattern(), Graphs{iri("")}, - Graphs{iri(""), iri("")})); + m::ConstructQuery({}, m::GraphPattern(), m::Graphs{iri("")}, + m::Graphs{iri(""), iri("")})); // GROUP BY and ORDER BY, but the ordered variable is not grouped expectConstructQueryFails( "CONSTRUCT {?a } WHERE { ?a ?b ?c } GROUP BY ?a ORDER BY ?b", @@ -1949,6 +1944,65 @@ TEST(SparqlParser, updateQueryUnsupported) { expectUpdateFails("COPY GRAPH TO GRAPH ", updateUnsupported); } +TEST(SparqlParser, Quads) { + auto expectQuads = ExpectCompleteParse<&Parser::quads>{defaultPrefixMap}; + auto expectQuadsFails = ExpectParseFails<&Parser::quads>{}; + auto Iri = [](std::string_view stringWithBrackets) { + return TripleComponent::Iri::fromIriref(stringWithBrackets); + }; + + expectQuads("?a ", + UnorderedElementsAre(m::Quad(Var("?a"), Iri(""), Iri(""), + std::monostate{}))); + expectQuads("GRAPH { ?a }", + UnorderedElementsAre( + m::Quad(Var("?a"), Iri(""), Iri(""), ::Iri("")))); + expectQuads("GRAPH { ?a } GRAPH { ?f }", + UnorderedElementsAre( + m::Quad(Var("?a"), Iri(""), Iri(""), ::Iri("")), + m::Quad(Iri(""), Iri(""), Var("?f"), ::Iri("")))); + expectQuads( + "GRAPH { ?a } . . ", + UnorderedElementsAre( + m::Quad(Var("?a"), Iri(""), Iri(""), ::Iri("")), + m::Quad(Iri(""), Iri(""), Iri(""), std::monostate{}), + m::Quad(Iri(""), Iri(""), Iri(""), std::monostate{}))); + expectQuads( + "GRAPH { ?a } . . GRAPH { " + " }", + UnorderedElementsAre( + m::Quad(Var("?a"), Iri(""), Iri(""), ::Iri("")), + m::Quad(Iri(""), Iri(""), Iri(""), std::monostate{}), + m::Quad(Iri(""), Iri(""), Iri(""), std::monostate{}), + m::Quad(Iri(""), Iri(""), Iri(""), ::Iri("")))); + + expectQuads( + "GRAPH { ?a } . . . GRAPH { " + " }", + UnorderedElementsAre( + m::Quad(Var("?a"), Iri(""), Iri(""), ::Iri("")), + m::Quad(Iri(""), Iri(""), Iri(""), std::monostate{}), + m::Quad(Iri(""), Iri(""), Iri(""), std::monostate{}), + m::Quad(Iri(""), Iri(""), Iri(""), ::Iri("")))); +} + +TEST(SparqlParser, QuadData) { + auto expectQuadData = + ExpectCompleteParse<&Parser::quadData>{defaultPrefixMap}; + auto expectQuadDataFails = ExpectParseFails<&Parser::quadData>{}; + auto Iri = [](std::string_view stringWithBrackets) { + return TripleComponent::Iri::fromIriref(stringWithBrackets); + }; + + expectQuadData("{ }", + ElementsAre(m::Quad(Iri(""), Iri(""), Iri(""), + std::monostate{}))); + expectQuadDataFails("{ ?c }"); + expectQuadDataFails("{ . GRAPH { ?e } }"); + expectQuadDataFails("{ . ?d } }"); + expectQuadDataFails("{ GRAPH ?foo { } }"); +} + TEST(SparqlParser, UpdateQuery) { auto expectUpdate = ExpectCompleteParse<&Parser::update>{defaultPrefixMap}; auto expectUpdateFails = ExpectParseFails<&Parser::update>{}; @@ -1958,56 +2012,121 @@ TEST(SparqlParser, UpdateQuery) { auto Literal = [](std::string s) { return TripleComponent::Literal::fromStringRepresentation(std::move(s)); }; + auto noGraph = std::monostate{}; - expectUpdate("INSERT DATA { }", - m::UpdateQuery({}, {{Iri(""), Iri(""), Iri("")}}, - m::GraphPattern())); + expectUpdate( + "INSERT DATA { }", + m::UpdateClause( + m::GraphUpdate({}, {{Iri(""), Iri(""), Iri(""), noGraph}}, + std::nullopt), + m::GraphPattern())); expectUpdate( "INSERT DATA { \"foo:bar\" }", - m::UpdateQuery({}, {{Iri(""), Iri(""), Literal("\"foo:bar\"")}}, - m::GraphPattern())); - expectUpdate("DELETE DATA { }", - m::UpdateQuery({{Iri(""), Iri(""), Iri("")}}, {}, - m::GraphPattern())); + m::UpdateClause( + m::GraphUpdate( + {}, {{Iri(""), Iri(""), Literal("\"foo:bar\""), noGraph}}, + std::nullopt), + m::GraphPattern())); + expectUpdate( + "DELETE DATA { }", + m::UpdateClause( + m::GraphUpdate({{Iri(""), Iri(""), Iri(""), noGraph}}, {}, + std::nullopt), + m::GraphPattern())); expectUpdate( "DELETE { ?a } WHERE { ?a }", - m::UpdateQuery( - {{Var("?a"), Iri(""), Iri("")}}, {}, + m::UpdateClause( + m::GraphUpdate({{Var("?a"), Iri(""), Iri(""), noGraph}}, {}, + std::nullopt), m::GraphPattern(m::Triples({{Iri(""), "", Var{"?a"}}})))); + expectUpdateFails("DELETE { ?a } WHERE { ?b ?c }"); expectUpdate( "DELETE { ?a } INSERT { ?a } WHERE { ?a }", - m::UpdateQuery( - {{Var("?a"), Iri(""), Iri("")}}, - {{Iri(""), Var("?a"), Iri("")}}, + m::UpdateClause( + m::GraphUpdate({{Var("?a"), Iri(""), Iri(""), noGraph}}, + {{Iri(""), Var("?a"), Iri(""), noGraph}}, + std::nullopt), m::GraphPattern(m::Triples({{Iri(""), "", Var{"?a"}}})))); expectUpdate( "DELETE WHERE { ?a ?c }", - m::UpdateQuery( - {{Var("?a"), Iri(""), Var("?c")}}, {}, + m::UpdateClause( + m::GraphUpdate({{Var("?a"), Iri(""), Var("?c"), noGraph}}, {}, + std::nullopt), m::GraphPattern(m::Triples({{Var{"?a"}, "", Var{"?c"}}})))); expectUpdate("CLEAR DEFAULT", - m::UpdateQuery({{Var("?s"), Var("?p"), Var("?o")}}, {}, - m::GraphPattern( - m::Triples({{Var("?s"), "?p", Var("?o")}})))); - expectUpdateFails("INSERT DATA { ?a ?b ?c }"); - expectUpdateFails("WITH DELETE { ?a ?b ?c } WHERE { ?a ?b ?c }"); - expectUpdateFails("DELETE { ?a ?b ?c } USING WHERE { ?a ?b ?c }"); - expectUpdateFails("INSERT DATA { GRAPH { } }"); - // Unsupported features. + m::UpdateClause(m::Clear(false, DEFAULT{}), m::GraphPattern())); + expectUpdateFails("INSERT DATA { ?a ?b ?c }"); // Variables are not allowed + // inside INSERT DATA. + expectUpdate( + "WITH DELETE { ?a ?b ?c } WHERE { ?a ?b ?c }", + m::UpdateClause( + m::GraphUpdate({{Var("?a"), Var("?b"), Var("?c"), noGraph}}, {}, + Iri("")), + m::GraphPattern(m::Triples({{Var{"?a"}, "?b", Var{"?c"}}})))); + expectUpdate( + "DELETE { ?a ?b ?c } USING WHERE { ?a ?b ?c }", + m::UpdateClause( + m::GraphUpdate({{Var("?a"), Var("?b"), Var("?c"), noGraph}}, {}, + std::nullopt), + m::GraphPattern(m::Triples({{Var{"?a"}, "?b", Var{"?c"}}})), + m::datasetClausesMatcher(m::Graphs{TripleComponent(Iri(""))}, + std::nullopt))); + expectUpdate( + "INSERT DATA { GRAPH { } }", + m::UpdateClause(m::GraphUpdate({}, {}, std::nullopt), m::GraphPattern())); + expectUpdate( + "INSERT DATA { GRAPH { } }", + m::UpdateClause( + m::GraphUpdate({}, + {{Iri(""), Iri(""), Iri(""), ::Iri("")}}, + std::nullopt), + m::GraphPattern())); + expectUpdate( + "INSERT DATA { GRAPH ?f { } }", + m::UpdateClause(m::GraphUpdate({}, {}, std::nullopt), m::GraphPattern())); + expectUpdate( + "DELETE { ?a } USING NAMED WHERE { ?a }", + m::UpdateClause( + m::GraphUpdate({{Var("?a"), Iri(""), Iri(""), noGraph}}, {}, + std::nullopt), + m::GraphPattern(m::Triples({{Iri(""), "", Var{"?a"}}})), + m::datasetClausesMatcher(std::nullopt, + m::Graphs{TripleComponent(Iri(""))}))); + expectUpdate( + "WITH DELETE { ?a } WHERE { ?a }", + m::UpdateClause( + m::GraphUpdate({{Var("?a"), Iri(""), Iri(""), noGraph}}, {}, + Iri("")), + m::GraphPattern(m::Triples({{Iri(""), "", Var{"?a"}}})))); + // Chaining multiple updates into one query is not supported. expectUpdateFails( "INSERT DATA { } ; INSERT { ?a } WHERE { ?a " "}"); - expectUpdateFails("LOAD "); - expectUpdateFails("CLEAR NAMED"); - expectUpdateFails("CLEAR GRAPH "); - expectUpdateFails("CREATE GRAPH "); - expectUpdateFails("DROP GRAPH "); - expectUpdateFails("MOVE GRAPH TO DEFAULT"); - expectUpdateFails("ADD DEFAULT TO GRAPH "); - expectUpdateFails("COPY DEFAULT TO GRAPH "); - expectUpdateFails( - "DELETE { ?a } USING NAMED WHERE { ?a }"); - expectUpdateFails("WITH DELETE { ?a } WHERE { ?a }"); + expectUpdate("LOAD ", + m::UpdateClause(m::Load(false, Iri(""), std::nullopt), + m::GraphPattern())); + expectUpdate("LOAD SILENT into GRAPH ", + m::UpdateClause(m::Load(true, Iri(""), Iri("")), + m::GraphPattern())); + expectUpdate("CLEAR NAMED", + m::UpdateClause(m::Clear(false, NAMED{}), m::GraphPattern())); + expectUpdate( + "CLEAR GRAPH ", + m::UpdateClause(m::Clear(false, Iri("")), m::GraphPattern())); + expectUpdate("DROP GRAPH ", m::UpdateClause(m::Drop(false, Iri("")), + m::GraphPattern())); + expectUpdate( + "CREATE GRAPH ", + m::UpdateClause(m::Create(false, Iri("")), m::GraphPattern())); + expectUpdate("ADD SILENT DEFAULT TO GRAPH ", + m::UpdateClause(m::Add(true, DEFAULT{}, Iri("")), + m::GraphPattern())); + expectUpdate("MOVE GRAPH TO DEFAULT", + m::UpdateClause(m::Move(false, Iri(""), DEFAULT{}), + m::GraphPattern())); + expectUpdate("COPY DEFAULT TO GRAPH ", + m::UpdateClause(m::Copy(false, DEFAULT{}, Iri("")), + m::GraphPattern())); } TEST(SparqlParser, EmptyQuery) { diff --git a/test/SparqlAntlrParserTestHelpers.h b/test/SparqlAntlrParserTestHelpers.h index 942d6a345..788ece3fb 100644 --- a/test/SparqlAntlrParserTestHelpers.h +++ b/test/SparqlAntlrParserTestHelpers.h @@ -888,19 +888,99 @@ inline auto VisibleVariables = return AD_PROPERTY(ParsedQuery, getVisibleVariables, testing::Eq(elems)); }; -inline auto UpdateQuery = - [](const std::vector& toDelete, - const std::vector& toInsert, - const Matcher& graphPatternMatcher) - -> Matcher { +using namespace updateClause; + +inline auto Load = [](bool silent, + const ad_utility::triple_component::Iri& source, + const std::optional& target) + -> Matcher { + return testing::VariantWith( + testing::AllOf(AD_FIELD(Load, silent_, testing::Eq(silent)), + AD_FIELD(Load, source_, testing::Eq(source)), + AD_FIELD(Load, target_, testing::Eq(target)))); +}; + +inline auto Clear = + [](bool silent, + const GraphRefAll& target) -> Matcher { + return testing::VariantWith( + testing::AllOf(AD_FIELD(Clear, silent_, testing::Eq(silent)), + AD_FIELD(Clear, target_, testing::Eq(target)))); +}; + +inline auto Drop = + [](bool silent, + const GraphRefAll& target) -> Matcher { + return testing::VariantWith( + testing::AllOf(AD_FIELD(Drop, silent_, testing::Eq(silent)), + AD_FIELD(Drop, target_, testing::Eq(target)))); +}; + +inline auto Create = + [](bool silent, + const GraphRef& target) -> Matcher { + return testing::VariantWith( + testing::AllOf(AD_FIELD(Create, silent_, testing::Eq(silent)), + AD_FIELD(Create, target_, testing::Eq(target)))); +}; + +inline auto Add = [](bool silent, const GraphOrDefault& source, + const GraphOrDefault& target) + -> Matcher { + return testing::VariantWith( + testing::AllOf(AD_FIELD(Add, silent_, testing::Eq(silent)), + AD_FIELD(Add, source_, testing::Eq(source)), + AD_FIELD(Add, target_, testing::Eq(target)))); +}; + +inline auto Move = [](bool silent, const GraphOrDefault& source, + const GraphOrDefault& target) + -> Matcher { + return testing::VariantWith( + testing::AllOf(AD_FIELD(Move, silent_, testing::Eq(silent)), + AD_FIELD(Move, source_, testing::Eq(source)), + AD_FIELD(Move, target_, testing::Eq(target)))); +}; + +inline auto Copy = [](bool silent, const GraphOrDefault& source, + const GraphOrDefault& target) + -> Matcher { + return testing::VariantWith( + testing::AllOf(AD_FIELD(Copy, silent_, testing::Eq(silent)), + AD_FIELD(Copy, source_, testing::Eq(source)), + AD_FIELD(Copy, target_, testing::Eq(target)))); +}; + +inline auto GraphUpdate = + [](const std::vector& toDelete, + const std::vector& toInsert, + const std::optional& with) + -> Matcher { + return testing::VariantWith(testing::AllOf( + AD_FIELD(GraphUpdate, toInsert_, testing::ElementsAreArray(toInsert)), + AD_FIELD(GraphUpdate, toDelete_, testing::ElementsAreArray(toDelete)), + AD_FIELD(GraphUpdate, with_, testing::Eq(with)))); +}; + +inline auto EmptyDatasets = [] { + return AllOf(AD_FIELD(ParsedQuery::DatasetClauses, defaultGraphs_, + testing::Eq(std::nullopt)), + AD_FIELD(ParsedQuery::DatasetClauses, namedGraphs_, + testing::Eq(std::nullopt))); +}; + +using Graphs = ad_utility::HashSet; + +inline auto UpdateClause = + [](const Matcher& opMatcher, + const Matcher& graphPatternMatcher, + const Matcher& datasetMatcher = + EmptyDatasets()) -> Matcher { return testing::AllOf( AD_PROPERTY(ParsedQuery, hasUpdateClause, testing::IsTrue()), AD_PROPERTY(ParsedQuery, updateClause, - AD_FIELD(parsedQuery::UpdateClause, toDelete_, - testing::ElementsAreArray(toDelete))), - AD_PROPERTY(ParsedQuery, updateClause, - AD_FIELD(parsedQuery::UpdateClause, toInsert_, - testing::ElementsAreArray(toInsert))), + AD_FIELD(parsedQuery::UpdateClause, op_, opMatcher)), + AD_FIELD(ParsedQuery, datasetClauses_, datasetMatcher), RootGraphPattern(graphPatternMatcher)); }; @@ -912,4 +992,14 @@ auto inline GraphRefIri = [](const string& iri) { TripleComponent::Iri, toStringRepresentation, testing::Eq(iri))); }; +inline auto Quad = [](const TripleComponent& s, const TripleComponent& p, + const TripleComponent& o, + const SparqlTripleSimpleWithGraph::Graph& g) { + return testing::AllOf( + AD_FIELD(SparqlTripleSimpleWithGraph, s_, testing::Eq(s)), + AD_FIELD(SparqlTripleSimpleWithGraph, p_, testing::Eq(p)), + AD_FIELD(SparqlTripleSimpleWithGraph, o_, testing::Eq(o)), + AD_FIELD(SparqlTripleSimpleWithGraph, g_, testing::Eq(g))); +}; + } // namespace matchers From d26a9b703da4a9f019235825c4c979047ec81da3 Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Sat, 26 Oct 2024 09:03:47 +0200 Subject: [PATCH 3/7] Prevent memory allocation errors because of caching for lazy results (#1578) Lazy operations materialize their result up to a certain threshold to store it in the cache if it is sufficiently small. If these thresholds were configured strangely (in particular close to the total memory limit of the engine) it could happen, that a lazy operation failed with an out-of-memory error because of that caching mechanism. This commit fixes this unwanted behavior: As soon as the caching encounters an out-of-memorry error, it simply discards the materialized partial result for the chache, but the lazy execution continues unharmed. --- src/engine/Result.cpp | 23 +++++++++++++++-------- test/ResultTest.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/engine/Result.cpp b/src/engine/Result.cpp index 292a62069..c4b9137d4 100644 --- a/src/engine/Result.cpp +++ b/src/engine/Result.cpp @@ -279,14 +279,21 @@ void Result::cacheDuringConsumption( const IdTableVocabPair& newTablePair) { bool doBothFitInCache = fitInCache(aggregate, newTablePair); if (doBothFitInCache) { - if (aggregate.has_value()) { - auto& value = aggregate.value(); - value.idTable_.insertAtEnd(newTablePair.idTable_); - value.localVocab_.mergeWith( - std::span{&newTablePair.localVocab_, 1}); - } else { - aggregate.emplace(newTablePair.idTable_.clone(), - newTablePair.localVocab_.clone()); + try { + if (aggregate.has_value()) { + auto& value = aggregate.value(); + value.idTable_.insertAtEnd(newTablePair.idTable_); + value.localVocab_.mergeWith( + std::span{&newTablePair.localVocab_, 1}); + } else { + aggregate.emplace(newTablePair.idTable_.clone(), + newTablePair.localVocab_.clone()); + } + } catch (const ad_utility::detail::AllocationExceedsLimitException&) { + // We expect potential memory allocation errors here. In this case + // we just abort the cached value entirely instead of aborting the + // query. + return false; } } return doBothFitInCache; diff --git a/test/ResultTest.cpp b/test/ResultTest.cpp index 0dfc6e8bc..debda8c96 100644 --- a/test/ResultTest.cpp +++ b/test/ResultTest.cpp @@ -315,6 +315,49 @@ TEST(Result, verifyCacheDuringConsumptionRespectsPassedParameters) { } } +// _____________________________________________________________________________ +TEST(Result, cacheDuringConsumptionAbortsValueWhenRunningIntoMemoryLimit) { + bool flag = false; + Result result{[](bool& innerFlag) -> Result::Generator { + co_yield {IdTable{1, ad_utility::makeAllocatorWithLimit( + ad_utility::MemorySize::bytes(0))}, + LocalVocab{}}; + IdTable idTable{1, ad_utility::makeUnlimitedAllocator()}; + idTable.push_back({Id::makeFromBool(true)}); + co_yield {std::move(idTable), LocalVocab{}}; + innerFlag = true; + }(flag), + {0}}; + result.cacheDuringConsumption( + [](const std::optional&, + const Result::IdTableVocabPair&) { return true; }, + [&](Result) { ADD_FAILURE() << "The result should not get cached."; }); + consumeGenerator(result.idTables()); + EXPECT_TRUE(flag); +} + +// _____________________________________________________________________________ +TEST( + Result, + cacheDuringConsumptionAbortsValueWhenRunningIntoMemoryLimitOnInitialClone) { + bool flag = false; + Result result{[](bool& innerFlag) -> Result::Generator { + IdTable idTable{ + 1, ad_utility::makeAllocatorWithLimit( + ad_utility::MemorySize::bytes(1) * sizeof(Id))}; + idTable.push_back({Id::makeFromBool(true)}); + co_yield {std::move(idTable), LocalVocab{}}; + innerFlag = true; + }(flag), + {0}}; + result.cacheDuringConsumption( + [](const std::optional&, + const Result::IdTableVocabPair&) { return true; }, + [&](Result) { ADD_FAILURE() << "The result should not get cached."; }); + consumeGenerator(result.idTables()); + EXPECT_TRUE(flag); +} + // _____________________________________________________________________________ TEST(Result, verifyApplyLimitOffsetDoesCorrectlyApplyLimitAndOffset) { auto idTable = From db089f88b6dc2d8b6ad782cb6edbadf718c60b6d Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Sat, 26 Oct 2024 09:15:22 +0200 Subject: [PATCH 4/7] Implement lazy `Distinct` operation (#1558) Allow the `Distinct` operation to deal with lazy inputs. Note: `DISTINCT` currently always assumes a sorted input, so the laziness doesn't yet happen too often in practice. --- src/engine/Distinct.cpp | 162 +++++++++++++++++++++++++--- src/engine/Distinct.h | 46 ++++++-- src/engine/Engine.h | 31 ------ src/engine/idTable/IdTable.h | 12 +-- test/EngineTest.cpp | 27 ----- test/engine/DistinctTest.cpp | 199 +++++++++++++++++++++++++++++++++++ 6 files changed, 387 insertions(+), 90 deletions(-) diff --git a/src/engine/Distinct.cpp b/src/engine/Distinct.cpp index 8a78dcd2d..06b971854 100644 --- a/src/engine/Distinct.cpp +++ b/src/engine/Distinct.cpp @@ -4,10 +4,7 @@ #include "./Distinct.h" -#include - #include "engine/CallFixedSize.h" -#include "engine/Engine.h" #include "engine/QueryExecutionTree.h" using std::endl; @@ -19,13 +16,13 @@ size_t Distinct::getResultWidth() const { return subtree_->getResultWidth(); } // _____________________________________________________________________________ Distinct::Distinct(QueryExecutionContext* qec, std::shared_ptr subtree, - const vector& keepIndices) - : Operation(qec), subtree_(std::move(subtree)), _keepIndices(keepIndices) {} + const std::vector& keepIndices) + : Operation{qec}, subtree_{std::move(subtree)}, keepIndices_{keepIndices} {} // _____________________________________________________________________________ string Distinct::getCacheKeyImpl() const { return absl::StrCat("DISTINCT (", subtree_->getCacheKey(), ") (", - absl::StrJoin(_keepIndices, ","), ")"); + absl::StrJoin(keepIndices_, ","), ")"); } // _____________________________________________________________________________ @@ -37,16 +34,151 @@ VariableToColumnMap Distinct::computeVariableToColumnMap() const { } // _____________________________________________________________________________ -ProtoResult Distinct::computeResult([[maybe_unused]] bool requestLaziness) { - IdTable idTable{getExecutionContext()->getAllocator()}; +template +Result::Generator Distinct::lazyDistinct(Result::Generator input, + bool yieldOnce) const { + IdTable aggregateTable{subtree_->getResultWidth(), allocator()}; + LocalVocab aggregateVocab{}; + std::optional::row_type> previousRow = + std::nullopt; + for (auto& [idTable, localVocab] : input) { + IdTable result = distinct(std::move(idTable), previousRow); + if (!result.empty()) { + previousRow.emplace(result.asStaticView().back()); + if (yieldOnce) { + aggregateVocab.mergeWith(std::array{std::move(localVocab)}); + aggregateTable.insertAtEnd(result); + } else { + co_yield {std::move(result), std::move(localVocab)}; + } + } + } + if (yieldOnce) { + co_yield {std::move(aggregateTable), std::move(aggregateVocab)}; + } +} + +// _____________________________________________________________________________ +ProtoResult Distinct::computeResult(bool requestLaziness) { LOG(DEBUG) << "Getting sub-result for distinct result computation..." << endl; - std::shared_ptr subRes = subtree_->getResult(); + std::shared_ptr subRes = subtree_->getResult(true); LOG(DEBUG) << "Distinct result computation..." << endl; - idTable.setNumColumns(subRes->idTable().numColumns()); - size_t width = subRes->idTable().numColumns(); - CALL_FIXED_SIZE(width, &Engine::distinct, subRes->idTable(), _keepIndices, - &idTable); - LOG(DEBUG) << "Distinct result computation done." << endl; - return {std::move(idTable), resultSortedOn(), subRes->getSharedLocalVocab()}; + size_t width = subtree_->getResultWidth(); + if (subRes->isFullyMaterialized()) { + IdTable idTable = CALL_FIXED_SIZE(width, &Distinct::outOfPlaceDistinct, + this, subRes->idTable()); + LOG(DEBUG) << "Distinct result computation done." << endl; + return {std::move(idTable), resultSortedOn(), + subRes->getSharedLocalVocab()}; + } + + auto generator = + CALL_FIXED_SIZE(width, &Distinct::lazyDistinct, this, + std::move(subRes->idTables()), !requestLaziness); + return requestLaziness + ? ProtoResult{std::move(generator), resultSortedOn()} + : ProtoResult{cppcoro::getSingleElement(std::move(generator)), + resultSortedOn()}; +} + +// _____________________________________________________________________________ +bool Distinct::matchesRow(const auto& a, const auto& b) const { + return std::ranges::all_of(keepIndices_, + [&a, &b](ColumnIndex i) { return a[i] == b[i]; }); +} + +// _____________________________________________________________________________ +template +IdTable Distinct::distinct( + IdTable dynInput, + std::optional::row_type> previousRow) const { + AD_CONTRACT_CHECK(keepIndices_.size() <= dynInput.numColumns()); + LOG(DEBUG) << "Distinct on " << dynInput.size() << " elements.\n"; + IdTableStatic result = std::move(dynInput).toStatic(); + + // Variant of `std::ranges::unique` that allows to skip the begin rows of + // elements found in the previous table. + auto begin = + std::ranges::find_if(result, [this, &previousRow](const auto& row) { + // Without explicit this clang seems to + // think the this capture is redundant. + return !previousRow.has_value() || + !this->matchesRow(row, previousRow.value()); + }); + auto end = result.end(); + + auto dest = result.begin(); + if (begin == dest) { + // Optimization to avoid redundant move operations. + begin = std::ranges::adjacent_find(begin, end, + [this](const auto& a, const auto& b) { + // Without explicit this clang seems to + // think the this capture is redundant. + return this->matchesRow(a, b); + }); + dest = begin; + if (begin != end) { + ++begin; + } + } else if (begin != end) { + *dest = std::move(*begin); + } + + if (begin != end) { + while (++begin != end) { + if (!matchesRow(*dest, *begin)) { + *++dest = std::move(*begin); + checkCancellation(); + } + } + ++dest; + } + checkCancellation(); + result.erase(dest, end); + checkCancellation(); + + LOG(DEBUG) << "Distinct done.\n"; + return std::move(result).toDynamic(); +} + +// _____________________________________________________________________________ +template +IdTable Distinct::outOfPlaceDistinct(const IdTable& dynInput) const { + AD_CONTRACT_CHECK(keepIndices_.size() <= dynInput.numColumns()); + LOG(DEBUG) << "Distinct on " << dynInput.size() << " elements.\n"; + auto inputView = dynInput.asStaticView(); + IdTableStatic output{dynInput.numColumns(), allocator()}; + + auto begin = inputView.begin(); + auto end = inputView.end(); + while (begin < end) { + int64_t allowedOffset = std::min(end - begin, CHUNK_SIZE); + begin = std::ranges::unique_copy(begin, begin + allowedOffset, + std::back_inserter(output), + [this](const auto& a, const auto& b) { + // Without explicit this clang seems to + // think the this capture is redundant. + return this->matchesRow(a, b); + }) + .in; + checkCancellation(); + // Skip to next unique value + do { + allowedOffset = std::min(end - begin, CHUNK_SIZE); + // This can only be called when dynInput is not empty, so `begin[-1]` is + // always valid. + auto lastRow = begin[-1]; + begin = std::ranges::find_if(begin, begin + allowedOffset, + [this, &lastRow](const auto& row) { + // Without explicit this clang seems to + // think the this capture is redundant. + return !this->matchesRow(row, lastRow); + }); + checkCancellation(); + } while (begin != end && matchesRow(*begin, begin[-1])); + } + + LOG(DEBUG) << "Distinct done.\n"; + return std::move(output).toDynamic(); } diff --git a/src/engine/Distinct.h b/src/engine/Distinct.h index bfd408126..0fda43fd5 100644 --- a/src/engine/Distinct.h +++ b/src/engine/Distinct.h @@ -3,30 +3,28 @@ // Author: Björn Buchhold (buchhold@informatik.uni-freiburg.de) #pragma once -#include #include #include "engine/Operation.h" #include "engine/QueryExecutionTree.h" -#include "parser/ParsedQuery.h" - -using std::vector; class Distinct : public Operation { private: std::shared_ptr subtree_; - vector _keepIndices; + std::vector keepIndices_; + + static constexpr int64_t CHUNK_SIZE = 100'000; public: Distinct(QueryExecutionContext* qec, std::shared_ptr subtree, - const vector& keepIndices); + const std::vector& keepIndices); [[nodiscard]] size_t getResultWidth() const override; [[nodiscard]] string getDescriptor() const override; - [[nodiscard]] vector resultSortedOn() const override { + [[nodiscard]] std::vector resultSortedOn() const override { return subtree_->resultSortedOn(); } @@ -46,7 +44,7 @@ class Distinct : public Operation { bool knownEmptyResult() override { return subtree_->knownEmptyResult(); } - vector getChildren() override { + std::vector getChildren() override { return {subtree_.get()}; } @@ -54,7 +52,37 @@ class Distinct : public Operation { [[nodiscard]] string getCacheKeyImpl() const override; private: - ProtoResult computeResult([[maybe_unused]] bool requestLaziness) override; + ProtoResult computeResult(bool requestLaziness) override; VariableToColumnMap computeVariableToColumnMap() const override; + + // Helper function that only compares rows on the columns in `keepIndices_`. + bool matchesRow(const auto& a, const auto& b) const; + + // Return a generator that applies an in-place unique algorithm to the + // `IdTables`s yielded by the input generator. The `yieldOnce` flag controls + // if every `IdTable` from `input` should yield it's own `IdTable` or if all + // of them should get aggregated into a single big `IdTable`. + template + Result::Generator lazyDistinct(Result::Generator input, bool yieldOnce) const; + + // Removes all duplicates from input with regards to the columns + // in keepIndices. The input needs to be sorted on the keep indices, + // otherwise the result of this function is undefined. The argument + // `previousRow` might hold a row representing the last row of the previous + // `IdTable`, so that the `IdTable` that will be returned doesn't return + // values that were already returned in the previous `IdTable`. + template + IdTable distinct( + IdTable dynInput, + std::optional::row_type> previousRow) const; + + // Out-of-place implementation of the unique algorithm. Does only copy values + // if they're actually unique. + template + IdTable outOfPlaceDistinct(const IdTable& dynInput) const; + + FRIEND_TEST(Distinct, distinct); + FRIEND_TEST(Distinct, distinctWithEmptyInput); + FRIEND_TEST(Distinct, testChunkEdgeCases); }; diff --git a/src/engine/Engine.h b/src/engine/Engine.h index aa5c42b08..47a415674 100644 --- a/src/engine/Engine.h +++ b/src/engine/Engine.h @@ -140,37 +140,6 @@ class Engine { static void sort(IdTable& idTable, const std::vector& sortCols); - /** - * @brief Removes all duplicates from input with regards to the columns - * in keepIndices. The input needs to be sorted on the keep indices, - * otherwise the result of this function is undefined. - **/ - template - static void distinct(const IdTable& dynInput, - const std::vector& keepIndices, - IdTable* dynResult) { - LOG(DEBUG) << "Distinct on " << dynInput.size() << " elements.\n"; - const IdTableView input = dynInput.asStaticView(); - IdTableStatic result = std::move(*dynResult).toStatic(); - result = input.clone(); - if (!input.empty()) { - AD_CONTRACT_CHECK(keepIndices.size() <= input.numColumns()); - - auto last = std::unique(result.begin(), result.end(), - [&keepIndices](const auto& a, const auto& b) { - for (ColumnIndex i : keepIndices) { - if (a[i] != b[i]) { - return false; - } - } - return true; - }); - result.erase(last, result.end()); - } - *dynResult = std::move(result).toDynamic(); - LOG(DEBUG) << "Distinct done.\n"; - } - // Return the number of distinct rows in the `input`. The input must have all // duplicates adjacent to each other (e.g. by being sorted), otherwise the // behavior is undefined. `checkCancellation()` is invoked regularly and can diff --git a/src/engine/idTable/IdTable.h b/src/engine/idTable/IdTable.h index 29a0257ca..c615e8350 100644 --- a/src/engine/idTable/IdTable.h +++ b/src/engine/idTable/IdTable.h @@ -359,9 +359,11 @@ class IdTable { // `std::vector` aand other containers. // TODO Remove the duplicates via explicit object parameters // ("deducing this"). - row_reference_restricted front() { return (*this)[0]; } + row_reference_restricted front() requires(!isView) { return (*this)[0]; } const_row_reference_restricted front() const { return (*this)[0]; } - row_reference_restricted back() { return (*this)[numRows() - 1]; } + row_reference_restricted back() requires(!isView) { + return (*this)[numRows() - 1]; + } const_row_reference_restricted back() const { return (*this)[numRows() - 1]; } // Resize the `IdTable` to exactly `numRows`. If `numRows < size()`, then the @@ -637,12 +639,6 @@ class IdTable { // that `begin() <= beginIt <= endIt < end`, else the behavior is undefined. // The order of the elements before and after the erased regions remains the // same. This behavior is similar to `std::vector::erase`. - // - // TODO It is currently used by the implementation of DISTINCT, which - // first copies the sorted input completely, and then calls `std::unique`, - // followed by `erase` at the end. `DISTINCT` should be implemented via an - // out-of-place algorithm that only writes the distinct elements. The the - // following two functions can be deleted. void erase(const iterator& beginIt, const iterator& endIt) requires(!isView) { AD_EXPENSIVE_CHECK(begin() <= beginIt && beginIt <= endIt && endIt <= end()); diff --git a/test/EngineTest.cpp b/test/EngineTest.cpp index 00104bdd9..e6fcc1e76 100644 --- a/test/EngineTest.cpp +++ b/test/EngineTest.cpp @@ -15,12 +15,10 @@ #include "engine/ValuesForTesting.h" #include "engine/idTable/IdTable.h" #include "util/AllocatorTestHelpers.h" -#include "util/Forward.h" #include "util/GTestHelpers.h" #include "util/IdTableHelpers.h" #include "util/IdTestHelpers.h" #include "util/IndexTestHelpers.h" -#include "util/Random.h" using ad_utility::testing::makeAllocator; using namespace ad_utility::testing; @@ -30,31 +28,6 @@ constexpr auto U = Id::makeUndefined(); using JoinColumns = std::vector>; } // namespace -TEST(EngineTest, distinctTest) { - IdTable input{makeIdTableFromVector( - {{1, 1, 3, 7}, {6, 1, 3, 6}, {2, 2, 3, 5}, {3, 6, 5, 4}, {1, 6, 5, 1}})}; - - IdTable result{4, makeAllocator()}; - - std::vector keepIndices{{1, 2}}; - CALL_FIXED_SIZE(4, Engine::distinct, input, keepIndices, &result); - - // For easier checking. - IdTable expectedResult{ - makeIdTableFromVector({{1, 1, 3, 7}, {2, 2, 3, 5}, {3, 6, 5, 4}})}; - ASSERT_EQ(expectedResult, result); -} - -TEST(EngineTest, distinctWithEmptyInput) { - IdTable input{1, makeAllocator()}; - // Deliberately input a non-empty result to check that it is - // overwritten by the (empty) input. - IdTable result = makeIdTableFromVector({{3}}); - CALL_FIXED_SIZE(1, Engine::distinct, input, std::vector{}, - &result); - ASSERT_EQ(input, result); -} - void testOptionalJoin(const IdTable& inputA, const IdTable& inputB, JoinColumns jcls, const IdTable& expectedResult) { { diff --git a/test/engine/DistinctTest.cpp b/test/engine/DistinctTest.cpp index 9c277ab12..0b66ca974 100644 --- a/test/engine/DistinctTest.cpp +++ b/test/engine/DistinctTest.cpp @@ -4,10 +4,37 @@ #include +#include "../util/IdTableHelpers.h" #include "../util/IndexTestHelpers.h" #include "engine/Distinct.h" #include "engine/NeutralElementOperation.h" +using ad_utility::testing::makeAllocator; +using V = Variable; + +namespace { +// Convert a generator to a vector for easier comparison in assertions +std::vector toVector(Result::Generator generator) { + std::vector result; + for (auto& [table, vocab] : generator) { + // IMPORTANT: The `vocab` will go out of scope here, but the tests don't use + // any vocabulary so this is fine. + result.push_back(std::move(table)); + } + return result; +} + +// _____________________________________________________________________________ +Distinct makeDistinct(const std::vector& keepIndices) { + auto* qec = ad_utility::testing::getQec(); + return {qec, + ad_utility::makeExecutionTree( + qec, std::vector{}, + std::vector>{Variable{"?x"}}), + keepIndices}; +} +} // namespace + TEST(Distinct, CacheKey) { // The cache key has to change when the subtree changes or when the // `keepIndices` (the distinct variables) change. @@ -24,3 +51,175 @@ TEST(Distinct, CacheKey) { EXPECT_NE(d->getCacheKey(), d3.getCacheKey()); EXPECT_NE(d2.getCacheKey(), d3.getCacheKey()); } + +// _____________________________________________________________________________ +TEST(Distinct, distinct) { + IdTable input{makeIdTableFromVector( + {{1, 1, 3, 7}, {6, 1, 3, 6}, {2, 2, 3, 5}, {3, 6, 5, 4}, {1, 6, 5, 1}})}; + + Distinct distinct = makeDistinct({1, 2}); + IdTable result = distinct.outOfPlaceDistinct<4>(input); + + IdTable expectedResult{ + makeIdTableFromVector({{1, 1, 3, 7}, {2, 2, 3, 5}, {3, 6, 5, 4}})}; + ASSERT_EQ(expectedResult, result); +} + +// _____________________________________________________________________________ +TEST(Distinct, testChunkEdgeCases) { + Distinct distinct = makeDistinct({0}); + IdTable input{1, ad_utility::testing::makeAllocator()}; + IdTable::row_type row{1}; + + { + input.resize(1); + row[0] = Id::makeFromInt(0); + std::ranges::fill(input, row); + IdTable result = distinct.outOfPlaceDistinct<1>(input); + + ASSERT_EQ(makeIdTableFromVector({{0}}, &Id::makeFromInt), result); + } + + { + input.resize(Distinct::CHUNK_SIZE + 1); + row[0] = Id::makeFromInt(0); + std::ranges::fill(input, row); + IdTable result = distinct.outOfPlaceDistinct<1>(input); + + ASSERT_EQ(makeIdTableFromVector({{0}}, &Id::makeFromInt), result); + } + + { + input.resize(Distinct::CHUNK_SIZE + 1); + row[0] = Id::makeFromInt(0); + std::ranges::fill(input, row); + input.at(Distinct::CHUNK_SIZE, 0) = Id::makeFromInt(1); + IdTable result = distinct.outOfPlaceDistinct<1>(input); + + ASSERT_EQ(makeIdTableFromVector({{0}, {1}}, &Id::makeFromInt), result); + } + + { + input.resize(2 * Distinct::CHUNK_SIZE); + row[0] = Id::makeFromInt(0); + std::ranges::fill(input, row); + IdTable result = distinct.outOfPlaceDistinct<1>(input); + + ASSERT_EQ(makeIdTableFromVector({{0}}, &Id::makeFromInt), result); + } + + { + input.resize(2 * Distinct::CHUNK_SIZE + 2); + row[0] = Id::makeFromInt(0); + std::ranges::fill(input, row); + input.at(2 * Distinct::CHUNK_SIZE + 1, 0) = Id::makeFromInt(1); + IdTable result = distinct.outOfPlaceDistinct<1>(input); + + ASSERT_EQ(makeIdTableFromVector({{0}, {1}}, &Id::makeFromInt), result); + } +} + +// _____________________________________________________________________________ +TEST(Distinct, distinctWithEmptyInput) { + IdTable input{1, makeAllocator()}; + Distinct distinct = makeDistinct({}); + IdTable result = distinct.outOfPlaceDistinct<1>(input); + ASSERT_EQ(input, result); +} + +// _____________________________________________________________________________ +TEST(Distinct, nonLazy) { + IdTable input{makeIdTableFromVector( + {{1, 1, 3, 7}, {6, 1, 3, 6}, {2, 2, 3, 5}, {3, 6, 5, 4}, {1, 6, 5, 1}})}; + + auto qec = ad_utility::testing::getQec(); + qec->getQueryTreeCache().clearAll(); + + auto values = ad_utility::makeExecutionTree( + qec, std::move(input), + std::vector>{ + {V{"?a"}, V{"?b"}, V{"?c"}, V{"?d"}}}, + false, std::vector{1, 2}, LocalVocab{}, std::nullopt, true); + + Distinct distinct{qec, std::move(values), {1, 2}}; + + { + auto result = + distinct.getResult(false, ComputationMode::FULLY_MATERIALIZED); + ASSERT_TRUE(result->isFullyMaterialized()); + EXPECT_EQ( + result->idTable(), + makeIdTableFromVector({{1, 1, 3, 7}, {2, 2, 3, 5}, {3, 6, 5, 4}})); + } + + { + auto result = distinct.getResult(false, ComputationMode::LAZY_IF_SUPPORTED); + ASSERT_TRUE(result->isFullyMaterialized()); + EXPECT_EQ( + result->idTable(), + makeIdTableFromVector({{1, 1, 3, 7}, {2, 2, 3, 5}, {3, 6, 5, 4}})); + } +} + +// _____________________________________________________________________________ +TEST(Distinct, nonLazyWithLazyInputs) { + std::vector idTables{}; + idTables.push_back(makeIdTableFromVector({{1, 1, 3, 7}})); + idTables.push_back(makeIdTableFromVector( + {{6, 1, 3, 6}, {2, 2, 3, 5}, {3, 6, 5, 4}, {1, 6, 5, 1}})); + + auto qec = ad_utility::testing::getQec(); + qec->getQueryTreeCache().clearAll(); + + auto values = ad_utility::makeExecutionTree( + qec, std::move(idTables), + std::vector>{ + {V{"?a"}, V{"?b"}, V{"?c"}, V{"?d"}}}, + false, std::vector{1, 2}); + + Distinct distinct{qec, std::move(values), {1, 2}}; + + auto result = distinct.getResult(false, ComputationMode::FULLY_MATERIALIZED); + ASSERT_TRUE(result->isFullyMaterialized()); + EXPECT_EQ(result->idTable(), + makeIdTableFromVector({{1, 1, 3, 7}, {2, 2, 3, 5}, {3, 6, 5, 4}})); +} + +// _____________________________________________________________________________ +TEST(Distinct, lazyWithLazyInputs) { + std::vector idTables{}; + idTables.push_back(makeIdTableFromVector({{1, 1, 3, 7}})); + idTables.push_back(makeIdTableFromVector( + {{6, 1, 3, 6}, {2, 2, 3, 5}, {3, 6, 5, 4}, {1, 6, 5, 1}})); + idTables.push_back(makeIdTableFromVector({{2, 6, 5, 2}})); + idTables.push_back(IdTable{4, ad_utility::makeUnlimitedAllocator()}); + idTables.push_back(makeIdTableFromVector( + {{6, 7, 0, 6}, {2, 7, 1, 5}, {3, 7, 2, 4}, {1, 7, 3, 1}})); + idTables.push_back(makeIdTableFromVector( + {{6, 7, 4, 6}, {2, 7, 4, 5}, {3, 7, 4, 4}, {1, 7, 4, 1}})); + + auto qec = ad_utility::testing::getQec(); + qec->getQueryTreeCache().clearAll(); + + auto values = ad_utility::makeExecutionTree( + qec, std::move(idTables), + std::vector>{ + {V{"?a"}, V{"?b"}, V{"?c"}, V{"?d"}}}, + false, std::vector{1, 2}); + + Distinct distinct{qec, std::move(values), {1, 2}}; + + auto result = distinct.getResult(false, ComputationMode::LAZY_IF_SUPPORTED); + ASSERT_FALSE(result->isFullyMaterialized()); + + auto m = matchesIdTable; + using ::testing::ElementsAre; + EXPECT_THAT( + toVector(std::move(result->idTables())), + ElementsAre( + m(makeIdTableFromVector({{1, 1, 3, 7}})), + m(makeIdTableFromVector({{2, 2, 3, 5}, {3, 6, 5, 4}})), + m(makeIdTableFromVector( + {{6, 7, 0, 6}, {2, 7, 1, 5}, {3, 7, 2, 4}, {1, 7, 3, 1}})), + m(makeIdTableFromVector({{6, 7, 4, 6}})))); +} From 168d37148bf60d4a4f3bba83d3b27596a31dd9aa Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Sat, 26 Oct 2024 18:54:47 +0200 Subject: [PATCH 5/7] Implement `COUNT(*)` for lazy and hash map-based `GroupBy`. (#1577) Previously `COUNT(*)` was not a supported aggregate function when trying to process `GroupBy` lazily. The same thing applies to the hash map optimization for the non-lazy `GroupBy`. This PR implements this functionality in a very basic manner. There still is potential for optimization left on the table. --- src/engine/GroupBy.cpp | 33 ++++- src/engine/GroupBy.h | 8 ++ src/engine/LazyGroupBy.cpp | 6 +- .../sparqlExpressions/CountStarExpression.cpp | 128 ++++++++---------- .../sparqlExpressions/CountStarExpression.h | 25 +++- test/GroupByTest.cpp | 61 ++++++++- 6 files changed, 181 insertions(+), 80 deletions(-) diff --git a/src/engine/GroupBy.cpp b/src/engine/GroupBy.cpp index ed8b795f0..cc0088784 100644 --- a/src/engine/GroupBy.cpp +++ b/src/engine/GroupBy.cpp @@ -14,6 +14,7 @@ #include "engine/LazyGroupBy.h" #include "engine/Sort.h" #include "engine/sparqlExpressions/AggregateExpression.h" +#include "engine/sparqlExpressions/CountStarExpression.h" #include "engine/sparqlExpressions/GroupConcatExpression.h" #include "engine/sparqlExpressions/LiteralExpression.h" #include "engine/sparqlExpressions/SampleExpression.h" @@ -1015,6 +1016,10 @@ GroupBy::isSupportedAggregate(sparqlExpression::SparqlExpression* expr) { if (dynamic_cast(expr)) return H{AVG}; if (dynamic_cast(expr)) return H{COUNT}; + // We reuse the COUNT implementation which works, but leaves some optimization + // potential on the table because `COUNT(*)` doesn't need to check for + // undefined values. + if (dynamic_cast(expr)) return H{COUNT}; if (dynamic_cast(expr)) return H{MIN}; if (dynamic_cast(expr)) return H{MAX}; if (dynamic_cast(expr)) return H{SUM}; @@ -1371,6 +1376,29 @@ void GroupBy::evaluateAlias( } } +// _____________________________________________________________________________ +sparqlExpression::ExpressionResult +GroupBy::evaluateChildExpressionOfAggregateFunction( + const HashMapAggregateInformation& aggregate, + sparqlExpression::EvaluationContext& evaluationContext) { + // The code below assumes that DISTINCT is not supported yet. + AD_CORRECTNESS_CHECK(aggregate.expr_->isAggregate() == + sparqlExpression::SparqlExpression::AggregateStatus:: + NonDistinctAggregate); + // Evaluate child expression on block + auto exprChildren = aggregate.expr_->children(); + // `COUNT(*)` is the only expression without children, so we fake the + // expression result in this case by providing an arbitrary, constant and + // defined value. This value will be verified as non-undefined by the + // `CountExpression` class and ignored afterward as long as `DISTINCT` is + // not set (which is not supported yet). + bool isCountStar = + dynamic_cast(aggregate.expr_); + AD_CORRECTNESS_CHECK(isCountStar || exprChildren.size() == 1); + return isCountStar ? Id::makeFromBool(true) + : exprChildren[0]->evaluate(&evaluationContext); +} + // _____________________________________________________________________________ template IdTable GroupBy::createResultFromHashMap( @@ -1497,10 +1525,9 @@ IdTable GroupBy::computeGroupByForHashMapOptimization( aggregationTimer.cont(); for (auto& aggregateAlias : aggregateAliases) { for (auto& aggregate : aggregateAlias.aggregateInfo_) { - // Evaluate child expression on block - auto exprChildren = aggregate.expr_->children(); sparqlExpression::ExpressionResult expressionResult = - exprChildren[0]->evaluate(&evaluationContext); + GroupBy::evaluateChildExpressionOfAggregateFunction( + aggregate, evaluationContext); auto& aggregationDataVariant = aggregationData.getAggregationDataVariant( diff --git a/src/engine/GroupBy.h b/src/engine/GroupBy.h index acfc6feac..115849ea4 100644 --- a/src/engine/GroupBy.h +++ b/src/engine/GroupBy.h @@ -441,6 +441,14 @@ class GroupBy : public Operation { const HashMapAggregationData& aggregationData, LocalVocab* localVocab, const Allocator& allocator); + // Helper function to evaluate the child expression of an aggregate function. + // Only `COUNT(*)` does not have a single child, so we make a special case for + // it. + static sparqlExpression::ExpressionResult + evaluateChildExpressionOfAggregateFunction( + const HashMapAggregateInformation& aggregate, + sparqlExpression::EvaluationContext& evaluationContext); + // Sort the HashMap by key and create result table. template IdTable createResultFromHashMap( diff --git a/src/engine/LazyGroupBy.cpp b/src/engine/LazyGroupBy.cpp index 0575d9fb8..75d547983 100644 --- a/src/engine/LazyGroupBy.cpp +++ b/src/engine/LazyGroupBy.cpp @@ -68,11 +68,9 @@ void LazyGroupBy::processBlock( evaluationContext._endIndex = endIndex; for (const auto& aggregateInfo : allAggregateInfoView()) { - // Evaluate child expression on block - auto exprChildren = aggregateInfo.expr_->children(); - AD_CORRECTNESS_CHECK(exprChildren.size() == 1); sparqlExpression::ExpressionResult expressionResult = - exprChildren[0]->evaluate(&evaluationContext); + GroupBy::evaluateChildExpressionOfAggregateFunction(aggregateInfo, + evaluationContext); visitAggregate( [blockSize, diff --git a/src/engine/sparqlExpressions/CountStarExpression.cpp b/src/engine/sparqlExpressions/CountStarExpression.cpp index 29f55050c..8823080ad 100644 --- a/src/engine/sparqlExpressions/CountStarExpression.cpp +++ b/src/engine/sparqlExpressions/CountStarExpression.cpp @@ -2,90 +2,82 @@ // Chair of Algorithms and Data Structures. // Author: Johannes Kalmbach +#include "engine/sparqlExpressions/CountStarExpression.h" + #include #include "engine/CallFixedSize.h" #include "engine/Engine.h" #include "engine/sparqlExpressions/SparqlExpression.h" -// The implementation of `COUNT *`, deliberately hidden in a `.cpp` file. namespace sparqlExpression { -class CountStarExpression : public SparqlExpression { - private: - bool distinct_; - - public: - // _________________________________________________________________________ - explicit CountStarExpression(bool distinct) : distinct_{distinct} { - setIsInsideAggregate(); - } - // _________________________________________________________________________ - ExpressionResult evaluate( - sparqlExpression::EvaluationContext* ctx) const override { - // The case of a simple `COUNT *` is trivial, just return the size - // of the evaluation context. - if (!distinct_) { - return Id::makeFromInt(static_cast(ctx->size())); - } +// _____________________________________________________________________________ +CountStarExpression::CountStarExpression(bool distinct) : distinct_{distinct} { + setIsInsideAggregate(); +} - // For the distinct case, we make a deep copy of the IdTable, sort it, - // and then count the number of distinct elements. This could be more - // efficient if we knew that the input was already sorted, but we leave that - // open for another time. - IdTable table{ctx->_allocator}; +// _____________________________________________________________________________ +ExpressionResult CountStarExpression::evaluate( + sparqlExpression::EvaluationContext* ctx) const { + // The case of a simple `COUNT *` is trivial, just return the size + // of the evaluation context. + if (!distinct_) { + return Id::makeFromInt(static_cast(ctx->size())); + } - // We only need to copy columns that are actually visible. Columns that are - // hidden, e.g. because they weren't selected in a subquery, shouldn't be - // part of the DISTINCT computation. + // For the distinct case, we make a deep copy of the IdTable, sort it, + // and then count the number of distinct elements. This could be more + // efficient if we knew that the input was already sorted, but we leave that + // open for another time. + IdTable table{ctx->_allocator}; - auto varToColNoInternalVariables = - ctx->_variableToColumnMap | - std::views::filter([](const auto& varAndIdx) { - return !varAndIdx.first.name().starts_with(INTERNAL_VARIABLE_PREFIX); - }); - table.setNumColumns(std::ranges::distance(varToColNoInternalVariables)); - table.resize(ctx->size()); - auto checkCancellation = [ctx]() { - ctx->cancellationHandle_->throwIfCancelled(); - }; - size_t targetColIdx = 0; - for (const auto& [sourceColIdx, _] : - varToColNoInternalVariables | std::views::values) { - const auto& sourceColumn = ctx->_inputTable.getColumn(sourceColIdx); - std::ranges::copy(sourceColumn.begin() + ctx->_beginIndex, - sourceColumn.begin() + ctx->_endIndex, - table.getColumn(targetColIdx).begin()); - ++targetColIdx; - checkCancellation(); - } - ctx->_qec.getSortPerformanceEstimator().throwIfEstimateTooLong( - table.numRows(), table.numColumns(), ctx->deadline_, - "Sort for COUNT(DISTINCT *)"); - ad_utility::callFixedSize(table.numColumns(), [&table]() { - Engine::sort(&table, std::ranges::lexicographical_compare); - }); - return Id::makeFromInt( - static_cast(Engine::countDistinct(table, checkCancellation))); - } + // We only need to copy columns that are actually visible. Columns that are + // hidden, e.g. because they weren't selected in a subquery, shouldn't be + // part of the DISTINCT computation. - // COUNT * technically is an aggregate. - AggregateStatus isAggregate() const override { - return distinct_ ? AggregateStatus::DistinctAggregate - : AggregateStatus::NonDistinctAggregate; + auto varToColNoInternalVariables = + ctx->_variableToColumnMap | std::views::filter([](const auto& varAndIdx) { + return !varAndIdx.first.name().starts_with(INTERNAL_VARIABLE_PREFIX); + }); + table.setNumColumns(std::ranges::distance(varToColNoInternalVariables)); + table.resize(ctx->size()); + auto checkCancellation = [ctx]() { + ctx->cancellationHandle_->throwIfCancelled(); + }; + size_t targetColIdx = 0; + for (const auto& [sourceColIdx, _] : + varToColNoInternalVariables | std::views::values) { + const auto& sourceColumn = ctx->_inputTable.getColumn(sourceColIdx); + std::ranges::copy(sourceColumn.begin() + ctx->_beginIndex, + sourceColumn.begin() + ctx->_endIndex, + table.getColumn(targetColIdx).begin()); + ++targetColIdx; + checkCancellation(); } + ctx->_qec.getSortPerformanceEstimator().throwIfEstimateTooLong( + table.numRows(), table.numColumns(), ctx->deadline_, + "Sort for COUNT(DISTINCT *)"); + ad_utility::callFixedSize(table.numColumns(), [&table]() { + Engine::sort(&table, std::ranges::lexicographical_compare); + }); + return Id::makeFromInt( + static_cast(Engine::countDistinct(table, checkCancellation))); +} - // __________________________________________________________________ - string getCacheKey( - [[maybe_unused]] const VariableToColumnMap& varColMap) const override { - return absl::StrCat("COUNT * with DISTINCT = ", distinct_); - } +// _____________________________________________________________________________ +SparqlExpression::AggregateStatus CountStarExpression::isAggregate() const { + return distinct_ ? AggregateStatus::DistinctAggregate + : AggregateStatus::NonDistinctAggregate; +} - // __________________________________________________________________ - std::span childrenImpl() override { return {}; } -}; +// _____________________________________________________________________________ +string CountStarExpression::getCacheKey( + [[maybe_unused]] const VariableToColumnMap& varColMap) const { + return absl::StrCat("COUNT * with DISTINCT = ", distinct_); +} -// __________________________________________________________________ +// _____________________________________________________________________________ SparqlExpression::Ptr makeCountStarExpression(bool distinct) { return std::make_unique(distinct); } diff --git a/src/engine/sparqlExpressions/CountStarExpression.h b/src/engine/sparqlExpressions/CountStarExpression.h index 3147371d8..4e9e7dfd5 100644 --- a/src/engine/sparqlExpressions/CountStarExpression.h +++ b/src/engine/sparqlExpressions/CountStarExpression.h @@ -9,5 +9,28 @@ // Return a `SparqlExpression::Ptr` that implements the `COUNT [DISTINCT} *` // function. namespace sparqlExpression { +class CountStarExpression : public SparqlExpression { + private: + bool distinct_; + + public: + // ___________________________________________________________________________ + explicit CountStarExpression(bool distinct); + + // ___________________________________________________________________________ + ExpressionResult evaluate( + sparqlExpression::EvaluationContext* ctx) const override; + + // COUNT * technically is an aggregate. + AggregateStatus isAggregate() const override; + + // ___________________________________________________________________________ + string getCacheKey( + [[maybe_unused]] const VariableToColumnMap& varColMap) const override; + + // ___________________________________________________________________________ + std::span childrenImpl() override { return {}; } +}; + SparqlExpression::Ptr makeCountStarExpression(bool distinct); -} +} // namespace sparqlExpression diff --git a/test/GroupByTest.cpp b/test/GroupByTest.cpp index bb307fecd..e12bac05a 100644 --- a/test/GroupByTest.cpp +++ b/test/GroupByTest.cpp @@ -18,6 +18,7 @@ #include "engine/Values.h" #include "engine/ValuesForTesting.h" #include "engine/sparqlExpressions/AggregateExpression.h" +#include "engine/sparqlExpressions/CountStarExpression.h" #include "engine/sparqlExpressions/GroupConcatExpression.h" #include "engine/sparqlExpressions/LiteralExpression.h" #include "engine/sparqlExpressions/NaryExpression.h" @@ -721,21 +722,57 @@ TEST_F(GroupByOptimizations, correctResultForHashMapOptimization) { SparqlTriple{Variable{"?z"}, {""}, Variable{"?y"}}); Tree join = makeExecutionTree(qec, zxScan, zyScan, 0, 0); std::vector sortedColumns = {1}; - Tree sortedJoin = makeExecutionTree(qec, join, sortedColumns); SparqlExpressionPimpl avgYPimpl = makeAvgPimpl(varY); std::vector aliasesAvgY{Alias{avgYPimpl, Variable{"?avg"}}}; // Calculate result with optimization RuntimeParameters().set<"group-by-hash-map-enabled">(true); - GroupBy groupByWithOptimization{qec, variablesOnlyX, aliasesAvgY, sortedJoin}; + GroupBy groupByWithOptimization{qec, variablesOnlyX, aliasesAvgY, join}; auto resultWithOptimization = groupByWithOptimization.getResult(); // Clear cache, calculate result without optimization qec->clearCacheUnpinnedOnly(); RuntimeParameters().set<"group-by-hash-map-enabled">(false); - GroupBy groupByWithoutOptimization{qec, variablesOnlyX, aliasesAvgY, - sortedJoin}; + GroupBy groupByWithoutOptimization{qec, variablesOnlyX, aliasesAvgY, join}; + auto resultWithoutOptimization = groupByWithoutOptimization.getResult(); + + // Compare results, using debugString as the result only contains 2 rows + ASSERT_EQ(resultWithOptimization->asDebugString(), + resultWithoutOptimization->asDebugString()); +} + +// _____________________________________________________________________________ +TEST_F(GroupByOptimizations, correctResultForHashMapOptimizationForCountStar) { + /* Setup query: + SELECT ?x (COUNT(*) as ?c) WHERE { + ?z ?x . + ?z ?y + } GROUP BY ?x + */ + Tree zxScan = makeExecutionTree( + qec, Permutation::Enum::PSO, + SparqlTriple{Variable{"?z"}, {""}, Variable{"?x"}}); + Tree zyScan = makeExecutionTree( + qec, Permutation::Enum::PSO, + SparqlTriple{Variable{"?z"}, {""}, Variable{"?y"}}); + Tree join = makeExecutionTree(qec, zxScan, zyScan, 0, 0); + std::vector sortedColumns = {1}; + + SparqlExpressionPimpl countStarPimpl{makeCountStarExpression(false), + "COUNT(*) as ?c"}; + std::vector aliasesCountStar{Alias{countStarPimpl, Variable{"?c"}}}; + + // Calculate result with optimization + RuntimeParameters().set<"group-by-hash-map-enabled">(true); + GroupBy groupByWithOptimization{qec, variablesOnlyX, aliasesCountStar, join}; + auto resultWithOptimization = groupByWithOptimization.getResult(); + + // Clear cache, calculate result without optimization + qec->clearCacheUnpinnedOnly(); + RuntimeParameters().set<"group-by-hash-map-enabled">(false); + GroupBy groupByWithoutOptimization{qec, variablesOnlyX, aliasesCountStar, + join}; auto resultWithoutOptimization = groupByWithoutOptimization.getResult(); // Compare results, using debugString as the result only contains 2 rows @@ -2194,3 +2231,19 @@ TEST_P(GroupByLazyFixture, aliasRenameWorks) { expectReturningIdTables<2>(groupBy, {makeIntTable({{1, 1}, {2, 2}, {4, 4}}), makeIntTable({{8, 8}})}); } + +// _____________________________________________________________________________ +TEST_P(GroupByLazyFixture, countStarWorks) { + std::vector idTables; + idTables.push_back(makeIntTable({{1}})); + idTables.push_back(makeIntTable({{2}, {4}, {8}})); + auto subtree = makeExecutionTree( + qec_, std::move(idTables), vars("?x"), true, std::vector{0}); + + Alias alias{ + SparqlExpressionPimpl{makeCountStarExpression(false), "COUNT(*) as ?y"}, + V{"?y"}}; + GroupBy groupBy{qec_, {}, {std::move(alias)}, std::move(subtree)}; + + expectReturningIdTables<1>(groupBy, {makeIntTable({{4}})}); +} From 9e65d284d6bced40f34768d988428f5d36d54966 Mon Sep 17 00:00:00 2001 From: unex <63149623+UNEXENU@users.noreply.github.com> Date: Sat, 26 Oct 2024 19:07:25 +0200 Subject: [PATCH 6/7] Only compute the sibling of a `SERVICE` once (#1556) Previously, the sibling of a `SERVICE` (see #1341 for details) was computed twice. This was not a problem for small siblings result, because they were cached, making the second computation actually a cheap cache read. It was however a problem if the result was too large for the cache. It then was computed twice, although only one of the computations was actually used, because the SERVICE discarded the result because it was too large. This commit fixes this behavior. The sibling is now only computed once, and only gets explicitly shared with the `SERVICE` if it is sufficiently small. --- src/engine/Join.cpp | 7 + src/engine/Minus.cpp | 7 + src/engine/Operation.cpp | 7 + src/engine/Operation.h | 11 ++ src/engine/OptionalJoin.cpp | 7 + src/engine/QueryPlanner.cpp | 81 ----------- src/engine/QueryPlanner.h | 10 -- src/engine/Service.cpp | 149 +++++++++++++++++-- src/engine/Service.h | 51 +++---- test/IdTableHelpersTest.cpp | 2 +- test/OperationTest.cpp | 16 ++ test/QueryPlannerTest.cpp | 56 +------ test/QueryPlannerTestHelpers.h | 19 +-- test/ServiceTest.cpp | 259 ++++++++++++++++++++++++--------- 14 files changed, 409 insertions(+), 273 deletions(-) diff --git a/src/engine/Join.cpp b/src/engine/Join.cpp index f2c5e9631..92a9bf66b 100644 --- a/src/engine/Join.cpp +++ b/src/engine/Join.cpp @@ -14,6 +14,7 @@ #include "engine/AddCombinedRowToTable.h" #include "engine/CallFixedSize.h" #include "engine/IndexScan.h" +#include "engine/Service.h" #include "global/Constants.h" #include "global/Id.h" #include "global/RuntimeParameters.h" @@ -151,6 +152,12 @@ ProtoResult Join::computeResult([[maybe_unused]] bool requestLaziness) { } } + // If one of the RootOperations is a Service, precompute the result of its + // sibling. + Service::precomputeSiblingResult(_left->getRootOperation(), + _right->getRootOperation(), false, + requestLaziness); + std::shared_ptr leftRes = leftResIfCached ? leftResIfCached : _left->getResult(); checkCancellation(); diff --git a/src/engine/Minus.cpp b/src/engine/Minus.cpp index ae3e870a1..32aa9eca3 100644 --- a/src/engine/Minus.cpp +++ b/src/engine/Minus.cpp @@ -5,6 +5,7 @@ #include "Minus.h" #include "engine/CallFixedSize.h" +#include "engine/Service.h" #include "util/Exception.h" using std::endl; @@ -35,6 +36,12 @@ string Minus::getDescriptor() const { return "Minus"; } ProtoResult Minus::computeResult([[maybe_unused]] bool requestLaziness) { LOG(DEBUG) << "Minus result computation..." << endl; + // If the right of the RootOperations is a Service, precompute the result of + // its sibling. + Service::precomputeSiblingResult(_left->getRootOperation(), + _right->getRootOperation(), true, + requestLaziness); + IdTable idTable{getExecutionContext()->getAllocator()}; idTable.setNumColumns(getResultWidth()); diff --git a/src/engine/Operation.cpp b/src/engine/Operation.cpp index 64d2af234..bb7d8fb0a 100644 --- a/src/engine/Operation.cpp +++ b/src/engine/Operation.cpp @@ -212,6 +212,13 @@ CacheValue Operation::runComputationAndPrepareForCache( // ________________________________________________________________________ std::shared_ptr Operation::getResult( bool isRoot, ComputationMode computationMode) { + // Use the precomputed Result if it exists. + if (precomputedResultBecauseSiblingOfService_.has_value()) { + auto result = std::move(precomputedResultBecauseSiblingOfService_).value(); + precomputedResultBecauseSiblingOfService_.reset(); + return result; + } + ad_utility::Timer timer{ad_utility::Timer::Started}; if (isRoot) { diff --git a/src/engine/Operation.h b/src/engine/Operation.h index 49045ca28..61702c776 100644 --- a/src/engine/Operation.h +++ b/src/engine/Operation.h @@ -131,6 +131,12 @@ class Operation { return false; } + // See the member variable with the same name below for documentation. + std::optional>& + precomputedResultBecauseSiblingOfService() { + return precomputedResultBecauseSiblingOfService_; + } + RuntimeInformation& runtimeInfo() const { return *_runtimeInfo; } std::shared_ptr getRuntimeInfoPointer() { @@ -354,6 +360,11 @@ class Operation { template void forAllDescendants(F f) const; + // Holds a precomputed Result of this operation if it is the sibling of a + // Service operation. + std::optional> + precomputedResultBecauseSiblingOfService_; + std::shared_ptr _runtimeInfo = std::make_shared(); /// Pointer to the head of the `RuntimeInformation`. diff --git a/src/engine/OptionalJoin.cpp b/src/engine/OptionalJoin.cpp index 45c7c2d0b..dea5fcc25 100644 --- a/src/engine/OptionalJoin.cpp +++ b/src/engine/OptionalJoin.cpp @@ -8,6 +8,7 @@ #include "engine/AddCombinedRowToTable.h" #include "engine/CallFixedSize.h" #include "engine/Engine.h" +#include "engine/Service.h" #include "util/JoinAlgorithms/JoinAlgorithms.h" using std::endl; @@ -92,6 +93,12 @@ string OptionalJoin::getDescriptor() const { ProtoResult OptionalJoin::computeResult([[maybe_unused]] bool requestLaziness) { LOG(DEBUG) << "OptionalJoin result computation..." << endl; + // If the right of the RootOperations is a Service, precompute the result of + // its sibling. + Service::precomputeSiblingResult(_left->getRootOperation(), + _right->getRootOperation(), true, + requestLaziness); + IdTable idTable{getExecutionContext()->getAllocator()}; idTable.setNumColumns(getResultWidth()); diff --git a/src/engine/QueryPlanner.cpp b/src/engine/QueryPlanner.cpp index b63958e19..a1aaa5fee 100644 --- a/src/engine/QueryPlanner.cpp +++ b/src/engine/QueryPlanner.cpp @@ -1806,20 +1806,11 @@ std::vector QueryPlanner::createJoinCandidates( // further into the query that optional should be resolved by now. AD_CONTRACT_CHECK(a.type != SubtreePlan::OPTIONAL); if (b.type == SubtreePlan::MINUS) { - if (auto opt = createSubtreeWithService(a, b)) { - return {opt.value()}; - } return {makeSubtreePlan(_qec, a._qet, b._qet)}; } // OPTIONAL JOINS are not symmetric! if (b.type == SubtreePlan::OPTIONAL) { - // If the OPTIONAL subtree's rootOperation is a SERVICE, try to simplify it - // using the result of the first subtree. - if (auto opt = createSubtreeWithService(a, b)) { - return {opt.value()}; - } - // Join the two optional columns using an optional join return {makeSubtreePlan(_qec, a._qet, b._qet)}; } @@ -1829,13 +1820,6 @@ std::vector QueryPlanner::createJoinCandidates( return candidates; } - // Check if one of the two Operations is a SERVICE. If so, we can try - // to simplify the Service Query using the result of the other operation. - if (auto opt = createJoinWithService(a, b, jcs)) { - candidates.push_back(std::move(opt.value())); - return candidates; - } - if (jcs.size() >= 2) { // If there are two or more join columns and we are not using the // TwoColumnJoin (the if part before this comment), use a multiColumnJoin. @@ -2008,71 +1992,6 @@ auto QueryPlanner::createJoinWithHasPredicateScan( return plan; } -// _____________________________________________________________________ -auto QueryPlanner::createJoinWithService( - const SubtreePlan& a, const SubtreePlan& b, - const std::vector>& jcs) - -> std::optional { - // We can only proceed if exactly one of the inputs is a `SERVICE`. - auto aRootOp = std::dynamic_pointer_cast(a._qet->getRootOperation()); - auto bRootOp = std::dynamic_pointer_cast(b._qet->getRootOperation()); - if (static_cast(aRootOp) == static_cast(bRootOp)) { - return std::nullopt; - } - - // Setup some variables that are agnostic of which of the two inputs is the - // serviceWithSibling clause, as these cases are completely symmetric. - const auto& [serviceIn, sibling, serviceIdx, siblingIdx] = [&]() { - // `std::tie` requires lvalue-references, that's why we define explicit - // variables for `0` and `1`. - static constexpr size_t zero = 0; - static constexpr size_t one = 1; - if (aRootOp) { - return std::tie(aRootOp, b, zero, one); - } else { - AD_CORRECTNESS_CHECK(bRootOp); - return std::tie(bRootOp, a, one, zero); - } - }(); - - auto serviceWithSibling = - makeSubtreePlan(serviceIn->createCopyWithSiblingTree(sibling._qet)); - auto qec = serviceIn->getExecutionContext(); - - SubtreePlan plan = - jcs.size() == 1 - ? makeSubtreePlan(qec, std::move(serviceWithSibling._qet), - sibling._qet, jcs[0][serviceIdx], - jcs[0][siblingIdx]) - : makeSubtreePlan( - qec, std::move(serviceWithSibling._qet), sibling._qet); - mergeSubtreePlanIds(plan, a, b); - - return plan; -} - -// _____________________________________________________________________ -template -auto QueryPlanner::createSubtreeWithService(const SubtreePlan& a, - const SubtreePlan& b) - -> std::optional { - // The right subtree has to be a Service. - auto bRootOp = std::dynamic_pointer_cast(b._qet->getRootOperation()); - if (!static_cast(bRootOp)) { - return std::nullopt; - } - - auto serviceWithSibling = - makeSubtreePlan(bRootOp->createCopyWithSiblingTree(a._qet)); - auto qec = bRootOp->getExecutionContext(); - - SubtreePlan plan = makeSubtreePlan( - qec, a._qet, std::move(serviceWithSibling._qet)); - mergeSubtreePlanIds(plan, a, b); - - return plan; -} - // _____________________________________________________________________ auto QueryPlanner::createJoinWithPathSearch( const SubtreePlan& a, const SubtreePlan& b, diff --git a/src/engine/QueryPlanner.h b/src/engine/QueryPlanner.h index 8d3a13cd3..1fa4c8546 100644 --- a/src/engine/QueryPlanner.h +++ b/src/engine/QueryPlanner.h @@ -338,20 +338,10 @@ class QueryPlanner { SubtreePlan a, SubtreePlan b, const std::vector>& jcs); - // The following two functions are used to provide a Service-operation with - // it's siblingTree, allowing us to optimize the service-query. - [[nodiscard]] static std::optional createJoinWithService( - const SubtreePlan& a, const SubtreePlan& b, - const std::vector>& jcs); - [[nodiscard]] static std::optional createJoinWithPathSearch( const SubtreePlan& a, const SubtreePlan& b, const std::vector>& jcs); - template - [[nodiscard]] static std::optional createSubtreeWithService( - const SubtreePlan& a, const SubtreePlan& b); - // if one of the inputs is a spatial join which is compatible with the other // input, then add that other input to the spatial join as a child instead of // creating a normal join. diff --git a/src/engine/Service.cpp b/src/engine/Service.cpp index 4587066ba..8c946a2fb 100644 --- a/src/engine/Service.cpp +++ b/src/engine/Service.cpp @@ -10,6 +10,7 @@ #include "engine/CallFixedSize.h" #include "engine/ExportQueryExecutionTrees.h" +#include "engine/Sort.h" #include "engine/VariableToColumnMap.h" #include "global/RuntimeParameters.h" #include "parser/RdfParser.h" @@ -23,12 +24,10 @@ // ____________________________________________________________________________ Service::Service(QueryExecutionContext* qec, parsedQuery::Service parsedServiceClause, - GetResultFunction getResultFunction, - std::shared_ptr siblingTree) + GetResultFunction getResultFunction) : Operation{qec}, parsedServiceClause_{std::move(parsedServiceClause)}, - getResultFunction_{std::move(getResultFunction)}, - siblingTree_{std::move(siblingTree)} {} + getResultFunction_{std::move(getResultFunction)} {} // ____________________________________________________________________________ std::string Service::getCacheKeyImpl() const { @@ -40,8 +39,8 @@ std::string Service::getCacheKeyImpl() const { os << parsedServiceClause_.serviceIri_.toStringRepresentation() << " {\n" << parsedServiceClause_.prologue_ << "\n" << parsedServiceClause_.graphPatternAsString_ << "\n"; - if (siblingTree_ != nullptr) { - os << siblingTree_->getRootOperation()->getCacheKey() << "\n"; + if (siblingInfo_.has_value()) { + os << siblingInfo_->cacheKey_ << "\n"; } os << "}\n"; return std::move(os).str(); @@ -302,20 +301,15 @@ Result::Generator Service::computeResultLazily( // ____________________________________________________________________________ std::optional Service::getSiblingValuesClause() const { - if (siblingTree_ == nullptr) { - return std::nullopt; - } - - const auto& siblingResult = siblingTree_->getResult(); - if (siblingResult->idTable().size() > - RuntimeParameters().get<"service-max-value-rows">()) { + if (!siblingInfo_.has_value()) { return std::nullopt; } + const auto& [siblingResult, siblingVars, _] = siblingInfo_.value(); + AD_CORRECTNESS_CHECK(siblingResult != nullptr); checkCancellation(); std::vector commonColumnIndices; - const auto& siblingVars = siblingTree_->getVariableColumns(); std::string vars = "("; for (const auto& localVar : parsedServiceClause_.visibleVariables_) { auto it = siblingVars.find(localVar); @@ -334,8 +328,7 @@ std::optional Service::getSiblingValuesClause() const { std::string row = "("; for (const auto& columnIdx : commonColumnIndices) { const auto& optStr = idToValueForValuesClause( - siblingTree_->getRootOperation()->getIndex(), - siblingResult->idTable()(rowIndex, columnIdx), + getIndex(), siblingResult->idTable()(rowIndex, columnIdx), siblingResult->localVocab()); if (!optStr.has_value()) { @@ -504,3 +497,127 @@ std::optional Service::idToValueForValuesClause( } } } + +// ____________________________________________________________________________ +void Service::precomputeSiblingResult(std::shared_ptr left, + std::shared_ptr right, + bool rightOnly, bool requestLaziness) { + AD_CORRECTNESS_CHECK(left && right); + + auto skipSortOperation = [](std::shared_ptr& op) { + if (static_cast(std::dynamic_pointer_cast(op))) { + const auto& children = op->getChildren(); + AD_CORRECTNESS_CHECK(children.size() == 1); + op = children[0]->getRootOperation(); + } + }; + skipSortOperation(left); + skipSortOperation(right); + + auto a = std::dynamic_pointer_cast(left); + auto b = std::dynamic_pointer_cast(right); + + // The sibling is only precomputed iff + // - `rightOnly` is true and the right operation is a Service + // - or exactly one of the operations is a Service. If we could estimate + // the result size of a Service, the Service with the smaller result could + // be used as a sibling here. + if ((rightOnly && !static_cast(b)) || + (!rightOnly && static_cast(a) == static_cast(b))) { + return; + } + + const auto& [service, sibling] = [&]() { + if (a) { + return std::tie(a, right); + } else { + AD_CORRECTNESS_CHECK(b); + return std::tie(b, left); + } + }(); + AD_CORRECTNESS_CHECK(service != nullptr); + + auto addRuntimeInfo = [&](bool siblingUsed) { + std::string_view v = siblingUsed ? "yes"sv : "no"sv; + service->runtimeInfo().addDetail("optimized-with-sibling-result", v); + sibling->runtimeInfo().addDetail("used-to-optimize-service-sibling", v); + }; + + auto siblingResult = sibling->getResult( + false, requestLaziness ? ComputationMode::LAZY_IF_SUPPORTED + : ComputationMode::FULLY_MATERIALIZED); + + if (siblingResult->isFullyMaterialized()) { + bool resultIsSmall = siblingResult->idTable().size() <= + RuntimeParameters().get<"service-max-value-rows">(); + if (resultIsSmall) { + service->siblingInfo_.emplace( + siblingResult, sibling->getExternallyVisibleVariableColumns(), + sibling->getCacheKey()); + } + sibling->precomputedResultBecauseSiblingOfService() = + std::move(siblingResult); + addRuntimeInfo(resultIsSmall); + return; + } + + // Creates a `Result::Generator` from partially materialized result data. + auto partialResultGenerator = + [](std::vector pairs, + Result::Generator prevGenerator, + Result::Generator::iterator it) -> Result::Generator { + for (auto& pair : pairs) { + co_yield pair; + } + for (auto& pair : std::ranges::subrange{it, prevGenerator.end()}) { + co_yield pair; + } + }; + + // Start materializing the lazy `siblingResult`. + size_t rows = 0; + std::vector resultPairs; + auto generator = std::move(siblingResult->idTables()); + const size_t maxValueRows = + RuntimeParameters().get<"service-max-value-rows">(); + for (auto it = generator.begin(); it != generator.end(); ++it) { + auto& pair = *it; + rows += pair.idTable_.size(); + resultPairs.push_back(std::move(pair)); + + if (rows > maxValueRows) { + // Stop precomputation as the size of `siblingResult` exceeds the + // threshold it is not useful for the service operation. Pass the + // partially materialized result to the sibling. + sibling->precomputedResultBecauseSiblingOfService() = + std::make_shared( + partialResultGenerator(std::move(resultPairs), + std::move(generator), std::move(++it)), + siblingResult->sortedBy()); + addRuntimeInfo(false); + return; + } + } + + // The `siblingResult` has been fully materialized, so it can now be + // used in both sibling and service. + Result::IdTableVocabPair siblingPair( + IdTable{sibling->getResultWidth(), + sibling->getExecutionContext()->getAllocator()}, + LocalVocab{}); + siblingPair.idTable_.reserve(rows); + + for (auto& pair : resultPairs) { + siblingPair.idTable_.insertAtEnd(pair.idTable_); + siblingPair.localVocab_.mergeWith(std::span{&pair.localVocab_, 1}); + } + + service->siblingInfo_.emplace( + std::make_shared(std::move(siblingPair), + siblingResult->sortedBy()), + sibling->getExternallyVisibleVariableColumns(), sibling->getCacheKey()); + + sibling->precomputedResultBecauseSiblingOfService() = + service->siblingInfo_->precomputedResult_; + addRuntimeInfo(true); +} diff --git a/src/engine/Service.h b/src/engine/Service.h index 41aa9e5e5..8fef6f5d0 100644 --- a/src/engine/Service.h +++ b/src/engine/Service.h @@ -7,7 +7,7 @@ #include #include "engine/Operation.h" -#include "engine/Values.h" +#include "engine/VariableToColumnMap.h" #include "parser/ParsedQuery.h" #include "util/LazyJsonParser.h" #include "util/http/HttpClient.h" @@ -37,6 +37,13 @@ class Service : public Operation { const boost::beast::http::verb&, std::string_view, std::string_view, std::string_view)>; + // Information on a Sibling operation. + struct SiblingInfo { + std::shared_ptr precomputedResult_; + VariableToColumnMap variables_; + std::string cacheKey_; + }; + private: // The parsed SERVICE clause. parsedQuery::Service parsedServiceClause_; @@ -44,8 +51,8 @@ class Service : public Operation { // The function used to obtain the result from the remote endpoint. GetResultFunction getResultFunction_; - // The siblingTree, used for SERVICE clause optimization. - std::shared_ptr siblingTree_; + // Optional sibling information to be used in `getSiblingValuesClause`. + std::optional siblingInfo_; public: // Construct from parsed Service clause. @@ -55,23 +62,7 @@ class Service : public Operation { // but in our tests (`ServiceTest`) we use a mock function that does not // require a running `HttpServer`. Service(QueryExecutionContext* qec, parsedQuery::Service parsedServiceClause, - GetResultFunction getResultFunction = sendHttpOrHttpsRequest, - std::shared_ptr siblingTree = nullptr); - - // Create a new `Service` operation, that is equal to `*this` but additionally - // respects the `siblingTree`. The sibling tree is a partial query that will - // later be joined with the result of the `Service`. If the result of the - // sibling is small, it will be used to constrain the SERVICE query using a - // `VALUES` clause. - [[nodiscard]] std::shared_ptr createCopyWithSiblingTree( - std::shared_ptr siblingTree) const { - AD_CORRECTNESS_CHECK(siblingTree_ == nullptr); - // TODO This copies the `parsedServiceClause_`. We could probably - // use a `shared_ptr` here to reduce the copying during QueryPlanning. - return std::make_shared(getExecutionContext(), - parsedServiceClause_, getResultFunction_, - std::move(siblingTree)); - } + GetResultFunction getResultFunction = sendHttpOrHttpsRequest); // Methods inherited from base class `Operation`. std::string getDescriptor() const override; @@ -79,12 +70,6 @@ class Service : public Operation { std::vector resultSortedOn() const override { return {}; } float getMultiplicity(size_t col) override; - // Getters for testing. - const auto& getSiblingTree() const { return siblingTree_; } - const auto& getGraphPatternAsString() const { - return parsedServiceClause_.graphPatternAsString_; - } - private: uint64_t getSizeEstimateBeforeLimit() override; @@ -109,11 +94,19 @@ class Service : public Operation { static std::optional idToValueForValuesClause( const Index& index, Id id, const LocalVocab& localVocab); + // Given two child-operations of a `Join`-, `OptionalJoin`- or `Minus` + // operation, this method tries to precompute the result of one if the other + // one (its sibling) is a `Service` operation. If `rightOnly` is true (used by + // `OptionalJoin` and `Minus`), only the right operation can be a `Service`. + static void precomputeSiblingResult(std::shared_ptr left, + std::shared_ptr right, + bool rightOnly, bool requestLaziness); + private: // The string returned by this function is used as cache key. std::string getCacheKeyImpl() const override; - // Compute the result using `getResultFunction_` and the siblingTree. + // Compute the result using `getResultFunction_` and `siblingInfo_`. ProtoResult computeResult([[maybe_unused]] bool requestLaziness) override; // Actually compute the result for the function above. @@ -151,4 +144,8 @@ class Service : public Operation { Result::Generator computeResultLazily( const std::vector vars, ad_utility::LazyJsonParser::Generator body, bool singleIdTable); + + FRIEND_TEST(ServiceTest, computeResult); + FRIEND_TEST(ServiceTest, getCacheKey); + FRIEND_TEST(ServiceTest, precomputeSiblingResult); }; diff --git a/test/IdTableHelpersTest.cpp b/test/IdTableHelpersTest.cpp index 2bae9888c..b6cbcebbd 100644 --- a/test/IdTableHelpersTest.cpp +++ b/test/IdTableHelpersTest.cpp @@ -186,7 +186,7 @@ TEST(IdTableHelpersTest, createRandomlyFilledIdTableWithoutGenerators) { // The overloads that take generators for creating the content of the join // columns. -TEST(IdTableHelpersTest, createRandomlyFilledIdTableWithGeneratos) { +TEST(IdTableHelpersTest, createRandomlyFilledIdTableWithGenerators) { // Creates a 'generator', that counts one up, every time it's called. auto createCountUpGenerator = []() { return [i = 0]() mutable { return ad_utility::testing::VocabId(i++); }; diff --git a/test/OperationTest.cpp b/test/OperationTest.cpp index 5b3591dea..0e93b8e9f 100644 --- a/test/OperationTest.cpp +++ b/test/OperationTest.cpp @@ -4,6 +4,8 @@ #include +#include + #include "engine/NeutralElementOperation.h" #include "engine/ValuesForTesting.h" #include "util/IdTableHelpers.h" @@ -163,6 +165,20 @@ TEST_F(OperationTestFixture, verifyCachePreventsInProgressState) { // _____________________________________________________________________________ +TEST_F(OperationTestFixture, getPrecomputedResultBecauseSiblingOfService) { + // If a precomputedResult is set, it will be returned by `getResult` + auto idTable = makeIdTableFromVector({{1, 6, 0}, {2, 5, 0}, {3, 4, 0}}); + auto result = std::make_shared( + idTable.clone(), std::vector{0}, LocalVocab{}); + operation.precomputedResultBecauseSiblingOfService() = + std::make_optional(result); + EXPECT_EQ(operation.getResult(), result); + EXPECT_FALSE( + operation.precomputedResultBecauseSiblingOfService().has_value()); +} + +// _____________________________________________________________________________ + TEST(OperationTest, verifyExceptionIsThrownOnCancellation) { auto qec = getQec(); auto handle = std::make_shared>(); diff --git a/test/QueryPlannerTest.cpp b/test/QueryPlannerTest.cpp index 5db2454f1..dfff1ac36 100644 --- a/test/QueryPlannerTest.cpp +++ b/test/QueryPlannerTest.cpp @@ -1903,6 +1903,13 @@ TEST(QueryPlanner, UnboundMinusIgnored) { h::IndexScanFromStrings("?a", "", "?b")); } +// ___________________________________________________________________________ +TEST(QueryPlanner, SimpleMinus) { + h::expect("SELECT * WHERE { ?a ?b MINUS{?a ?b}}", + h::Minus(h::IndexScanFromStrings("?a", "", "?b"), + h::IndexScanFromStrings("?a", "", "?b"))); +} + // ___________________________________________________________________________ TEST(QueryPlanner, CancellationCancelsQueryPlanning) { auto cancellationHandle = @@ -1919,55 +1926,6 @@ TEST(QueryPlanner, CancellationCancelsQueryPlanning) { } // ___________________________________________________________________________ -TEST(QueryPlanner, JoinWithService) { - auto scan = h::IndexScanFromStrings; - auto sibling = scan("?x", "", "?y"); - std::string_view graphPatternAsString = "{ ?x ?z . }"; - - h::expect( - "SELECT * WHERE {" - "SERVICE { ?x ?z . ?y ?a . }}", - h::Service(std::nullopt, "{ ?x ?z . ?y ?a . }")); - - h::expect( - "SELECT * WHERE { ?x ?y ." - "SERVICE { ?x ?z . }}", - h::UnorderedJoins(sibling, h::Service(sibling, graphPatternAsString))); - - h::expect( - "SELECT * WHERE { ?x ?y . " - "SERVICE { ?x ?z . ?y ?a . }}", - h::MultiColumnJoin( - sibling, - h::Sort(h::Service(sibling, "{ ?x ?z . ?y ?a . }")))); -} - -// ___________________________________________________________________________ -TEST(QueryPlanner, SubtreeWithService) { - auto scan = h::IndexScanFromStrings; - auto sibling = scan("?x", "", "?y"); - - h::expect( - "SELECT * WHERE { ?x ?y ." - "OPTIONAL{SERVICE { ?x ?z . }}}", - h::OptionalJoin(sibling, - h::Sort(h::Service(sibling, "{ ?x ?z . }")))); - - h::expect( - "SELECT * WHERE { ?x ?y . " - "OPTIONAL{" - "SERVICE { ?x ?z . ?y ?a . }}}", - h::OptionalJoin( - sibling, - h::Sort(h::Service(sibling, "{ ?x ?z . ?y ?a . }")))); - - h::expect( - "SELECT * WHERE { ?x ?y MINUS{SERVICE { ?x " - " ?z . }}}", - h::Minus(sibling, h::Sort(h::Service(sibling, "{ ?x ?z . }")))); -} - -// ________________________________________ TEST(QueryPlanner, DatasetClause) { auto scan = h::IndexScanFromStrings; using Graphs = ad_utility::HashSet; diff --git a/test/QueryPlannerTestHelpers.h b/test/QueryPlannerTestHelpers.h index 2457284c8..48abc6d34 100644 --- a/test/QueryPlannerTestHelpers.h +++ b/test/QueryPlannerTestHelpers.h @@ -26,7 +26,6 @@ #include "engine/PathSearch.h" #include "engine/QueryExecutionTree.h" #include "engine/QueryPlanner.h" -#include "engine/Service.h" #include "engine/Sort.h" #include "engine/SpatialJoin.h" #include "engine/TextIndexScanForEntity.h" @@ -34,6 +33,7 @@ #include "engine/TextLimit.h" #include "engine/TransitivePathBase.h" #include "engine/Union.h" +#include "engine/Values.h" #include "global/RuntimeParameters.h" #include "parser/SparqlParser.h" #include "util/IndexTestHelpers.h" @@ -382,23 +382,6 @@ constexpr auto OrderBy = [](const ::OrderBy::SortedVariables& sortedVariables, // Match a `UNION` operation. constexpr auto Union = MatchTypeAndOrderedChildren<::Union>; -// Match a `SERVICE` operation. -constexpr auto Service = [](const std::optional& siblingMatcher, - std::string_view graphPatternAsString) { - const auto optSiblingMatcher = - [&]() -> Matcher&> { - if (siblingMatcher.has_value()) { - return Pointee(siblingMatcher.value()); - } - return IsNull(); - }(); - - return RootOperation<::Service>( - AllOf(AD_PROPERTY(::Service, getSiblingTree, optSiblingMatcher), - AD_PROPERTY(::Service, getGraphPatternAsString, - Eq(graphPatternAsString)))); -}; - /// Parse the given SPARQL `query`, pass it to a `QueryPlanner` with empty /// execution context, and return the resulting `QueryExecutionTree` QueryExecutionTree parseAndPlan(std::string query, QueryExecutionContext* qec) { diff --git a/test/ServiceTest.cpp b/test/ServiceTest.cpp index 8ee80703e..7f53202db 100644 --- a/test/ServiceTest.cpp +++ b/test/ServiceTest.cpp @@ -10,6 +10,8 @@ #include #include "engine/Service.h" +#include "engine/Sort.h" +#include "engine/Values.h" #include "global/Constants.h" #include "global/IndexTypes.h" #include "global/RuntimeParameters.h" @@ -127,6 +129,12 @@ class ServiceTest : public ::testing::Test { } return res.dump(); } + + static Service::SiblingInfo siblingInfoFromOp(std::shared_ptr op) { + return Service::SiblingInfo{op->getResult(), + op->getExternallyVisibleVariableColumns(), + op->getCacheKey()}; + }; }; // Test basic methods of class `Service`. @@ -409,19 +417,17 @@ TEST_F(ServiceTest, computeResult) { auto iri = ad_utility::testing::iri; using TC = TripleComponent; - auto siblingTree = std::make_shared( - testQec, - std::make_shared( - testQec, - (parsedQuery::SparqlValues){ - {Variable{"?x"}, Variable{"?y"}, Variable{"?z"}}, - {{TC(iri("")), TC(iri("")), TC(iri(""))}, - {TC(iri("")), TC(iri("")), TC(iri(""))}, - {TC(iri("")), TC(iri("")), TC(iri(""))}, - // This row will be ignored in the created Values Clause as it - // contains a blank node. - {TC(Id::makeFromBlankNodeIndex(BlankNodeIndex::make(0))), - TC(iri("")), TC(iri(""))}}})); + + auto sibling = std::make_shared( + testQec, (parsedQuery::SparqlValues){ + {Variable{"?x"}, Variable{"?y"}, Variable{"?z"}}, + {{TC(iri("")), TC(iri("")), TC(iri(""))}, + {TC(iri("")), TC(iri("")), TC(iri(""))}, + {TC(iri("")), TC(iri("")), TC(iri(""))}, + // This row will be ignored in the created Values Clause + // as it contains a blank node. + {TC(Id::makeFromBlankNodeIndex(BlankNodeIndex::make(0))), + TC(iri("")), TC(iri(""))}}}); auto parsedServiceClause5 = parsedServiceClause; parsedServiceClause5.graphPatternAsString_ = @@ -441,29 +447,10 @@ TEST_F(ServiceTest, computeResult) { genJsonResult({"x", "y", "z2"}, {{"x", "y", "y"}, {"bla", "bli", "y"}, {"blu", "bla", "y"}, - {"bli", "blu", "y"}})), - siblingTree}; - EXPECT_NO_THROW(serviceOperation5.computeResultOnlyForTesting()); + {"bli", "blu", "y"}}))}; - // Check 6: SiblingTree's rows exceed maxValue - const auto maxValueRowsDefault = - RuntimeParameters().get<"service-max-value-rows">(); - RuntimeParameters().set<"service-max-value-rows">(0); - testQec->getQueryTreeCache().clearAll(); - std::string_view expectedSparqlQuery6 = - "PREFIX doof: SELECT ?x ?y ?z2 " - "WHERE { ?x ?y . ?y ?z2 . }"; - Service serviceOperation6{ - testQec, parsedServiceClause5, - getResultFunctionFactory( - expectedUrl, expectedSparqlQuery6, - genJsonResult({"x", "y", "z2"}, {{"x", "y", "y"}, - {"bla", "bli", "y"}, - {"blue", "bla", "y"}, - {"bli", "blu", "y"}})), - siblingTree}; - EXPECT_NO_THROW(serviceOperation6.computeResultOnlyForTesting()); - RuntimeParameters().set<"service-max-value-rows">(maxValueRowsDefault); + serviceOperation5.siblingInfo_.emplace(siblingInfoFromOp(sibling)); + EXPECT_NO_THROW(serviceOperation5.computeResultOnlyForTesting()); // Check 7: Lazy computation Service lazyService{ @@ -529,47 +516,27 @@ TEST_F(ServiceTest, getCacheKey) { // of the siblingTree, as it might alter the Service Query. auto iri = ad_utility::testing::iri; using TC = TripleComponent; - auto siblingTree = std::make_shared( - testQec, - std::make_shared( - testQec, - (parsedQuery::SparqlValues){ - {Variable{"?x"}, Variable{"?y"}, Variable{"?z"}}, - {{TC(iri("")), TC(iri("")), TC(iri(""))}, - {TC(iri("")), TC(iri("")), TC(iri(""))}}})); - - auto siblingCacheKey = - service.createCopyWithSiblingTree(siblingTree)->getCacheKey(); + auto sibling = std::make_shared( + testQec, (parsedQuery::SparqlValues){ + {Variable{"?x"}, Variable{"?y"}, Variable{"?z"}}, + {{TC(iri("")), TC(iri("")), TC(iri(""))}, + {TC(iri("")), TC(iri("")), TC(iri(""))}}}); + service.siblingInfo_.emplace(siblingInfoFromOp(sibling)); + + auto siblingCacheKey = service.getCacheKey(); EXPECT_NE(baseCacheKey, siblingCacheKey); - auto siblingTree2 = std::make_shared( - testQec, - std::make_shared( - testQec, (parsedQuery::SparqlValues){ - {Variable{"?x"}, Variable{"?y"}, Variable{"?z"}}, - {{TC(iri("")), TC(iri("")), TC(iri(""))}}})); - - auto serviceWithSibling = service.createCopyWithSiblingTree(siblingTree2); + auto sibling2 = std::make_shared( + testQec, (parsedQuery::SparqlValues){ + {Variable{"?x"}, Variable{"?y"}, Variable{"?z"}}, + {{TC(iri("")), TC(iri("")), TC(iri(""))}}}); + service.siblingInfo_.emplace(siblingInfoFromOp(sibling2)); - EXPECT_NE(siblingCacheKey, serviceWithSibling->getCacheKey()); + EXPECT_NE(siblingCacheKey, service.getCacheKey()); // SILENT keyword - parsedQuery::Service silentParsedServiceClause{ - {Variable{"?x"}, Variable{"?y"}}, - TripleComponent::Iri::fromIriref(""), - "PREFIX doof: ", - "{ }", - true}; - Service silentService( - testQec, silentParsedServiceClause, - getResultFunctionFactory( - "http://localhorst:80/api", - "PREFIX doof: SELECT ?x ?y WHERE { }", - genJsonResult( - {"x", "y"}, - {{"x", "y"}, {"bla", "bli"}, {"blu", "bla"}, {"bli", "blu"}}))); - - EXPECT_NE(baseCacheKey, silentService.getCacheKey()); + service.parsedServiceClause_.silent_ = true; + EXPECT_NE(baseCacheKey, service.getCacheKey()); } // Test that bindingToTripleComponent behaves as expected. @@ -678,3 +645,153 @@ TEST_F(ServiceTest, idToValueForValuesClause) { .value(), absl::StrCat("\"POINT(130.200000 70.500000)\"^^<", GEO_WKT_LITERAL, ">")); } + +// ____________________________________________________________________________ +TEST_F(ServiceTest, precomputeSiblingResult) { + auto service = std::make_shared( + testQec, + parsedQuery::Service{ + {Variable{"?x"}, Variable{"?y"}}, + TripleComponent::Iri::fromIriref(""), + "PREFIX doof: ", + "{ }", + true}, + getResultFunctionFactory( + "http://localhorst:80/api", + "PREFIX doof: SELECT ?x ?y WHERE { }", + genJsonResult({"x", "y"}, {{"a", "b"}}), + boost::beast::http::status::ok, "application/sparql-results+json")); + + auto service2 = std::make_shared(*service); + + // Adaptation of the Values class, allowing to compute lazy Results. + class MockValues : public Values { + public: + MockValues(QueryExecutionContext* qec, + parsedQuery::SparqlValues parsedValues) + : Values(qec, parsedValues) {} + + ProtoResult computeResult([[maybe_unused]] bool requestLaziness) override { + ProtoResult res = Values::computeResult(false); + + if (!requestLaziness) { + return ProtoResult(Result::IdTableVocabPair(res.idTable().clone(), + res.localVocab().clone()), + res.sortedBy()); + } + + // yield each row individually + return {[&](IdTable clone) -> Result::Generator { + IdTable idt{clone.numColumns(), + ad_utility::makeUnlimitedAllocator()}; + for (size_t i = 0; i < clone.size(); ++i) { + idt.push_back(clone[i]); + Result::IdTableVocabPair pair{std::move(idt), LocalVocab{}}; + co_yield pair; + idt.clear(); + } + }(res.idTable().clone()), + res.sortedBy()}; + } + }; + + auto iri = ad_utility::testing::iri; + using TC = TripleComponent; + auto siblingOperation = std::make_shared( + testQec, parsedQuery::SparqlValues{{Variable{"?x"}, Variable{"?y"}}, + {{TC(iri("")), TC(iri(""))}, + {TC(iri("")), TC(iri(""))}}}); + auto sibling = std::make_shared( + testQec, std::make_shared(testQec, siblingOperation), + std::vector{}); + + // Reset the computed results, to reuse the mock-operations. + auto reset = [&]() { + service->siblingInfo_.reset(); + service2->precomputedResultBecauseSiblingOfService().reset(); + siblingOperation->precomputedResultBecauseSiblingOfService().reset(); + testQec->clearCacheUnpinnedOnly(); + }; + + // Right requested but it is not a Service -> no computation + Service::precomputeSiblingResult(service, sibling, true, false); + EXPECT_FALSE( + siblingOperation->precomputedResultBecauseSiblingOfService().has_value()); + EXPECT_FALSE(service->siblingInfo_.has_value()); + EXPECT_FALSE(service->precomputedResultBecauseSiblingOfService().has_value()); + reset(); + + // Two Service operations -> no computation + Service::precomputeSiblingResult(service, service2, false, false); + EXPECT_FALSE( + service2->precomputedResultBecauseSiblingOfService().has_value()); + EXPECT_FALSE(service->siblingInfo_.has_value()); + EXPECT_FALSE(service->precomputedResultBecauseSiblingOfService().has_value()); + reset(); + + // Right requested and two Service operations -> compute + Service::precomputeSiblingResult(service, service2, true, false); + EXPECT_TRUE(service2->precomputedResultBecauseSiblingOfService().has_value()); + EXPECT_TRUE(service->siblingInfo_.has_value()); + EXPECT_FALSE(service->precomputedResultBecauseSiblingOfService().has_value()); + reset(); + + // Right requested and it is a service -> sibling result is computed and + // shared with service + Service::precomputeSiblingResult(sibling, service, true, false); + ASSERT_TRUE( + siblingOperation->precomputedResultBecauseSiblingOfService().has_value()); + EXPECT_TRUE(siblingOperation->precomputedResultBecauseSiblingOfService() + .value() + ->isFullyMaterialized()); + EXPECT_TRUE(service->siblingInfo_.has_value()); + EXPECT_FALSE(service->precomputedResultBecauseSiblingOfService().has_value()); + reset(); + + // Compute (large) sibling -> sibling result is computed + const auto maxValueRowsDefault = + RuntimeParameters().get<"service-max-value-rows">(); + RuntimeParameters().set<"service-max-value-rows">(0); + Service::precomputeSiblingResult(sibling, service, true, false); + ASSERT_TRUE( + siblingOperation->precomputedResultBecauseSiblingOfService().has_value()); + EXPECT_TRUE(siblingOperation->precomputedResultBecauseSiblingOfService() + .value() + ->isFullyMaterialized()); + EXPECT_FALSE(service->siblingInfo_.has_value()); + EXPECT_FALSE(service->precomputedResultBecauseSiblingOfService().has_value()); + RuntimeParameters().set<"service-max-value-rows">(maxValueRowsDefault); + reset(); + + // Lazy compute (small) sibling -> sibling result is fully materialized and + // shared with service + Service::precomputeSiblingResult(service, sibling, false, true); + ASSERT_TRUE( + siblingOperation->precomputedResultBecauseSiblingOfService().has_value()); + EXPECT_TRUE(siblingOperation->precomputedResultBecauseSiblingOfService() + .value() + ->isFullyMaterialized()); + EXPECT_TRUE(service->siblingInfo_.has_value()); + EXPECT_FALSE(service->precomputedResultBecauseSiblingOfService().has_value()); + reset(); + + // Lazy compute (large) sibling -> partially materialized result is passed + // back to sibling + RuntimeParameters().set<"service-max-value-rows">(0); + Service::precomputeSiblingResult(service, sibling, false, true); + ASSERT_TRUE( + siblingOperation->precomputedResultBecauseSiblingOfService().has_value()); + EXPECT_FALSE(siblingOperation->precomputedResultBecauseSiblingOfService() + .value() + ->isFullyMaterialized()); + EXPECT_FALSE(service->siblingInfo_.has_value()); + EXPECT_FALSE(service->precomputedResultBecauseSiblingOfService().has_value()); + RuntimeParameters().set<"service-max-value-rows">(maxValueRowsDefault); + + // consume the sibling result-generator + for ([[maybe_unused]] auto& _ : + siblingOperation->precomputedResultBecauseSiblingOfService() + .value() + ->idTables()) { + } +} From d60b6101dbb0b0ff101e75cf6715206ab1c3f5bf Mon Sep 17 00:00:00 2001 From: Hannah Bast Date: Sun, 27 Oct 2024 08:30:14 +0100 Subject: [PATCH 7/7] Add internal function `ql:isGeoPoint` (#1565) The function returns true if and only if the argument is a `ValueId` with datatype `Datatype::GeoPoint`, that is, it represents a WKT point and the value is encoded directly in the `Id`. This is useful for https://github.com/ad-freiburg/qlever-petrimaps, which does not have to pre-fetch and store the literals for such points. It is important to note that there are WKT points for which this function evaluates to false. For example, points where the coordinates are out of range, are not encoded in the `Id` but have an entry in the vocabulary. Due to this QLever-specific semantics, we make this a QLever-internal function. This is our first QLever-internal function. So far we only had QLever-internal predicates (like `ql:has-predicate` or `ql:langtag`) and Qlever-internal entities (like `ql:@en`). Used that occasion to make the names of several of the QLever-internal constants in the code more meaningful and consistent. --- src/engine/QueryPlanner.cpp | 2 +- .../sparqlExpressions/CountStarExpression.cpp | 3 +- .../IsSomethingExpressions.cpp | 18 ++++-- src/engine/sparqlExpressions/NaryExpression.h | 3 +- .../SparqlExpressionValueGetters.h | 46 +++++++------- src/global/Constants.h | 60 ++++++++++--------- src/global/SpecialIds.h | 2 +- src/index/ConstantsIndexBuilding.h | 2 +- src/index/IndexImpl.cpp | 8 +-- src/index/Permutation.cpp | 3 +- src/index/Vocabulary.cpp | 2 +- src/index/VocabularyMerger.h | 3 +- src/parser/ParsedQuery.cpp | 13 ++-- src/parser/SparqlParser.cpp | 2 +- .../sparqlParser/SparqlQleverVisitor.cpp | 24 +++++++- src/util/Conversions.cpp | 8 +-- test/AggregateExpressionTest.cpp | 2 +- test/ConstantsTest.cpp | 8 +-- test/QueryPlannerTest.cpp | 2 +- test/SparqlAntlrParserTest.cpp | 20 +++++-- test/SparqlAntlrParserTestHelpers.h | 8 +-- test/SparqlExpressionTest.cpp | 3 + 22 files changed, 146 insertions(+), 96 deletions(-) diff --git a/src/engine/QueryPlanner.cpp b/src/engine/QueryPlanner.cpp index a1aaa5fee..471dd43e0 100644 --- a/src/engine/QueryPlanner.cpp +++ b/src/engine/QueryPlanner.cpp @@ -937,7 +937,7 @@ ParsedQuery::GraphPattern QueryPlanner::uniteGraphPatterns( // _____________________________________________________________________________ Variable QueryPlanner::generateUniqueVarName() { - return Variable{absl::StrCat(INTERNAL_VARIABLE_QUERY_PLANNER_PREFIX, + return Variable{absl::StrCat(QLEVER_INTERNAL_VARIABLE_QUERY_PLANNER_PREFIX, _internalVarCount++)}; } diff --git a/src/engine/sparqlExpressions/CountStarExpression.cpp b/src/engine/sparqlExpressions/CountStarExpression.cpp index 8823080ad..e4abc2f20 100644 --- a/src/engine/sparqlExpressions/CountStarExpression.cpp +++ b/src/engine/sparqlExpressions/CountStarExpression.cpp @@ -38,7 +38,8 @@ ExpressionResult CountStarExpression::evaluate( auto varToColNoInternalVariables = ctx->_variableToColumnMap | std::views::filter([](const auto& varAndIdx) { - return !varAndIdx.first.name().starts_with(INTERNAL_VARIABLE_PREFIX); + return !varAndIdx.first.name().starts_with( + QLEVER_INTERNAL_VARIABLE_PREFIX); }); table.setNumColumns(std::ranges::distance(varToColNoInternalVariables)); table.resize(ctx->size()); diff --git a/src/engine/sparqlExpressions/IsSomethingExpressions.cpp b/src/engine/sparqlExpressions/IsSomethingExpressions.cpp index 0d1780b75..dc196fe2b 100644 --- a/src/engine/sparqlExpressions/IsSomethingExpressions.cpp +++ b/src/engine/sparqlExpressions/IsSomethingExpressions.cpp @@ -24,14 +24,19 @@ namespace detail { // `...Expression` (`std::move` the arguments into the constructor). The // function should be declared in `NaryExpression.h`. -// Expressions for `isIRI`, `isBlank`, `isLiteral`, and `isNumeric`. Note that -// the value getters already return the correct `Id`, hence `std::identity`. +// Expressions for the builtin functions `isIRI`, `isBlank`, `isLiteral`, +// `isNumeric`, and the custom function `isWktPoint`. Note that the value +// getters already return the correct `Id`, hence `std::identity`. using isIriExpression = NARY<1, FV>; -using isBlankExpression = NARY<1, FV>; using isLiteralExpression = NARY<1, FV>; using isNumericExpression = NARY<1, FV>; -// The expression for `bound` is slightly different because -// `IsValidValueGetter` returns a `bool` and not an `Id`. +using isBlankExpression = + NARY<1, FV>>; +using isGeoPointExpression = + NARY<1, FV>>; + +// The expression for `bound` is slightly different as `IsValidValueGetter` +// returns a `bool` and not an `Id`. inline auto boolToId = [](bool b) { return Id::makeFromBool(b); }; using boundExpression = NARY<1, FV>; @@ -49,6 +54,9 @@ SparqlExpression::Ptr makeIsLiteralExpression(SparqlExpression::Ptr arg) { SparqlExpression::Ptr makeIsNumericExpression(SparqlExpression::Ptr arg) { return std::make_unique(std::move(arg)); } +SparqlExpression::Ptr makeIsGeoPointExpression(SparqlExpression::Ptr arg) { + return std::make_unique(std::move(arg)); +} SparqlExpression::Ptr makeBoundExpression(SparqlExpression::Ptr arg) { return std::make_unique(std::move(arg)); } diff --git a/src/engine/sparqlExpressions/NaryExpression.h b/src/engine/sparqlExpressions/NaryExpression.h index 5237a2283..8fb8d9504 100644 --- a/src/engine/sparqlExpressions/NaryExpression.h +++ b/src/engine/sparqlExpressions/NaryExpression.h @@ -116,9 +116,10 @@ std::optional getVariableFromLangExpression( SparqlExpression::Ptr makeEncodeForUriExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeIsIriExpression(SparqlExpression::Ptr child); -SparqlExpression::Ptr makeIsBlankExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeIsLiteralExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeIsNumericExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr makeIsBlankExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr makeIsGeoPointExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeBoundExpression(SparqlExpression::Ptr child); // For a `function` that takes `std::vector` (size only diff --git a/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h b/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h index 6e7cd310e..88dbc2d82 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h +++ b/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h @@ -141,11 +141,13 @@ struct StringValueGetter : Mixin { } }; -// Value getter for `isBlank`. -struct IsBlankNodeValueGetter : Mixin { - using Mixin::operator(); - Id operator()(ValueId id, const EvaluationContext*) const { - return Id::makeFromBool(id.getDatatype() == Datatype::BlankNodeIndex); +// Boolean value getter that checks whether the given `Id` is a `ValueId` of the +// given `datatype`. +template +struct IsValueIdValueGetter : Mixin> { + using Mixin::operator(); + Id operator()(Id id, const EvaluationContext*) const { + return Id::makeFromBool(id.getDatatype() == datatype); } Id operator()(const LiteralOrIri&, const EvaluationContext*) const { @@ -153,7 +155,21 @@ struct IsBlankNodeValueGetter : Mixin { } }; -// Value getters for `isIRI`, `isBlank`, and `isLiteral`. +// Boolean value getter for `isNumeric`. Regarding which datatypes count as +// numeric, see https://www.w3.org/TR/sparql11-query/#operandDataTypes . +struct IsNumericValueGetter : Mixin { + using Mixin::operator(); + Id operator()(ValueId id, const EvaluationContext*) const { + Datatype datatype = id.getDatatype(); + return Id::makeFromBool(datatype == Datatype::Double || + datatype == Datatype::Int); + } + Id operator()(const LiteralOrIri&, const EvaluationContext*) const { + return Id::makeFromBool(false); + } +}; + +// Boolean value getters for `isIRI`, `isBlank`, and `isLiteral`. template struct IsSomethingValueGetter : Mixin; -// Value getter for `isNumeric`. Regarding which datatypes count as numeric, -// see https://www.w3.org/TR/sparql11-query/#operandDataTypes . -struct IsNumericValueGetter : Mixin { - using Mixin::operator(); - Id operator()(ValueId id, const EvaluationContext*) const { - Datatype datatype = id.getDatatype(); - return Id::makeFromBool(datatype == Datatype::Double || - datatype == Datatype::Int); - } - Id operator()(const LiteralOrIri&, const EvaluationContext*) const { - return Id::makeFromBool(false); - } -}; - -/// This class can be used as the `ValueGetter` argument of Expression -/// templates. It produces a `std::optional`. +// This class can be used as the `ValueGetter` argument of Expression +// templates. It produces a `std::optional`. struct DateValueGetter : Mixin { using Mixin::operator(); using Opt = std::optional; diff --git a/src/global/Constants.h b/src/global/Constants.h index 0e94c1ed9..f5632acc8 100644 --- a/src/global/Constants.h +++ b/src/global/Constants.h @@ -35,43 +35,44 @@ constexpr inline size_t TEXT_PREDICATE_CARDINALITY_ESTIMATE = 1'000'000'000; constexpr inline size_t GALLOP_THRESHOLD = 1000; -constexpr inline char INTERNAL_PREDICATE_PREFIX_NAME[] = "ql"; - -constexpr inline char INTERNAL_PREDICATE_PREFIX[] = +constexpr inline char QLEVER_INTERNAL_PREFIX_NAME[] = "ql"; +constexpr inline char QLEVER_INTERNAL_PREFIX_URL[] = "http://qlever.cs.uni-freiburg.de/builtin-functions/"; -// Return a IRI of the form -// `` +// Make a QLever-internal IRI from `QL_INTERNAL_PREFIX_URL` by appending the +// concatenation of the given `suffixes` and enclosing the result in angle +// brackets (const and non-const version). template < ad_utility::detail::constexpr_str_cat_impl::ConstexprString... suffixes> -constexpr std::string_view makeInternalIriConst() { - return ad_utility::constexprStrCat<"<", INTERNAL_PREDICATE_PREFIX, +constexpr std::string_view makeQleverInternalIriConst() { + return ad_utility::constexprStrCat<"<", QLEVER_INTERNAL_PREFIX_URL, suffixes..., ">">(); } - -inline std::string makeInternalIri(const auto&... suffixes) { - return absl::StrCat("<", std::string_view{INTERNAL_PREDICATE_PREFIX}, +inline std::string makeQleverInternalIri(const auto&... suffixes) { + return absl::StrCat("<", std::string_view{QLEVER_INTERNAL_PREFIX_URL}, suffixes..., ">"); } -constexpr inline std::string_view INTERNAL_ENTITIES_URI_PREFIX = - ad_utility::constexprStrCat<"<", INTERNAL_PREDICATE_PREFIX>(); -constexpr inline std::string_view INTERNAL_PREDICATE_PREFIX_IRI = - makeInternalIriConst<"">(); + +constexpr inline std::string_view QLEVER_INTERNAL_PREFIX_IRI = + makeQleverInternalIriConst<"">(); +constexpr inline std::string_view + QLEVER_INTERNAL_PREFIX_IRI_WITHOUT_CLOSING_BRACKET = + ad_utility::constexprStrCat<"<", QLEVER_INTERNAL_PREFIX_URL>(); constexpr inline std::string_view CONTAINS_ENTITY_PREDICATE = - makeInternalIriConst<"contains-entity">(); + makeQleverInternalIriConst<"contains-entity">(); constexpr inline std::string_view CONTAINS_WORD_PREDICATE = - makeInternalIriConst<"contains-word">(); + makeQleverInternalIriConst<"contains-word">(); -constexpr inline std::string_view INTERNAL_TEXT_MATCH_PREDICATE = - makeInternalIriConst<"text">(); +constexpr inline std::string_view QLEVER_INTERNAL_TEXT_MATCH_PREDICATE = + makeQleverInternalIriConst<"text">(); constexpr inline std::string_view HAS_PREDICATE_PREDICATE = - makeInternalIriConst<"has-predicate">(); + makeQleverInternalIriConst<"has-predicate">(); constexpr inline std::string_view HAS_PATTERN_PREDICATE = - makeInternalIriConst<"has-pattern">(); + makeQleverInternalIriConst<"has-pattern">(); constexpr inline std::string_view DEFAULT_GRAPH_IRI = - makeInternalIriConst<"default-graph">(); -constexpr inline std::string_view INTERNAL_GRAPH_IRI = - makeInternalIriConst<"internal-graph">(); + makeQleverInternalIriConst<"default-graph">(); +constexpr inline std::string_view QLEVER_INTERNAL_GRAPH_IRI = + makeQleverInternalIriConst<"internal-graph">(); constexpr inline std::pair GEOF_PREFIX = { "geof:", "http://www.opengis.net/def/function/geosparql/"}; @@ -79,22 +80,25 @@ constexpr inline std::pair MATH_PREFIX = { "math:", "http://www.w3.org/2005/xpath-functions/math#"}; constexpr inline std::pair XSD_PREFIX = { "xsd", "http://www.w3.org/2001/XMLSchema#"}; +constexpr inline std::pair QL_PREFIX = { + QLEVER_INTERNAL_PREFIX_NAME, QLEVER_INTERNAL_PREFIX_URL}; -constexpr inline std::string_view INTERNAL_VARIABLE_PREFIX = +constexpr inline std::string_view QLEVER_INTERNAL_VARIABLE_PREFIX = "?_QLever_internal_variable_"; -constexpr inline std::string_view INTERNAL_BLANKNODE_VARIABLE_PREFIX = +constexpr inline std::string_view QLEVER_INTERNAL_BLANKNODE_VARIABLE_PREFIX = "?_QLever_internal_variable_bn_"; -constexpr inline std::string_view INTERNAL_VARIABLE_QUERY_PLANNER_PREFIX = - "?_QLever_internal_variable_qp_"; +constexpr inline std::string_view + QLEVER_INTERNAL_VARIABLE_QUERY_PLANNER_PREFIX = + "?_QLever_internal_variable_qp_"; constexpr inline std::string_view SCORE_VARIABLE_PREFIX = "?ql_score_"; constexpr inline std::string_view MATCHINGWORD_VARIABLE_PREFIX = "?ql_matchingword_"; constexpr inline std::string_view LANGUAGE_PREDICATE = - makeInternalIriConst<"langtag">(); + makeQleverInternalIriConst<"langtag">(); // this predicate is one of the supported identifiers for the SpatialJoin class. // It joins the two objects, if their distance is smaller or equal to the diff --git a/src/global/SpecialIds.h b/src/global/SpecialIds.h index 658998bf7..b844e1172 100644 --- a/src/global/SpecialIds.h +++ b/src/global/SpecialIds.h @@ -27,7 +27,7 @@ inline const ad_utility::HashMap& specialIds() { {S{HAS_PREDICATE_PREDICATE}, Id::fromBits(1)}, {S{HAS_PATTERN_PREDICATE}, Id::fromBits(2)}, {S{DEFAULT_GRAPH_IRI}, Id::fromBits(3)}, - {S{INTERNAL_GRAPH_IRI}, Id::fromBits(4)}}; + {S{QLEVER_INTERNAL_GRAPH_IRI}, Id::fromBits(4)}}; // Perform the following checks: All the special IDs are unique, all of them // have the `Undefined` datatype, but none of them is equal to the "actual" diff --git a/src/index/ConstantsIndexBuilding.h b/src/index/ConstantsIndexBuilding.h index 5f7643484..71d489787 100644 --- a/src/index/ConstantsIndexBuilding.h +++ b/src/index/ConstantsIndexBuilding.h @@ -63,7 +63,7 @@ constexpr inline std::string_view TMP_BASENAME_COMPRESSION = ".tmp.for-prefix-compression"; // _________________________________________________________________ -constexpr inline std::string_view INTERNAL_INDEX_INFIX = ".internal"; +constexpr inline std::string_view QLEVER_INTERNAL_INDEX_INFIX = ".internal"; // _________________________________________________________________ // The degree of parallelism that is used for the index building step, where the diff --git a/src/index/IndexImpl.cpp b/src/index/IndexImpl.cpp index cd12e58b6..7f5e479f5 100644 --- a/src/index/IndexImpl.cpp +++ b/src/index/IndexImpl.cpp @@ -268,7 +268,7 @@ std::pair IndexImpl::createInternalPSOandPOS( auto&& internalTriplesPsoSorter) { auto onDiskBaseBackup = onDiskBase_; auto configurationJsonBackup = configurationJson_; - onDiskBase_.append(INTERNAL_INDEX_INFIX); + onDiskBase_.append(QLEVER_INTERNAL_INDEX_INFIX); auto internalTriplesUnique = ad_utility::uniqueBlockView( internalTriplesPsoSorter.template getSortedBlocks<0>()); createPSOAndPOSImpl(NumColumnsIndexBuilding, std::move(internalTriplesUnique), @@ -560,7 +560,7 @@ IndexBuilderDataAsStxxlVector IndexImpl::passFileForVocabulary( idOfHasPatternDuringIndexBuilding_ = mergeRes.specialIdMapping().at(HAS_PATTERN_PREDICATE); idOfInternalGraphDuringIndexBuilding_ = - mergeRes.specialIdMapping().at(INTERNAL_GRAPH_IRI); + mergeRes.specialIdMapping().at(QLEVER_INTERNAL_GRAPH_IRI); LOG(INFO) << "Number of words in external vocabulary: " << res.vocabularyMetaData_.numWordsTotal() - sizeInternalVocabulary << std::endl; @@ -858,7 +858,7 @@ void IndexImpl::createFromOnDiskIndex(const string& onDiskBase) { << vocab_.size() << std::endl; auto range1 = - vocab_.prefixRanges(absl::StrCat("<", INTERNAL_PREDICATE_PREFIX)); + vocab_.prefixRanges(QLEVER_INTERNAL_PREFIX_IRI_WITHOUT_CLOSING_BRACKET); auto range2 = vocab_.prefixRanges("@"); auto isInternalId = [range1, range2](Id id) { // TODO What about internal vocab stuff for update queries? this @@ -1464,7 +1464,7 @@ size_t IndexImpl::getCardinality(const TripleComponent& comp, // or objects anyway. // TODO Find out what the effect of this special case is for the // query planning. - if (comp == INTERNAL_TEXT_MATCH_PREDICATE) { + if (comp == QLEVER_INTERNAL_TEXT_MATCH_PREDICATE) { return TEXT_PREDICATE_CARDINALITY_ESTIMATE; } if (std::optional relId = comp.toValueId(getVocab()); relId.has_value()) { diff --git a/src/index/Permutation.cpp b/src/index/Permutation.cpp index 0b173e0bd..16f5113d6 100644 --- a/src/index/Permutation.cpp +++ b/src/index/Permutation.cpp @@ -26,7 +26,8 @@ void Permutation::loadFromDisk(const std::string& onDiskBase, internalPermutation_ = std::make_unique(permutation_, allocator_); internalPermutation_->loadFromDisk( - absl::StrCat(onDiskBase, INTERNAL_INDEX_INFIX), isInternalId_, false); + absl::StrCat(onDiskBase, QLEVER_INTERNAL_INDEX_INFIX), isInternalId_, + false); } if constexpr (MetaData::isMmapBased_) { meta_.setup(onDiskBase + ".index" + fileSuffix_ + MMAP_FILE_SUFFIX, diff --git a/src/index/Vocabulary.cpp b/src/index/Vocabulary.cpp index 9e64e0a23..9afc172f1 100644 --- a/src/index/Vocabulary.cpp +++ b/src/index/Vocabulary.cpp @@ -111,7 +111,7 @@ bool Vocabulary::shouldEntityBeExternalized( // Never externalize the internal IRIs as they are sometimes added before or // after the externalization happens and we thus get inconsistent behavior // etc. for `ql:langtag`. - if (word.starts_with(INTERNAL_ENTITIES_URI_PREFIX)) { + if (word.starts_with(QLEVER_INTERNAL_PREFIX_IRI_WITHOUT_CLOSING_BRACKET)) { return false; } // Never externalize the special IRIs starting with `@` (for example, diff --git a/src/index/VocabularyMerger.h b/src/index/VocabularyMerger.h index 3c171d2c6..8b9322796 100644 --- a/src/index/VocabularyMerger.h +++ b/src/index/VocabularyMerger.h @@ -125,7 +125,8 @@ struct VocabularyMetaData { size_t numBlankNodesTotal_ = 0; IdRangeForPrefix langTaggedPredicates_{ std::string{ad_utility::languageTaggedPredicatePrefix}}; - IdRangeForPrefix internalEntities_{std::string{INTERNAL_ENTITIES_URI_PREFIX}}; + IdRangeForPrefix internalEntities_{ + std::string{QLEVER_INTERNAL_PREFIX_IRI_WITHOUT_CLOSING_BRACKET}}; ad_utility::HashMap specialIdMapping_; const ad_utility::HashMap* globalSpecialIds_ = diff --git a/src/parser/ParsedQuery.cpp b/src/parser/ParsedQuery.cpp index c07f25768..c9496608e 100644 --- a/src/parser/ParsedQuery.cpp +++ b/src/parser/ParsedQuery.cpp @@ -253,7 +253,7 @@ void ParsedQuery::registerVariablesVisibleInQueryBody( // _____________________________________________________________________________ void ParsedQuery::registerVariableVisibleInQueryBody(const Variable& variable) { auto addVariable = [&variable](auto& clause) { - if (!variable.name().starts_with(INTERNAL_VARIABLE_PREFIX)) { + if (!variable.name().starts_with(QLEVER_INTERNAL_VARIABLE_PREFIX)) { clause.addVisibleVariable(variable); } }; @@ -285,7 +285,8 @@ void ParsedQuery::GraphPattern::addLanguageFilter(const Variable& variable, if (triple.o_ == variable && (triple.p_._operation == PropertyPath::Operation::IRI && !isVariable(triple.p_)) && - !triple.p_._iri.starts_with(INTERNAL_ENTITIES_URI_PREFIX)) { + !triple.p_._iri.starts_with( + QLEVER_INTERNAL_PREFIX_IRI_WITHOUT_CLOSING_BRACKET)) { matchingTriples.push_back(&triple); } } @@ -492,14 +493,14 @@ void ParsedQuery::addOrderByClause(OrderClause orderClause, bool isGroupBy, // ________________________________________________________________ Variable ParsedQuery::getNewInternalVariable() { - auto variable = - Variable{absl::StrCat(INTERNAL_VARIABLE_PREFIX, numInternalVariables_)}; + auto variable = Variable{ + absl::StrCat(QLEVER_INTERNAL_VARIABLE_PREFIX, numInternalVariables_)}; numInternalVariables_++; return variable; } Variable ParsedQuery::blankNodeToInternalVariable(std::string_view blankNode) { AD_CONTRACT_CHECK(blankNode.starts_with("_:")); - return Variable{ - absl::StrCat(INTERNAL_BLANKNODE_VARIABLE_PREFIX, blankNode.substr(2))}; + return Variable{absl::StrCat(QLEVER_INTERNAL_BLANKNODE_VARIABLE_PREFIX, + blankNode.substr(2))}; } diff --git a/src/parser/SparqlParser.cpp b/src/parser/SparqlParser.cpp index c043c6905..e75855fe9 100644 --- a/src/parser/SparqlParser.cpp +++ b/src/parser/SparqlParser.cpp @@ -14,7 +14,7 @@ ParsedQuery SparqlParser::parseQuery(std::string query) { using S = std::string; sparqlParserHelpers::ParserAndVisitor p{ std::move(query), - {{S{INTERNAL_PREDICATE_PREFIX_NAME}, S{INTERNAL_PREDICATE_PREFIX_IRI}}}}; + {{S{QLEVER_INTERNAL_PREFIX_NAME}, S{QLEVER_INTERNAL_PREFIX_IRI}}}}; // Note: `AntlrParser::query` is a method of `AntlrParser` (which is an alias // for `SparqlAutomaticParser`) that returns the `QueryContext*` for the whole // query. diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index d1f0242cd..7e8ae8fac 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -87,6 +87,7 @@ ExpressionPtr Visitor::processIriFunctionCall( return false; } }; + // Helper lambda that checks the number of arguments and throws an error // if it's not right. The `functionName` and `prefixName` are used for the // error message. @@ -102,6 +103,7 @@ ExpressionPtr Visitor::processIriFunctionCall( numArgs == 1 ? " argument" : " arguments")); } }; + // Geo functions. if (checkPrefix(GEOF_PREFIX)) { if (functionName == "distance") { @@ -115,7 +117,10 @@ ExpressionPtr Visitor::processIriFunctionCall( checkNumArgs(1); return sparqlExpression::makeLatitudeExpression(std::move(argList[0])); } - } else if (checkPrefix(MATH_PREFIX)) { + } + + // Math functions. + if (checkPrefix(MATH_PREFIX)) { if (functionName == "log") { checkNumArgs(1); return sparqlExpression::makeLogExpression(std::move(argList[0])); @@ -139,7 +144,10 @@ ExpressionPtr Visitor::processIriFunctionCall( return sparqlExpression::makePowExpression(std::move(argList[0]), std::move(argList[1])); } - } else if (checkPrefix(XSD_PREFIX)) { + } + + // XSD conversion functions. + if (checkPrefix(XSD_PREFIX)) { if (functionName == "integer" || functionName == "int") { checkNumArgs(1); return sparqlExpression::makeConvertToIntExpression( @@ -150,6 +158,18 @@ ExpressionPtr Visitor::processIriFunctionCall( std::move(argList[0])); } } + + // QLever-internal functions. + // + // NOTE: Predicates like `ql:has-predicate` etc. are handled elsewhere. + if (checkPrefix(QL_PREFIX)) { + if (functionName == "isGeoPoint") { + checkNumArgs(1); + return sparqlExpression::makeIsGeoPointExpression(std::move(argList[0])); + } + } + + // If none of the above matched, report unknown function. reportNotSupported(ctx, "Function \""s + iri.toStringRepresentation() + "\" is"); } diff --git a/src/util/Conversions.cpp b/src/util/Conversions.cpp index 1d56040aa..8c2fe1600 100644 --- a/src/util/Conversions.cpp +++ b/src/util/Conversions.cpp @@ -1,6 +1,6 @@ -// Copyright 2022, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Author: Johannes Kalmbach +// Copyright 2022 - 2024, University of Freiburg +// Chair of Algorithms and Data Structures +// Author: Johannes Kalmbach #include "util/Conversions.h" @@ -23,7 +23,7 @@ namespace ad_utility { // _________________________________________________________ triple_component::Iri convertLangtagToEntityUri(const string& tag) { - return triple_component::Iri::fromIriref(makeInternalIri("@", tag)); + return triple_component::Iri::fromIriref(makeQleverInternalIri("@", tag)); } // _________________________________________________________ diff --git a/test/AggregateExpressionTest.cpp b/test/AggregateExpressionTest.cpp index af53e45ed..297754881 100644 --- a/test/AggregateExpressionTest.cpp +++ b/test/AggregateExpressionTest.cpp @@ -196,7 +196,7 @@ TEST(AggregateExpression, CountStar) { // This variable is internal, so it doesn't count towards the `COUNT(DISTINCT // *)` and doesn't change the result. t.varToColMap[Variable{ - absl::StrCat(INTERNAL_VARIABLE_PREFIX, "someInternalVar")}] = { + absl::StrCat(QLEVER_INTERNAL_VARIABLE_PREFIX, "someInternalVar")}] = { 0, ColumnIndexAndTypeInfo::UndefStatus::AlwaysDefined}; t.qec->getQueryTreeCache().clearAll(); EXPECT_THAT(m, matcher(totalSize)); diff --git a/test/ConstantsTest.cpp b/test/ConstantsTest.cpp index 4c868cdd0..9e19c5ac2 100644 --- a/test/ConstantsTest.cpp +++ b/test/ConstantsTest.cpp @@ -26,9 +26,9 @@ TEST(Constants, testDefaultQueryTimeoutIsStriclyPositive) { EXPECT_NO_THROW(RuntimeParameters().set<"default-query-timeout">(1s)); } -TEST(Constants, makeInternalIri) { - EXPECT_EQ(makeInternalIri("hi", "-bye"), - (makeInternalIriConst<"hi", "-bye">())); - EXPECT_EQ(makeInternalIri("hi", "-bye"), +TEST(Constants, makeQleverInternalIri) { + EXPECT_EQ(makeQleverInternalIri("hi", "-bye"), + (makeQleverInternalIriConst<"hi", "-bye">())); + EXPECT_EQ(makeQleverInternalIri("hi", "-bye"), ""); } diff --git a/test/QueryPlannerTest.cpp b/test/QueryPlannerTest.cpp index dfff1ac36..adb099ea5 100644 --- a/test/QueryPlannerTest.cpp +++ b/test/QueryPlannerTest.cpp @@ -710,7 +710,7 @@ namespace { // A helper function to recreate the internal variables added by the query // planner for transitive paths. std::string internalVar(int i) { - return absl::StrCat(INTERNAL_VARIABLE_QUERY_PLANNER_PREFIX, i); + return absl::StrCat(QLEVER_INTERNAL_VARIABLE_QUERY_PLANNER_PREFIX, i); } } // namespace diff --git a/test/SparqlAntlrParserTest.cpp b/test/SparqlAntlrParserTest.cpp index e19a6a2d3..06e8ae3a7 100644 --- a/test/SparqlAntlrParserTest.cpp +++ b/test/SparqlAntlrParserTest.cpp @@ -43,8 +43,8 @@ auto iri = ad_utility::testing::iri; auto lit = ad_utility::testing::tripleComponentLiteral; const ad_utility::HashMap defaultPrefixMap{ - {std::string{INTERNAL_PREDICATE_PREFIX_NAME}, - std::string{INTERNAL_PREDICATE_PREFIX_IRI}}}; + {std::string{QLEVER_INTERNAL_PREFIX_NAME}, + std::string{QLEVER_INTERNAL_PREFIX_IRI}}}; template auto parse = @@ -817,9 +817,10 @@ TEST(SparqlParser, triplesSameSubjectPath) { ExpectCompleteParse<&Parser::triplesSameSubjectPath, true>{}; expectTriplesConstruct("_:1 ?baz", {{BlankNode(false, "1"), PathIri(""), Var{"?baz"}}}); - expectTriples("_:one ?baz", - {{Var{absl::StrCat(INTERNAL_BLANKNODE_VARIABLE_PREFIX, "one")}, - PathIri(""), Var{"?baz"}}}); + expectTriples( + "_:one ?baz", + {{Var{absl::StrCat(QLEVER_INTERNAL_BLANKNODE_VARIABLE_PREFIX, "one")}, + PathIri(""), Var{"?baz"}}}); expectTriples("10.0 true", {{Literal(10.0), PathIri(""), Literal(true)}}); expectTriples( @@ -1730,12 +1731,15 @@ TEST(SparqlParser, FunctionCall) { auto geof = absl::StrCat("<", GEOF_PREFIX.second); auto math = absl::StrCat("<", MATH_PREFIX.second); auto xsd = absl::StrCat("<", XSD_PREFIX.second); + auto ql = absl::StrCat("<", QL_PREFIX.second); // Correct function calls. Check that the parser picks the correct expression. expectFunctionCall(absl::StrCat(geof, "latitude>(?x)"), matchUnary(&makeLatitudeExpression)); expectFunctionCall(absl::StrCat(geof, "longitude>(?x)"), matchUnary(&makeLongitudeExpression)); + expectFunctionCall(absl::StrCat(ql, "isGeoPoint>(?x)"), + matchUnary(&makeIsGeoPointExpression)); expectFunctionCall( absl::StrCat(geof, "distance>(?a, ?b)"), matchNary(&makeDistExpression, Variable{"?a"}, Variable{"?b"})); @@ -1765,10 +1769,14 @@ TEST(SparqlParser, FunctionCall) { // Wrong number of arguments. expectFunctionCallFails(absl::StrCat(geof, "distance>(?a)")); - // Unknown function with `geof:`, `math:`, or `xsd:` prefix. + expectFunctionCallFails(absl::StrCat(geof, "distance>(?a, ?b, ?c)")); + + // Unknown function with `geof:`, `math:`, `xsd:`, or `ql` prefix. expectFunctionCallFails(absl::StrCat(geof, "nada>(?x)")); expectFunctionCallFails(absl::StrCat(math, "nada>(?x)")); expectFunctionCallFails(absl::StrCat(xsd, "nada>(?x)")); + expectFunctionCallFails(absl::StrCat(ql, "nada>(?x)")); + // Prefix for which no function is known. std::string prefixNexistepas = ""; expectFunctionCallFails(absl::StrCat(prefixNexistepas, "nada>(?x)")); diff --git a/test/SparqlAntlrParserTestHelpers.h b/test/SparqlAntlrParserTestHelpers.h index 788ece3fb..43c0de28a 100644 --- a/test/SparqlAntlrParserTestHelpers.h +++ b/test/SparqlAntlrParserTestHelpers.h @@ -287,10 +287,10 @@ inline auto BlankNode = [](bool generated, const std::string& label) { }; inline auto InternalVariable = [](const std::string& label) { - return MultiVariantWith( - testing::AllOf(AD_PROPERTY(::Variable, name, - testing::StartsWith(INTERNAL_VARIABLE_PREFIX)), - AD_PROPERTY(::Variable, name, testing::EndsWith(label)))); + return MultiVariantWith(testing::AllOf( + AD_PROPERTY(::Variable, name, + testing::StartsWith(QLEVER_INTERNAL_VARIABLE_PREFIX)), + AD_PROPERTY(::Variable, name, testing::EndsWith(label)))); }; // _____________________________________________________________________________ diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index ab5b3add6..285f990bc 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -1117,6 +1117,7 @@ TEST(SparqlExpression, testToNumericExpression) { TEST(SparqlExpression, geoSparqlExpressions) { auto checkLat = testUnaryExpression<&makeLatitudeExpression>; auto checkLong = testUnaryExpression<&makeLongitudeExpression>; + auto checkIsGeoPoint = testUnaryExpression<&makeIsGeoPointExpression>; auto checkDist = std::bind_front(testNaryExpression, &makeDistExpression); auto p = GeoPoint(26.8, 24.3); @@ -1136,9 +1137,11 @@ TEST(SparqlExpression, geoSparqlExpressions) { checkLat(v, vLat); checkLong(v, vLng); + checkIsGeoPoint(v, B(true)); checkDist(D(0.0), v, v); checkLat(idOrLitOrStringVec({"NotAPoint", I(12)}), Ids{U, U}); checkLong(idOrLitOrStringVec({D(4.2), "NotAPoint"}), Ids{U, U}); + checkIsGeoPoint(IdOrLiteralOrIri{lit("NotAPoint")}, B(false)); checkDist(U, v, IdOrLiteralOrIri{I(12)}); checkDist(U, IdOrLiteralOrIri{I(12)}, v); checkDist(U, v, IdOrLiteralOrIri{lit("NotAPoint")});