From 3213257bf7fc9dd18817b9c5072ab732fd6274f0 Mon Sep 17 00:00:00 2001 From: Felix Meisen <85636111+Flixtastic@users.noreply.github.com> Date: Wed, 22 Jan 2025 08:59:44 +0100 Subject: [PATCH 01/28] Refactor the parsing of the text index builder (#1695) Split up large functions, modernize code, choose better names and add some documentation --- src/global/IndexTypes.h | 1 + src/index/IndexImpl.Text.cpp | 139 ++++++++-------- src/index/IndexImpl.h | 18 ++- src/parser/CMakeLists.txt | 2 +- src/parser/ContextFileParser.cpp | 46 ------ src/parser/ContextFileParser.h | 45 ------ src/parser/WordsAndDocsFileParser.cpp | 61 +++++++ src/parser/WordsAndDocsFileParser.h | 192 +++++++++++++++++++++++ test/CMakeLists.txt | 2 +- test/ContextFileParserTest.cpp | 59 ------- test/WordsAndDocsFileLineCreator.h | 22 +++ test/WordsAndDocsFileParserTest.cpp | 165 +++++++++++++++++++ test/engine/TextIndexScanForWordTest.cpp | 83 +++++----- test/engine/TextIndexScanTestHelpers.h | 14 -- 14 files changed, 571 insertions(+), 278 deletions(-) delete mode 100644 src/parser/ContextFileParser.cpp delete mode 100644 src/parser/ContextFileParser.h create mode 100644 src/parser/WordsAndDocsFileParser.cpp create mode 100644 src/parser/WordsAndDocsFileParser.h delete mode 100644 test/ContextFileParserTest.cpp create mode 100644 test/WordsAndDocsFileLineCreator.h create mode 100644 test/WordsAndDocsFileParserTest.cpp diff --git a/src/global/IndexTypes.h b/src/global/IndexTypes.h index 08ee960d00..4868e59694 100644 --- a/src/global/IndexTypes.h +++ b/src/global/IndexTypes.h @@ -16,3 +16,4 @@ using LocalVocabIndex = const LocalVocabEntry*; using TextRecordIndex = ad_utility::TypedIndex; using WordVocabIndex = ad_utility::TypedIndex; using BlankNodeIndex = ad_utility::TypedIndex; +using DocumentIndex = ad_utility::TypedIndex; diff --git a/src/index/IndexImpl.Text.cpp b/src/index/IndexImpl.Text.cpp index 76c0015974..d1e6a70777 100644 --- a/src/index/IndexImpl.Text.cpp +++ b/src/index/IndexImpl.Text.cpp @@ -17,39 +17,22 @@ #include "backports/algorithm.h" #include "engine/CallFixedSize.h" #include "index/FTSAlgorithms.h" -#include "parser/ContextFileParser.h" +#include "parser/WordsAndDocsFileParser.h" #include "util/Conversions.h" #include "util/Simple8bCode.h" -namespace { - -// Custom delimiter class for tokenization of literals using `absl::StrSplit`. -// The `Find` function returns the next delimiter in `text` after the given -// `pos` or an empty substring if there is no next delimiter. -struct LiteralsTokenizationDelimiter { - absl::string_view Find(absl::string_view text, size_t pos) { - auto isWordChar = [](char c) -> bool { return std::isalnum(c); }; - auto found = std::find_if_not(text.begin() + pos, text.end(), isWordChar); - if (found == text.end()) return text.substr(text.size()); - return {found, found + 1}; - } -}; - -} // namespace - // _____________________________________________________________________________ -cppcoro::generator IndexImpl::wordsInTextRecords( - const std::string& contextFile, bool addWordsFromLiterals) { +cppcoro::generator IndexImpl::wordsInTextRecords( + std::string contextFile, bool addWordsFromLiterals) const { auto localeManager = textVocab_.getLocaleManager(); // ROUND 1: If context file aka wordsfile is not empty, read words from there. // Remember the last context id for the (optional) second round. TextRecordIndex contextId = TextRecordIndex::make(0); if (!contextFile.empty()) { - ContextFileParser::Line line; - ContextFileParser p(contextFile, localeManager); + WordsFileParser p(contextFile, localeManager); ad_utility::HashSet items; - while (p.getLine(line)) { - contextId = line._contextId; + for (auto& line : p) { + contextId = line.contextId_; co_yield line; } if (contextId > TextRecordIndex::make(0)) { @@ -65,15 +48,13 @@ cppcoro::generator IndexImpl::wordsInTextRecords( if (!isLiteral(text)) { continue; } - ContextFileParser::Line entityLine{text, true, contextId, 1, true}; + WordsFileLine entityLine{text, true, contextId, 1, true}; co_yield entityLine; std::string_view textView = text; textView = textView.substr(0, textView.rfind('"')); textView.remove_prefix(1); - for (auto word : absl::StrSplit(textView, LiteralsTokenizationDelimiter{}, - absl::SkipEmpty{})) { - auto wordNormalized = localeManager.getLowercaseUtf8(word); - ContextFileParser::Line wordLine{wordNormalized, false, contextId, 1}; + for (auto word : tokenizeAndNormalizeText(textView, localeManager)) { + WordsFileLine wordLine{std::move(word), false, contextId, 1}; co_yield wordLine; } contextId = contextId.incremented(); @@ -81,6 +62,56 @@ cppcoro::generator IndexImpl::wordsInTextRecords( } } +// _____________________________________________________________________________ +void IndexImpl::processEntityCaseDuringInvertedListProcessing( + const WordsFileLine& line, + ad_utility::HashMap& entitiesInContext, size_t& nofLiterals, + size_t& entityNotFoundErrorMsgCount) const { + VocabIndex eid; + // TODO Currently only IRIs and strings from the vocabulary can + // be tagged entities in the text index (no doubles, ints, etc). + if (getVocab().getId(line.word_, &eid)) { + // Note that `entitiesInContext` is a HashMap, so the `Id`s don't have + // to be contiguous. + entitiesInContext[Id::makeFromVocabIndex(eid)] += line.score_; + if (line.isLiteralEntity_) { + ++nofLiterals; + } + } else { + logEntityNotFound(line.word_, entityNotFoundErrorMsgCount); + } +} + +// _____________________________________________________________________________ +void IndexImpl::processWordCaseDuringInvertedListProcessing( + const WordsFileLine& line, + ad_utility::HashMap& wordsInContext) const { + // TODO Let the `textVocab_` return a `WordIndex` directly. + WordVocabIndex vid; + bool ret = textVocab_.getId(line.word_, &vid); + WordIndex wid = vid.get(); + if (!ret) { + LOG(ERROR) << "ERROR: word \"" << line.word_ << "\" " + << "not found in textVocab. Terminating\n"; + AD_FAIL(); + } + wordsInContext[wid] += line.score_; +} + +// _____________________________________________________________________________ +void IndexImpl::logEntityNotFound(const string& word, + size_t& entityNotFoundErrorMsgCount) const { + if (entityNotFoundErrorMsgCount < 20) { + LOG(WARN) << "Entity from text not in KB: " << word << '\n'; + if (++entityNotFoundErrorMsgCount == 20) { + LOG(WARN) << "There are more entities not in the KB..." + << " suppressing further warnings...\n"; + } + } else { + entityNotFoundErrorMsgCount++; + } +} + // _____________________________________________________________________________ void IndexImpl::addTextFromContextFile(const string& contextFile, bool addWordsFromLiterals) { @@ -214,12 +245,12 @@ size_t IndexImpl::processWordsForVocabulary(string const& contextFile, for (auto line : wordsInTextRecords(contextFile, addWordsFromLiterals)) { ++numLines; // LOG(INFO) << "LINE: " - // << std::setw(50) << line._word << " " - // << line._isEntity << "\t" - // << line._contextId.get() << "\t" - // << line._score << std::endl; - if (!line._isEntity) { - distinctWords.insert(line._word); + // << std::setw(50) << line.word_ << " " + // << line.isEntity_ << "\t" + // << line.contextId_.get() << "\t" + // << line.score_ << std::endl; + if (!line.isEntity_) { + distinctWords.insert(line.word_); } } textVocab_.createFromSet(distinctWords, onDiskBase_ + ".text.vocabulary"); @@ -243,49 +274,21 @@ void IndexImpl::processWordsForInvertedLists(const string& contextFile, size_t nofLiterals = 0; for (auto line : wordsInTextRecords(contextFile, addWordsFromLiterals)) { - if (line._contextId != currentContext) { + if (line.contextId_ != currentContext) { ++nofContexts; addContextToVector(writer, currentContext, wordsInContext, entitiesInContext); - currentContext = line._contextId; + currentContext = line.contextId_; wordsInContext.clear(); entitiesInContext.clear(); } - if (line._isEntity) { + if (line.isEntity_) { ++nofEntityPostings; - // TODO Currently only IRIs and strings from the vocabulary can - // be tagged entities in the text index (no doubles, ints, etc). - VocabIndex eid; - if (getVocab().getId(line._word, &eid)) { - // Note that `entitiesInContext` is a HashMap, so the `Id`s don't have - // to be contiguous. - entitiesInContext[Id::makeFromVocabIndex(eid)] += line._score; - if (line._isLiteralEntity) { - ++nofLiterals; - } - } else { - if (entityNotFoundErrorMsgCount < 20) { - LOG(WARN) << "Entity from text not in KB: " << line._word << '\n'; - if (++entityNotFoundErrorMsgCount == 20) { - LOG(WARN) << "There are more entities not in the KB..." - << " suppressing further warnings...\n"; - } - } else { - entityNotFoundErrorMsgCount++; - } - } + processEntityCaseDuringInvertedListProcessing( + line, entitiesInContext, nofLiterals, entityNotFoundErrorMsgCount); } else { ++nofWordPostings; - // TODO Let the `textVocab_` return a `WordIndex` directly. - WordVocabIndex vid; - bool ret = textVocab_.getId(line._word, &vid); - WordIndex wid = vid.get(); - if (!ret) { - LOG(ERROR) << "ERROR: word \"" << line._word << "\" " - << "not found in textVocab. Terminating\n"; - AD_FAIL(); - } - wordsInContext[wid] += line._score; + processWordCaseDuringInvertedListProcessing(line, wordsInContext); } } if (entityNotFoundErrorMsgCount > 0) { diff --git a/src/index/IndexImpl.h b/src/index/IndexImpl.h index d9ec19eb14..ac0003db87 100644 --- a/src/index/IndexImpl.h +++ b/src/index/IndexImpl.h @@ -29,9 +29,9 @@ #include "index/TextMetaData.h" #include "index/Vocabulary.h" #include "index/VocabularyMerger.h" -#include "parser/ContextFileParser.h" #include "parser/RdfParser.h" #include "parser/TripleComponent.h" +#include "parser/WordsAndDocsFileParser.h" #include "util/BufferedVector.h" #include "util/CancellationHandle.h" #include "util/File.h" @@ -521,8 +521,20 @@ class IndexImpl { // TODO: So far, this is limited to the internal vocabulary (still in the // testing phase, once it works, it should be easy to include the IRIs and // literals from the external vocabulary as well). - cppcoro::generator wordsInTextRecords( - const std::string& contextFile, bool addWordsFromLiterals); + cppcoro::generator wordsInTextRecords( + std::string contextFile, bool addWordsFromLiterals) const; + + void processEntityCaseDuringInvertedListProcessing( + const WordsFileLine& line, + ad_utility::HashMap& entitiesInContxt, size_t& nofLiterals, + size_t& entityNotFoundErrorMsgCount) const; + + void processWordCaseDuringInvertedListProcessing( + const WordsFileLine& line, + ad_utility::HashMap& wordsInContext) const; + + void logEntityNotFound(const string& word, + size_t& entityNotFoundErrorMsgCount) const; size_t processWordsForVocabulary(const string& contextFile, bool addWordsFromLiterals); diff --git a/src/parser/CMakeLists.txt b/src/parser/CMakeLists.txt index be4b3db44c..6fa123a793 100644 --- a/src/parser/CMakeLists.txt +++ b/src/parser/CMakeLists.txt @@ -10,7 +10,7 @@ add_library(parser ParsedQuery.cpp RdfParser.cpp Tokenizer.cpp - ContextFileParser.cpp + WordsAndDocsFileParser.cpp TurtleTokenId.h ParallelBuffer.cpp SparqlParserHelpers.cpp diff --git a/src/parser/ContextFileParser.cpp b/src/parser/ContextFileParser.cpp deleted file mode 100644 index 523bde486b..0000000000 --- a/src/parser/ContextFileParser.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Author: Björn Buchhold (buchhold@informatik.uni-freiburg.de) - -#include "./ContextFileParser.h" - -#include - -#include "../util/Exception.h" -#include "../util/StringUtils.h" - -// _____________________________________________________________________________ -ContextFileParser::ContextFileParser(const string& contextFile, - LocaleManager localeManager) - : _in(contextFile), _localeManager(std::move(localeManager)) {} - -// _____________________________________________________________________________ -ContextFileParser::~ContextFileParser() { _in.close(); } - -// _____________________________________________________________________________ -bool ContextFileParser::getLine(ContextFileParser::Line& line) { - string l; - if (std::getline(_in, l)) { - size_t i = l.find('\t'); - assert(i != string::npos); - size_t j = i + 2; - assert(j + 3 < l.size()); - size_t k = l.find('\t', j + 2); - assert(k != string::npos); - line._isEntity = (l[i + 1] == '1'); - line._word = - (line._isEntity ? l.substr(0, i) - : _localeManager.getLowercaseUtf8(l.substr(0, i))); - line._contextId = - TextRecordIndex::make(atol(l.substr(j + 1, k - j - 1).c_str())); - line._score = static_cast(atol(l.substr(k + 1).c_str())); -#ifndef NDEBUG - if (_lastCId > line._contextId) { - AD_THROW("ContextFile has to be sorted by context Id."); - } - _lastCId = line._contextId; -#endif - return true; - } - return false; -} diff --git a/src/parser/ContextFileParser.h b/src/parser/ContextFileParser.h deleted file mode 100644 index ba8d7bac9c..0000000000 --- a/src/parser/ContextFileParser.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2015, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Author: Björn Buchhold (buchhold@informatik.uni-freiburg.de) - -#pragma once - -#include - -#include -#include - -#include "../global/Id.h" -#include "../index/StringSortComparator.h" - -using std::string; - -class ContextFileParser { - public: - struct Line { - string _word; - bool _isEntity; - TextRecordIndex _contextId; - Score _score; - bool _isLiteralEntity = false; - }; - - explicit ContextFileParser(const string& contextFile, - LocaleManager localeManager); - ~ContextFileParser(); - // Don't allow copy & assignment - explicit ContextFileParser(const ContextFileParser& other) = delete; - ContextFileParser& operator=(const ContextFileParser& other) = delete; - - // Get the next line from the file. - // Returns true if something was stored. - bool getLine(Line&); - - private: - std::ifstream _in; -#ifndef NDEBUG - // Only used for sanity checks in debug builds - TextRecordIndex _lastCId = TextRecordIndex::make(0); -#endif - LocaleManager _localeManager; -}; diff --git a/src/parser/WordsAndDocsFileParser.cpp b/src/parser/WordsAndDocsFileParser.cpp new file mode 100644 index 0000000000..e7d36974c6 --- /dev/null +++ b/src/parser/WordsAndDocsFileParser.cpp @@ -0,0 +1,61 @@ +// Copyright 2015, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Björn Buchhold (buchhold@informatik.uni-freiburg.de) +// Felix Meisen (fesemeisen@outlook.de) + +#include "parser/WordsAndDocsFileParser.h" + +#include + +#include "util/Exception.h" +#include "util/StringUtils.h" + +// _____________________________________________________________________________ +WordsAndDocsFileParser::WordsAndDocsFileParser( + const string& wordsOrDocsFile, const LocaleManager& localeManager) + : in_(wordsOrDocsFile), localeManager_(localeManager) {} + +// _____________________________________________________________________________ +ad_utility::InputRangeFromGet::Storage WordsFileParser::get() { + WordsFileLine line; + string l; + if (!std::getline(getInputStream(), l)) { + return std::nullopt; + } + std::string_view lineView(l); + size_t i = lineView.find('\t'); + assert(i != string::npos); + size_t j = i + 2; + assert(j + 3 < lineView.size()); + size_t k = lineView.find('\t', j + 2); + assert(k != string::npos); + line.isEntity_ = (lineView[i + 1] == '1'); + line.word_ = + (line.isEntity_ + ? lineView.substr(0, i) + : getLocaleManager().getLowercaseUtf8(lineView.substr(0, i))); + line.contextId_ = + TextRecordIndex::make(atol(lineView.substr(j + 1, k - j - 1).data())); + line.score_ = static_cast(atol(lineView.substr(k + 1).data())); +#ifndef NDEBUG + if (lastCId_ > line.contextId_) { + AD_THROW("ContextFile has to be sorted by context Id."); + } + lastCId_ = line.contextId_; +#endif + return line; +} + +// _____________________________________________________________________________ +ad_utility::InputRangeFromGet::Storage DocsFileParser::get() { + string l; + if (!std::getline(getInputStream(), l)) { + return std::nullopt; + } + DocsFileLine line; + size_t i = l.find('\t'); + assert(i != string::npos); + line.docId_ = DocumentIndex::make(atol(l.substr(0, i).c_str())); + line.docContent_ = l.substr(i + 1); + return line; +} diff --git a/src/parser/WordsAndDocsFileParser.h b/src/parser/WordsAndDocsFileParser.h new file mode 100644 index 0000000000..1fc80523ff --- /dev/null +++ b/src/parser/WordsAndDocsFileParser.h @@ -0,0 +1,192 @@ +// Copyright 2015, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Björn Buchhold (buchhold@informatik.uni-freiburg.de) +// Felix Meisen (fesemeisen@outlook.de) + +#pragma once + +#include +#include + +#include +#include + +#include "global/Id.h" +#include "index/StringSortComparator.h" +#include "util/Iterators.h" +#include "util/Views.h" + +using std::string; + +/** + * @brief Represents a line in the words file. + * + * This struct holds information about a word or entity as it appears in the + * words file. + * + * The Fields are ordered in the same way the values follow in a line. + * Short field overview: string word_, bool isEntity, TextRecordIndex contextId, + * Score score_, bool isLiteralEntity (not found in + * wordsfile) + * + * @details + * + * Fields: + * - string word_: The string of the word, if it is an entity it will be + * . + * - bool isEntity_: True if the given word is an entity, false if it's a word. + * - TextRecordIndex contextId_: When creating the wordsfile docs from the + * docsfile get split into so called contexts. + * Those contexts overlap, meaning words and + * entities are covered multiple times. Each + * contextId corresponds to the next bigger or + * equal docId. + * - Score score_: Either 1 or 0 if isEntity is false. 0, 1, 100, 150 if + * isEntity is true. (this info is only constructed on the + * scientists.wordsfile.tsv) The score in the wordsfile is only + * relevant for the counting scoring metric. Because of the + * overlap of contexts the score is 1 if the word really has + * been seen for the first time and 0 if not. If a doc contains + * multiple mentions of a word there should be exactly as many + * wordsfile lines of that word with score 1 as there are + * mentions. The score for entities seems rather random and + * since no clear explanation of the creation of wordsfiles + * has been found yet they will stay rather random. + * - bool isLiteralEntity_: This does not directly stem from the wordsfile. + * When building the text index with literals, for + * every literal there will be WordsFileLines for all + * words in that literal. Additionally the whole + * literal itself will be added as word with isEntity + * being true. The need to count this comes only from + * a trick used in testing right now. To be specific + * the method getTextRecordFromResultTable + */ +struct WordsFileLine { + string word_; + bool isEntity_; + TextRecordIndex contextId_; + Score score_; + bool isLiteralEntity_ = false; +}; + +/** + * @brief Represents a line from the docsfile.tsv. + * + * This struct stores everything given in a line of the docsfile.tsv. + * + * The Fields are ordered in the same way the values follow in a line. + * Short field overview: DocumentIndex docId_, string docContent_ + * + * @details + * + * Fields: + * - DocumentIndex docId_: The docId is needed to build inverted indices for + * scoring and building of the docsDB. It is also used + * to return actual texts when searching for a word. + * The word (and entity) search returns a table with + * TextRecordIndex as type of one column. Those get + * mapped to the next bigger or equal docId which is + * then used to extract the text from the docsDB. + * - string docContent_: The whole text given after the first tab of a line of + * docsfile. + */ +struct DocsFileLine { + DocumentIndex docId_; + string docContent_; +}; + +// Custom delimiter class for tokenization of literals using `absl::StrSplit`. +// The `Find` function returns the next delimiter in `text` after the given +// `pos` or an empty substring if there is no next delimiter. +struct LiteralsTokenizationDelimiter { + absl::string_view Find(absl::string_view text, size_t pos) const { + auto isWordChar = [](char c) -> bool { return std::isalnum(c); }; + auto found = std::find_if_not(text.begin() + pos, text.end(), isWordChar); + if (found == text.end()) return text.substr(text.size()); + return {found, found + 1}; + } +}; + +/** + * @brief A function that can be used to tokenize and normalize a given text. + * @warning Both params are const refs where the original objects have to be + * kept alive during the usage of the returned object. + * @param text The text to be tokenized and normalized. + * @param localeManager The localeManager to be used for normalization. + * @details This function can be used in the following way: + * for (auto normalizedWord : tokenizeAndNormalizeText(text, localeManager)) { + * code; + * } + */ +inline auto tokenizeAndNormalizeText(std::string_view text, + const LocaleManager& localeManager) { + std::vector split{ + absl::StrSplit(text, LiteralsTokenizationDelimiter{}, absl::SkipEmpty{})}; + return ql::views::transform(ad_utility::OwningView{std::move(split)}, + [&localeManager](const auto& str) { + return localeManager.getLowercaseUtf8(str); + }); +} +/** + * @brief This class is the parent class of WordsFileParser and DocsFileParser + * + * @details It exists to reduce code duplication since the only difference + * between the child classes is the line type returned. + */ +class WordsAndDocsFileParser { + public: + explicit WordsAndDocsFileParser(const string& wordsOrDocsFile, + const LocaleManager& localeManager); + explicit WordsAndDocsFileParser(const WordsAndDocsFileParser& other) = delete; + WordsAndDocsFileParser& operator=(const WordsAndDocsFileParser& other) = + delete; + + protected: + std::ifstream& getInputStream() { return in_; } + const LocaleManager& getLocaleManager() const { return localeManager_; } + + private: + std::ifstream in_; + LocaleManager localeManager_; +}; + +/** + * @brief This class takes in the a pathToWordsFile and a localeManager. It then + * can be used to iterate the wordsFile while already normalizing the words + * using the localeManager. (If words are entities it doesn't normalize them) + * + * @details An object of this class can be iterated as follows: + * for (auto wordsFileLine : WordsFileParser{wordsFile, localeManager}) { + * code; + * } + * The type of the value returned when iterating is WordsFileLine + */ +class WordsFileParser : public WordsAndDocsFileParser, + public ad_utility::InputRangeFromGet { + public: + using WordsAndDocsFileParser::WordsAndDocsFileParser; + Storage get() override; + +#ifndef NDEBUG + private: + // Only used for sanity checks in debug builds + TextRecordIndex lastCId_ = TextRecordIndex::make(0); +#endif +}; + +/** + * @brief This class takes in the a pathToDocsFile and a localeManager. It then + * can be used to iterate over the docsFile to get the lines. + * + * @details An object of this class can be iterated as follows: + * for (auto docsFileLine : DocsFileParser{docsFile, localeManager}) { + * code; + * } + * The type of the value returned when iterating is DocsFileLine + */ +class DocsFileParser : public WordsAndDocsFileParser, + public ad_utility::InputRangeFromGet { + public: + using WordsAndDocsFileParser::WordsAndDocsFileParser; + Storage get() override; +}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bd375f4826..b9581312e8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -137,7 +137,7 @@ addLinkAndDiscoverTestSerial(FileTest) addLinkAndDiscoverTest(Simple8bTest) -addLinkAndDiscoverTest(ContextFileParserTest parser) +addLinkAndDiscoverTest(WordsAndDocsFileParserTest parser) addLinkAndDiscoverTest(IndexMetaDataTest index) diff --git a/test/ContextFileParserTest.cpp b/test/ContextFileParserTest.cpp deleted file mode 100644 index 2b27c0f34d..0000000000 --- a/test/ContextFileParserTest.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2015, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Author: Björn Buchhold (buchhold@informatik.uni-freiburg.de) - -#include - -#include -#include - -#include "../src/parser/ContextFileParser.h" - -TEST(ContextFileParserTest, getLineTest) { - char* locale = setlocale(LC_CTYPE, ""); - std::cout << "Set locale LC_CTYPE to: " << locale << std::endl; - - std::fstream f("_testtmp.contexts.tsv", std::ios_base::out); - f << "Foo\t0\t0\t2\n" - "foo\t0\t0\t2\n" - "Bär\t1\t0\t1\n" - "Äü\t0\t0\t1\n" - "X\t0\t1\t1\n"; - - f.close(); - ContextFileParser p("_testtmp.contexts.tsv", - LocaleManager("en", "US", false)); - ContextFileParser::Line a; - ASSERT_TRUE(p.getLine(a)); - ASSERT_EQ("foo", a._word); - ASSERT_FALSE(a._isEntity); - ASSERT_EQ(0u, a._contextId.get()); - ASSERT_EQ(2u, a._score); - - ASSERT_TRUE(p.getLine(a)); - ASSERT_EQ("foo", a._word); - ASSERT_FALSE(a._isEntity); - ASSERT_EQ(0u, a._contextId.get()); - ASSERT_EQ(2u, a._score); - - ASSERT_TRUE(p.getLine(a)); - ASSERT_EQ("Bär", a._word); - ASSERT_TRUE(a._isEntity); - ASSERT_EQ(0u, a._contextId.get()); - ASSERT_EQ(1u, a._score); - - ASSERT_TRUE(p.getLine(a)); - ASSERT_EQ("äü", a._word); - ASSERT_FALSE(a._isEntity); - ASSERT_EQ(0u, a._contextId.get()); - ASSERT_EQ(1u, a._score); - - ASSERT_TRUE(p.getLine(a)); - ASSERT_EQ("x", a._word); - ASSERT_FALSE(a._isEntity); - ASSERT_EQ(1u, a._contextId.get()); - ASSERT_EQ(1u, a._score); - - ASSERT_FALSE(p.getLine(a)); - remove("_testtmp.contexts.tsv"); -}; diff --git a/test/WordsAndDocsFileLineCreator.h b/test/WordsAndDocsFileLineCreator.h new file mode 100644 index 0000000000..cb151216fd --- /dev/null +++ b/test/WordsAndDocsFileLineCreator.h @@ -0,0 +1,22 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Felix Meisen (fesemeisen@outlook.de) + +#pragma once + +#include + +constexpr std::string_view inlineSeparator = "\t"; +constexpr std::string_view lineSeparator = "\n"; + +inline std::string createWordsFileLineAsString(std::string_view word, + bool isEntity, size_t contextId, + size_t score) { + return absl::StrCat(word, inlineSeparator, isEntity, inlineSeparator, + contextId, inlineSeparator, score, lineSeparator); +}; + +inline std::string createDocsFileLineAsString(size_t docId, + std::string_view docContent) { + return absl::StrCat(docId, inlineSeparator, docContent, lineSeparator); +}; diff --git a/test/WordsAndDocsFileParserTest.cpp b/test/WordsAndDocsFileParserTest.cpp new file mode 100644 index 0000000000..de7216ada7 --- /dev/null +++ b/test/WordsAndDocsFileParserTest.cpp @@ -0,0 +1,165 @@ +// Copyright 2015, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Björn Buchhold (buchhold@informatik.uni-freiburg.de) + +#include + +#include +#include + +#include "./WordsAndDocsFileLineCreator.h" +#include "parser/WordsAndDocsFileParser.h" + +// All lambdas and type aliases used in this file contained here +namespace { + +/// Type aliases + +// Word, isEntity, contextId, score +using WordLine = std::tuple; +using WordLineVec = std::vector; + +// docId, docContent +using DocLine = std::tuple; +using DocLineVec = std::vector; + +using StringVec = std::vector; + +/// Lambdas + +auto getLocaleManager = []() -> LocaleManager { + return LocaleManager("en", "US", false); +}; + +auto wordsFileLineToWordLine = + [](const WordsFileLine& wordsFileLine) -> WordLine { + return std::make_tuple(wordsFileLine.word_, wordsFileLine.isEntity_, + static_cast(wordsFileLine.contextId_.get()), + static_cast(wordsFileLine.score_)); +}; + +// Lambda that takes in a path to wordsFile to initialize the Parser and an +// expectedResult that is compared against the parsers outputs. +auto testWordsFileParser = [](const std::string& wordsFilePath, + const WordLineVec& expectedResult) { + size_t i = 0; + LocaleManager localeManager = getLocaleManager(); + for (auto wordsFileLine : WordsFileParser{wordsFilePath, localeManager}) { + ASSERT_TRUE(i < expectedResult.size()); + WordLine testLine = wordsFileLineToWordLine(wordsFileLine); + + // Not testing the whole tuples against each other to have a cleaner + // indication what exactly caused the assertion to fail + ASSERT_EQ(std::get<0>(testLine), std::get<0>(expectedResult.at(i))); + ASSERT_EQ(std::get<1>(testLine), std::get<1>(expectedResult.at(i))); + ASSERT_EQ(std::get<2>(testLine), std::get<2>(expectedResult.at(i))); + ASSERT_EQ(std::get<3>(testLine), std::get<3>(expectedResult.at(i))); + + ++i; + } + ASSERT_EQ(i, expectedResult.size()); +}; + +auto docsFileLineToDocLine = [](const DocsFileLine& docsFileLine) -> DocLine { + return std::make_tuple(static_cast(docsFileLine.docId_.get()), + docsFileLine.docContent_); +}; + +// Same as testWordsFileParser but for docsFile +auto testDocsFileParser = [](const std::string& docsFilePath, + const DocLineVec& expectedResult) { + size_t i = 0; + LocaleManager localeManager = getLocaleManager(); + for (auto docsFileLine : DocsFileParser{docsFilePath, localeManager}) { + ASSERT_TRUE(i < expectedResult.size()); + DocLine testLine = docsFileLineToDocLine(docsFileLine); + + // Not testing the whole tuples against each other to have a cleaner + // indication what exactly caused the assertion to fail + ASSERT_EQ(std::get<0>(testLine), std::get<0>(expectedResult.at(i))); + ASSERT_EQ(std::get<1>(testLine), std::get<1>(expectedResult.at(i))); + + ++i; + } +}; + +// Passing the testText as copy to make sure it stays alive during the usage of +// tokenizer +auto testTokenizeAndNormalizeText = [](std::string testText, + const StringVec& normalizedTextAsVec) { + size_t i = 0; + LocaleManager localeManager = getLocaleManager(); + for (auto normalizedWord : + tokenizeAndNormalizeText(testText, localeManager)) { + ASSERT_TRUE(i < normalizedTextAsVec.size()); + ASSERT_EQ(normalizedWord, normalizedTextAsVec.at(i)); + + ++i; + } + ASSERT_EQ(i, normalizedTextAsVec.size()); +}; + +} // namespace + +TEST(WordsAndDocsFileParserTest, wordsFileParserTest) { + char* locale = setlocale(LC_CTYPE, ""); + std::cout << "Set locale LC_CTYPE to: " << locale << std::endl; + + std::fstream f("_testtmp.contexts.tsv", std::ios_base::out); + f << createWordsFileLineAsString("Foo", false, 0, 2) + << createWordsFileLineAsString("foo", false, 0, 2) + << createWordsFileLineAsString("Bär", true, 0, 1) + << createWordsFileLineAsString("Äü", false, 0, 1) + << createWordsFileLineAsString("X", false, 1, 1); + f.close(); + + WordLineVec expected = {{"foo", false, 0, 2}, + {"foo", false, 0, 2}, + {"Bär", true, 0, 1}, + {"äü", false, 0, 1}, + {"x", false, 1, 1}}; + + testWordsFileParser("_testtmp.contexts.tsv", expected); + remove("_testtmp.contexts.tsv"); +}; + +TEST(WordsAndDocsFileParser, docsFileParserTest) { + char* locale = setlocale(LC_CTYPE, ""); + std::cout << "Set locale LC_CTYPE to: " << locale << std::endl; + + std::fstream f("_testtmp.documents.tsv", std::ios_base::out); + f << createDocsFileLineAsString(4, "This TeSt is OnlyCharcters") + << createDocsFileLineAsString(7, "Wh4t h4pp3ns t0 num83rs") + << createDocsFileLineAsString(8, "An( sp@ci*l ch.ar,:act=_er+s") + << createDocsFileLineAsString(190293, "Large docId"); + f.close(); + + DocLineVec expected = {{4, "This TeSt is OnlyCharcters"}, + {7, "Wh4t h4pp3ns t0 num83rs"}, + {8, "An( sp@ci*l ch.ar,:act=_er+s"}, + {190293, "Large docId"}}; + + testDocsFileParser("_testtmp.documents.tsv", expected); + remove("_testtmp.documents.tsv"); +} + +TEST(TokenizeAndNormalizeText, tokenizeAndNormalizeTextTest) { + char* locale = setlocale(LC_CTYPE, ""); + std::cout << "Set locale LC_CTYPE to: " << locale << std::endl; + + // Test 1 + testTokenizeAndNormalizeText("already normalized text", + {"already", "normalized", "text"}); + + // Test 2 + testTokenizeAndNormalizeText("TeXt WITH UpperCASe", + {"text", "with", "uppercase"}); + + // Test 3 + testTokenizeAndNormalizeText("41ph4num3r1c t3xt", {"41ph4num3r1c", "t3xt"}); + + // Test 4 + testTokenizeAndNormalizeText( + "test\twith\ndifferent,separators.here ,.\t", + {"test", "with", "different", "separators", "here"}); +} diff --git a/test/engine/TextIndexScanForWordTest.cpp b/test/engine/TextIndexScanForWordTest.cpp index eac3cb0d2f..cc9b685ec8 100644 --- a/test/engine/TextIndexScanForWordTest.cpp +++ b/test/engine/TextIndexScanForWordTest.cpp @@ -5,6 +5,7 @@ #include #include +#include "../WordsAndDocsFileLineCreator.h" #include "../printers/VariablePrinters.h" #include "../util/GTestHelpers.h" #include "../util/IdTableHelpers.h" @@ -26,45 +27,45 @@ std::string kg = ". . . ."; std::string wordsFileContent = - h::createWordsFileLine("astronomer", false, 1, 1) + - h::createWordsFileLine("", true, 1, 0) + - h::createWordsFileLine("scientist", false, 1, 1) + - h::createWordsFileLine("field", false, 1, 1) + - h::createWordsFileLine("astronomy", false, 1, 1) + - h::createWordsFileLine("astronomer", false, 2, 0) + - h::createWordsFileLine("", true, 2, 0) + - h::createWordsFileLine(":s:firstsentence", false, 2, 0) + - h::createWordsFileLine("scientist", false, 2, 0) + - h::createWordsFileLine("field", false, 2, 0) + - h::createWordsFileLine("astronomy", false, 2, 0) + - h::createWordsFileLine("astronomy", false, 3, 1) + - h::createWordsFileLine("concentrates", false, 3, 1) + - h::createWordsFileLine("studies", false, 3, 1) + - h::createWordsFileLine("specific", false, 3, 1) + - h::createWordsFileLine("question", false, 3, 1) + - h::createWordsFileLine("outside", false, 3, 1) + - h::createWordsFileLine("scope", false, 3, 1) + - h::createWordsFileLine("earth", false, 3, 1) + - h::createWordsFileLine("astronomy", false, 4, 1) + - h::createWordsFileLine("concentrates", false, 4, 1) + - h::createWordsFileLine("studies", false, 4, 1) + - h::createWordsFileLine("field", false, 4, 1) + - h::createWordsFileLine("outside", false, 4, 1) + - h::createWordsFileLine("scope", false, 4, 1) + - h::createWordsFileLine("earth", false, 4, 1) + - h::createWordsFileLine("tester", false, 5, 1) + - h::createWordsFileLine("rockets", false, 5, 1) + - h::createWordsFileLine("astronomer", false, 5, 1) + - h::createWordsFileLine("", true, 5, 0) + - h::createWordsFileLine("although", false, 5, 1) + - h::createWordsFileLine("astronomer", false, 6, 0) + - h::createWordsFileLine("", true, 6, 0) + - h::createWordsFileLine("although", false, 6, 0) + - h::createWordsFileLine("", true, 6, 0) + - h::createWordsFileLine("space", false, 6, 1) + - h::createWordsFileLine("", true, 7, 0) + - h::createWordsFileLine("space", false, 7, 0) + - h::createWordsFileLine("earth", false, 7, 1); + createWordsFileLineAsString("astronomer", false, 1, 1) + + createWordsFileLineAsString("", true, 1, 0) + + createWordsFileLineAsString("scientist", false, 1, 1) + + createWordsFileLineAsString("field", false, 1, 1) + + createWordsFileLineAsString("astronomy", false, 1, 1) + + createWordsFileLineAsString("astronomer", false, 2, 0) + + createWordsFileLineAsString("", true, 2, 0) + + createWordsFileLineAsString(":s:firstsentence", false, 2, 0) + + createWordsFileLineAsString("scientist", false, 2, 0) + + createWordsFileLineAsString("field", false, 2, 0) + + createWordsFileLineAsString("astronomy", false, 2, 0) + + createWordsFileLineAsString("astronomy", false, 3, 1) + + createWordsFileLineAsString("concentrates", false, 3, 1) + + createWordsFileLineAsString("studies", false, 3, 1) + + createWordsFileLineAsString("specific", false, 3, 1) + + createWordsFileLineAsString("question", false, 3, 1) + + createWordsFileLineAsString("outside", false, 3, 1) + + createWordsFileLineAsString("scope", false, 3, 1) + + createWordsFileLineAsString("earth", false, 3, 1) + + createWordsFileLineAsString("astronomy", false, 4, 1) + + createWordsFileLineAsString("concentrates", false, 4, 1) + + createWordsFileLineAsString("studies", false, 4, 1) + + createWordsFileLineAsString("field", false, 4, 1) + + createWordsFileLineAsString("outside", false, 4, 1) + + createWordsFileLineAsString("scope", false, 4, 1) + + createWordsFileLineAsString("earth", false, 4, 1) + + createWordsFileLineAsString("tester", false, 5, 1) + + createWordsFileLineAsString("rockets", false, 5, 1) + + createWordsFileLineAsString("astronomer", false, 5, 1) + + createWordsFileLineAsString("", true, 5, 0) + + createWordsFileLineAsString("although", false, 5, 1) + + createWordsFileLineAsString("astronomer", false, 6, 0) + + createWordsFileLineAsString("", true, 6, 0) + + createWordsFileLineAsString("although", false, 6, 0) + + createWordsFileLineAsString("", true, 6, 0) + + createWordsFileLineAsString("space", false, 6, 1) + + createWordsFileLineAsString("", true, 7, 0) + + createWordsFileLineAsString("space", false, 7, 0) + + createWordsFileLineAsString("earth", false, 7, 1); std::string firstDocText = "An astronomer is a scientist in the field of " @@ -77,8 +78,8 @@ std::string secondDocText = "too although they might not be in space but on " "earth."; -std::string docsFileContent = h::createDocsFileLine(4, firstDocText) + - h::createDocsFileLine(7, secondDocText); +std::string docsFileContent = createDocsFileLineAsString(4, firstDocText) + + createDocsFileLineAsString(7, secondDocText); std::pair contentsOfWordsFileAndDocsFile = { wordsFileContent, docsFileContent}; diff --git a/test/engine/TextIndexScanTestHelpers.h b/test/engine/TextIndexScanTestHelpers.h index 83a72ddea4..6ba1b8c6de 100644 --- a/test/engine/TextIndexScanTestHelpers.h +++ b/test/engine/TextIndexScanTestHelpers.h @@ -66,18 +66,4 @@ inline string combineToString(const string& text, const string& word) { ss << "Text: " << text << ", Word: " << word << std::endl; return ss.str(); } - -inline std::string inlineSeparator = "\t"; -inline std::string lineSeparator = "\n"; - -inline string createWordsFileLine(std::string word, bool isEntity, - size_t contextId, size_t score) { - return word + inlineSeparator + (isEntity ? "1" : "0") + inlineSeparator + - std::to_string(contextId) + inlineSeparator + std::to_string(score) + - lineSeparator; -}; - -inline string createDocsFileLine(size_t docId, std::string docContent) { - return std::to_string(docId) + inlineSeparator + docContent + lineSeparator; -}; } // namespace textIndexScanTestHelpers From d7ec9be2e0405573456466b1489f77be43514ef3 Mon Sep 17 00:00:00 2001 From: Julian <14220769+Qup42@users.noreply.github.com> Date: Wed, 22 Jan 2025 10:24:58 +0100 Subject: [PATCH 02/28] Graph Store HTTP Protocol (GET, POST) back end (#1668) Implement a function `transformGraphStoreProtocol` that does the back end of transforming a SPARQL Graph Store HTTP Protocol request to it's equivalent SPARQL Query or Update. The integration will be a separate step. --- src/engine/CMakeLists.txt | 2 +- src/engine/GraphStoreProtocol.cpp | 101 ++++++++++++ src/engine/GraphStoreProtocol.h | 140 +++++++++++++++++ src/engine/Server.cpp | 42 ++--- src/engine/Server.h | 12 -- src/parser/TripleComponent.h | 4 + src/util/http/MediaTypes.cpp | 3 +- src/util/http/MediaTypes.h | 1 + src/util/http/UrlParser.cpp | 22 +++ src/util/http/UrlParser.h | 7 + test/CMakeLists.txt | 2 + test/GraphStoreProtocolTest.cpp | 247 ++++++++++++++++++++++++++++++ test/ServerTest.cpp | 95 ++++-------- test/TripleComponentTest.cpp | 18 +++ test/UrlParserTest.cpp | 23 +++ test/util/HttpRequestHelpers.h | 46 ++++++ 16 files changed, 651 insertions(+), 114 deletions(-) create mode 100644 src/engine/GraphStoreProtocol.cpp create mode 100644 src/engine/GraphStoreProtocol.h create mode 100644 test/GraphStoreProtocolTest.cpp create mode 100644 test/util/HttpRequestHelpers.h diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index be22a64d5d..e81c834303 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -14,5 +14,5 @@ add_library(engine CartesianProductJoin.cpp TextIndexScanForWord.cpp TextIndexScanForEntity.cpp TextLimit.cpp LazyGroupBy.cpp GroupByHashMapOptimization.cpp SpatialJoin.cpp CountConnectedSubgraphs.cpp SpatialJoinAlgorithms.cpp PathSearch.cpp ExecuteUpdate.cpp - Describe.cpp) + Describe.cpp GraphStoreProtocol.cpp) qlever_target_link_libraries(engine util index parser sparqlExpressions http SortPerformanceEstimator Boost::iostreams s2) diff --git a/src/engine/GraphStoreProtocol.cpp b/src/engine/GraphStoreProtocol.cpp new file mode 100644 index 0000000000..fc46ec6fa0 --- /dev/null +++ b/src/engine/GraphStoreProtocol.cpp @@ -0,0 +1,101 @@ +// Copyright 2024, University of Freiburg +// Chair of Algorithms and Data Structures +// Authors: Julian Mundhahs + +#include "engine/GraphStoreProtocol.h" + +#include "util/http/beast.h" + +// ____________________________________________________________________________ +GraphOrDefault GraphStoreProtocol::extractTargetGraph( + const ad_utility::url_parser::ParamValueMap& params) { + const std::optional graphIri = + ad_utility::url_parser::checkParameter(params, "graph", std::nullopt); + const bool isDefault = + ad_utility::url_parser::checkParameter(params, "default", "").has_value(); + if (graphIri.has_value() == isDefault) { + throw std::runtime_error( + "Exactly one of the query parameters default or graph must be set to " + "identify the graph for the graph store protocol request."); + } + if (graphIri.has_value()) { + return GraphRef::fromIrirefWithoutBrackets(graphIri.value()); + } else { + AD_CORRECTNESS_CHECK(isDefault); + return DEFAULT{}; + } +} + +// ____________________________________________________________________________ +void GraphStoreProtocol::throwUnsupportedMediatype( + const string_view& mediatype) { + throw UnsupportedMediatypeError(absl::StrCat( + "Mediatype \"", mediatype, + "\" is not supported for SPARQL Graph Store HTTP Protocol in QLever. " + "Supported: ", + toString(ad_utility::MediaType::turtle), ", ", + toString(ad_utility::MediaType::ntriples), ".")); +} + +// ____________________________________________________________________________ +void GraphStoreProtocol::throwUnsupportedHTTPMethod( + const std::string_view& method) { + throw std::runtime_error(absl::StrCat( + method, + " in the SPARQL Graph Store HTTP Protocol is not yet implemented " + "in QLever.")); +} + +// ____________________________________________________________________________ +std::vector GraphStoreProtocol::parseTriples( + const string& body, const ad_utility::MediaType contentType) { + using Re2Parser = RdfStringParser>; + switch (contentType) { + case ad_utility::MediaType::turtle: + case ad_utility::MediaType::ntriples: { + auto parser = Re2Parser(); + parser.setInputStream(body); + return parser.parseAndReturnAllTriples(); + } + default: { + throwUnsupportedMediatype(toString(contentType)); + } + } +} + +// ____________________________________________________________________________ +std::vector GraphStoreProtocol::convertTriples( + const GraphOrDefault& graph, std::vector triples) { + SparqlTripleSimpleWithGraph::Graph tripleGraph{std::monostate{}}; + if (std::holds_alternative(graph)) { + tripleGraph = Iri(std::get(graph).toStringRepresentation()); + } + auto transformTurtleTriple = [&tripleGraph](TurtleTriple&& triple) { + AD_CORRECTNESS_CHECK(triple.graphIri_.isId() && + triple.graphIri_.getId() == + qlever::specialIds().at(DEFAULT_GRAPH_IRI)); + + return SparqlTripleSimpleWithGraph(std::move(triple.subject_), + std::move(triple.predicate_), + std::move(triple.object_), tripleGraph); + }; + return ad_utility::transform(std::move(triples), transformTurtleTriple); +} + +// ____________________________________________________________________________ +ParsedQuery GraphStoreProtocol::transformGet(const GraphOrDefault& graph) { + ParsedQuery res; + res._clause = parsedQuery::ConstructClause( + {{Variable("?s"), Variable("?p"), Variable("?o")}}); + res._rootGraphPattern = {}; + parsedQuery::GraphPattern selectSPO; + selectSPO._graphPatterns.emplace_back(parsedQuery::BasicGraphPattern{ + {SparqlTriple(Variable("?s"), "?p", Variable("?o"))}}); + if (const auto* iri = + std::get_if(&graph)) { + res.datasetClauses_ = + parsedQuery::DatasetClauses::fromClauses({DatasetClause{*iri, false}}); + } + res._rootGraphPattern = std::move(selectSPO); + return res; +} diff --git a/src/engine/GraphStoreProtocol.h b/src/engine/GraphStoreProtocol.h new file mode 100644 index 0000000000..dee41d8fe3 --- /dev/null +++ b/src/engine/GraphStoreProtocol.h @@ -0,0 +1,140 @@ +// Copyright 2024, University of Freiburg +// Chair of Algorithms and Data Structures +// Authors: Julian Mundhahs + +#pragma once + +#include + +#include "parser/ParsedQuery.h" +#include "parser/RdfParser.h" +#include "util/http/HttpUtils.h" +#include "util/http/UrlParser.h" + +// The mediatype of a request could not be determined. +class UnknownMediatypeError : public std::runtime_error { + public: + explicit UnknownMediatypeError(std::string_view msg) + : std::runtime_error{std::string{msg}} {} +}; + +// The mediatype of a request is not supported. +class UnsupportedMediatypeError : public std::runtime_error { + public: + explicit UnsupportedMediatypeError(std::string_view msg) + : std::runtime_error{std::string{msg}} {} +}; + +// Transform SPARQL Graph Store Protocol requests to their equivalent +// ParsedQuery (SPARQL Query or Update). +class GraphStoreProtocol { + private: + // Extract the mediatype from a request. + static ad_utility::MediaType extractMediatype( + const ad_utility::httpUtils::HttpRequest auto& rawRequest) { + using namespace boost::beast::http; + + std::string_view contentTypeString; + if (rawRequest.find(field::content_type) != rawRequest.end()) { + contentTypeString = rawRequest.at(field::content_type); + } + if (contentTypeString.empty()) { + // If the mediatype is not given, return an error. + // Note: The specs also allow to try to determine the media type from the + // content. + throw UnknownMediatypeError("Mediatype empty or not set."); + } + const auto mediatype = + ad_utility::getMediaTypeFromAcceptHeader(contentTypeString); + // A media type is set but not one of the supported ones as per the QLever + // MediaType code. + if (!mediatype.has_value()) { + throwUnsupportedMediatype(rawRequest.at(field::content_type)); + } + return mediatype.value(); + } + FRIEND_TEST(GraphStoreProtocolTest, extractMediatype); + + // Throws the error if a mediatype is not supported. + [[noreturn]] static void throwUnsupportedMediatype( + const std::string_view& mediatype); + + // Throws the error if an HTTP method is not supported. + [[noreturn]] static void throwUnsupportedHTTPMethod( + const std::string_view& method); + + // Parse the triples from the request body according to the content type. + static std::vector parseTriples( + const std::string& body, const ad_utility::MediaType contentType); + FRIEND_TEST(GraphStoreProtocolTest, parseTriples); + + // Transforms the triples from `TurtleTriple` to `SparqlTripleSimpleWithGraph` + // and sets the correct graph. + static std::vector convertTriples( + const GraphOrDefault& graph, std::vector triples); + FRIEND_TEST(GraphStoreProtocolTest, convertTriples); + + // Transform a SPARQL Graph Store Protocol POST to an equivalent ParsedQuery + // which is an SPARQL Update. + static ParsedQuery transformPost( + const ad_utility::httpUtils::HttpRequest auto& rawRequest, + const GraphOrDefault& graph) { + auto triples = + parseTriples(rawRequest.body(), extractMediatype(rawRequest)); + auto convertedTriples = convertTriples(graph, std::move(triples)); + updateClause::GraphUpdate up{std::move(convertedTriples), {}}; + ParsedQuery res; + res._clause = parsedQuery::UpdateClause{std::move(up)}; + return res; + } + FRIEND_TEST(GraphStoreProtocolTest, transformPost); + + // Transform a SPARQL Graph Store Protocol GET to an equivalent ParsedQuery + // which is an SPARQL Query. + static ParsedQuery transformGet(const GraphOrDefault& graph); + FRIEND_TEST(GraphStoreProtocolTest, transformGet); + + public: + // Every Graph Store Protocol request has equivalent SPARQL Query or Update. + // Transform the Graph Store Protocol request into it's equivalent Query or + // Update. + static ParsedQuery transformGraphStoreProtocol( + const ad_utility::httpUtils::HttpRequest auto& rawRequest) { + ad_utility::url_parser::ParsedUrl parsedUrl = + ad_utility::url_parser::parseRequestTarget(rawRequest.target()); + // We only support passing the target graph as a query parameter (`Indirect + // Graph Identification`). `Direct Graph Identification` (the URL is the + // graph) is not supported. See also + // https://www.w3.org/TR/2013/REC-sparql11-http-rdf-update-20130321/#graph-identification. + GraphOrDefault graph = extractTargetGraph(parsedUrl.parameters_); + + using enum boost::beast::http::verb; + auto method = rawRequest.method(); + if (method == get) { + return transformGet(graph); + } else if (method == put) { + throwUnsupportedHTTPMethod("PUT"); + } else if (method == delete_) { + throwUnsupportedHTTPMethod("DELETE"); + } else if (method == post) { + return transformPost(rawRequest, graph); + } else if (method == head) { + throwUnsupportedHTTPMethod("HEAD"); + } else if (method == patch) { + throwUnsupportedHTTPMethod("PATCH"); + } else { + throw std::runtime_error( + absl::StrCat("Unsupported HTTP method \"", + std::string_view{rawRequest.method_string()}, + "\" for the SPARQL Graph Store HTTP Protocol.")); + } + } + + private: + // Extract the graph to be acted upon using from the URL query parameters + // (`Indirect Graph Identification`). See + // https://www.w3.org/TR/2013/REC-sparql11-http-rdf-update-20130321/#indirect-graph-identification + static GraphOrDefault extractTargetGraph( + const ad_utility::url_parser::ParamValueMap& params); + FRIEND_TEST(GraphStoreProtocolTest, extractTargetGraph); +}; diff --git a/src/engine/Server.cpp b/src/engine/Server.cpp index 08fc6f9607..987c69fc7e 100644 --- a/src/engine/Server.cpp +++ b/src/engine/Server.cpp @@ -11,6 +11,7 @@ #include #include +#include "GraphStoreProtocol.h" #include "engine/ExecuteUpdate.h" #include "engine/ExportQueryExecutionTrees.h" #include "engine/QueryPlanner.h" @@ -347,8 +348,8 @@ Awaitable Server::process( // We always want to call `Server::checkParameter` with the same first // parameter. - auto checkParameter = - std::bind_front(&Server::checkParameter, std::cref(parameters)); + auto checkParameter = std::bind_front(&ad_utility::url_parser::checkParameter, + std::cref(parameters)); // Check the access token. If an access token is provided and the check fails, // throw an exception and do not process any part of the query (even if the @@ -537,9 +538,11 @@ Awaitable Server::process( std::pair Server::determineResultPinning( const ad_utility::url_parser::ParamValueMap& params) { const bool pinSubtrees = - checkParameter(params, "pinsubtrees", "true").has_value(); + ad_utility::url_parser::checkParameter(params, "pinsubtrees", "true") + .has_value(); const bool pinResult = - checkParameter(params, "pinresult", "true").has_value(); + ad_utility::url_parser::checkParameter(params, "pinresult", "true") + .has_value(); return {pinSubtrees, pinResult}; } @@ -729,17 +732,11 @@ Awaitable Server::sendStreamableResponse( } } -// ____________________________________________________________________________ -class NoSupportedMediatypeError : public std::runtime_error { - public: - explicit NoSupportedMediatypeError(std::string_view msg) - : std::runtime_error{std::string{msg}} {} -}; - // ____________________________________________________________________________ MediaType Server::determineMediaType( const ad_utility::url_parser::ParamValueMap& params, const ad_utility::httpUtils::HttpRequest auto& request) { + using namespace ad_utility::url_parser; // The following code block determines the media type to be used for the // result. The media type is either determined by the "Accept:" header of // the request or by the URL parameter "action=..." (for TSV and CSV export, @@ -997,7 +994,7 @@ Awaitable Server::processQueryOrUpdate( } catch (const QueryAlreadyInUseError& e) { responseStatus = http::status::conflict; exceptionErrorMsg = e.what(); - } catch (const NoSupportedMediatypeError& e) { + } catch (const UnknownMediatypeError& e) { responseStatus = http::status::bad_request; exceptionErrorMsg = e.what(); } catch (const ad_utility::CancellationException& e) { @@ -1118,24 +1115,3 @@ bool Server::checkAccessToken( return true; } } - -// _____________________________________________________________________________ -std::optional Server::checkParameter( - const ad_utility::url_parser::ParamValueMap& parameters, - std::string_view key, std::optional value) { - auto param = - ad_utility::url_parser::getParameterCheckAtMostOnce(parameters, key); - if (!param.has_value()) { - return std::nullopt; - } - std::string parameterValue = param.value(); - - // If value is given, but not equal to param value, return std::nullopt. If - // no value is given, set it to param value. - if (value == std::nullopt) { - value = parameterValue; - } else if (value != parameterValue) { - return std::nullopt; - } - return value; -} diff --git a/src/engine/Server.h b/src/engine/Server.h index 3ccc070cb7..399a5383d6 100644 --- a/src/engine/Server.h +++ b/src/engine/Server.h @@ -256,18 +256,6 @@ class Server { /// HTTP error response. bool checkAccessToken(std::optional accessToken) const; - /// Checks if a URL parameter exists in the request, and it matches the - /// expected `value`. If yes, return the value, otherwise return - /// `std::nullopt`. If `value` is `std::nullopt`, only check if the key - /// exists. We need this because we have parameters like "cmd=stats", where a - /// fixed combination of the key and value determines the kind of action, as - /// well as parameters like "index-decription=...", where the key determines - /// the kind of action. If the key is not found, always return `std::nullopt`. - static std::optional checkParameter( - const ad_utility::url_parser::ParamValueMap& parameters, - std::string_view key, std::optional value); - FRIEND_TEST(ServerTest, checkParameter); - /// Check if user-provided timeout is authorized with a valid access-token or /// lower than the server default. Return an empty optional and send a 403 /// Forbidden HTTP response if the change is not allowed. Return the new diff --git a/src/parser/TripleComponent.h b/src/parser/TripleComponent.h index fb874fc3c1..84abe54f7e 100644 --- a/src/parser/TripleComponent.h +++ b/src/parser/TripleComponent.h @@ -175,6 +175,10 @@ class TripleComponent { } [[nodiscard]] Variable& getVariable() { return std::get(_variant); } + bool isId() const { return std::holds_alternative(_variant); } + const Id& getId() const { return std::get(_variant); } + Id& getId() { return std::get(_variant); } + /// Convert to an RDF literal. `std::strings` will be emitted directly, /// `int64_t` is converted to a `xsd:integer` literal, and a `double` is /// converted to a `xsd:double`. diff --git a/src/util/http/MediaTypes.cpp b/src/util/http/MediaTypes.cpp index d91e7584df..b95d822562 100644 --- a/src/util/http/MediaTypes.cpp +++ b/src/util/http/MediaTypes.cpp @@ -18,7 +18,7 @@ using enum MediaType; // specified in the request. It's "application/sparql-results+json", as // required by the SPARQL standard. constexpr std::array SUPPORTED_MEDIA_TYPES{ - sparqlJson, sparqlXml, qleverJson, tsv, csv, turtle, octetStream}; + sparqlJson, sparqlXml, qleverJson, tsv, csv, turtle, ntriples, octetStream}; // _____________________________________________________________ const ad_utility::HashMap& getAllMediaTypes() { @@ -40,6 +40,7 @@ const ad_utility::HashMap& getAllMediaTypes() { add(sparqlXml, "application", "sparql-results+xml", {}); add(qleverJson, "application", "qlever-results+json", {}); add(turtle, "text", "turtle", {".ttl"}); + add(ntriples, "application", "n-triples", {".nt"}); add(octetStream, "application", "octet-stream", {}); return t; }(); diff --git a/src/util/http/MediaTypes.h b/src/util/http/MediaTypes.h index 0b2634b8ce..e03efd6842 100644 --- a/src/util/http/MediaTypes.h +++ b/src/util/http/MediaTypes.h @@ -28,6 +28,7 @@ enum class MediaType { tsv, csv, turtle, + ntriples, octetStream }; diff --git a/src/util/http/UrlParser.cpp b/src/util/http/UrlParser.cpp index 3149f97aec..1670fa937d 100644 --- a/src/util/http/UrlParser.cpp +++ b/src/util/http/UrlParser.cpp @@ -8,6 +8,7 @@ using namespace ad_utility::url_parser; +// _____________________________________________________________________________ std::optional ad_utility::url_parser::getParameterCheckAtMostOnce( const ParamValueMap& map, string_view key) { if (!map.contains(key)) { @@ -21,6 +22,27 @@ std::optional ad_utility::url_parser::getParameterCheckAtMostOnce( } return value.front(); } + +// _____________________________________________________________________________ +std::optional ad_utility::url_parser::checkParameter( + const ParamValueMap& parameters, std::string_view key, + std::optional value) { + const auto param = getParameterCheckAtMostOnce(parameters, key); + if (!param.has_value()) { + return std::nullopt; + } + std::string parameterValue = param.value(); + + // If no value is given, return the parameter's value. If value is given, but + // not equal to the parameter's value, return `std::nullopt`. + if (value == std::nullopt) { + value = parameterValue; + } else if (value != parameterValue) { + return std::nullopt; + } + return value; +} + // _____________________________________________________________________________ ParsedUrl ad_utility::url_parser::parseRequestTarget(std::string_view target) { auto urlResult = boost::urls::parse_origin_form(target); diff --git a/src/util/http/UrlParser.h b/src/util/http/UrlParser.h index 33ebc86b1d..47edd8404f 100644 --- a/src/util/http/UrlParser.h +++ b/src/util/http/UrlParser.h @@ -29,6 +29,13 @@ using ParamValueMap = ad_utility::HashMap>; std::optional getParameterCheckAtMostOnce(const ParamValueMap& map, string_view key); +// Checks if a parameter exists, and it matches the +// expected `value`. If yes, return the value, otherwise return +// `std::nullopt`. +std::optional checkParameter(const ParamValueMap& parameters, + std::string_view key, + std::optional value); + // A parsed URL. // - `path_` is the URL path // - `parameters_` is a map of the HTTP Query parameters diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b9581312e8..5520ce2ff9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -452,3 +452,5 @@ addLinkAndDiscoverTest(UrlParserTest) addLinkAndDiscoverTest(ServerTest engine) addLinkAndDiscoverTest(ExecuteUpdateTest engine) + +addLinkAndDiscoverTest(GraphStoreProtocolTest engine) diff --git a/test/GraphStoreProtocolTest.cpp b/test/GraphStoreProtocolTest.cpp new file mode 100644 index 0000000000..a7accb3ffd --- /dev/null +++ b/test/GraphStoreProtocolTest.cpp @@ -0,0 +1,247 @@ +// Copyright 2024, University of Freiburg +// Chair of Algorithms and Data Structures +// Authors: Julian Mundhahs + +#include +#include +#include + +#include "./util/GTestHelpers.h" +#include "./util/HttpRequestHelpers.h" +#include "./util/TripleComponentTestHelpers.h" +#include "SparqlAntlrParserTestHelpers.h" +#include "engine/GraphStoreProtocol.h" + +namespace m = matchers; +using namespace ad_utility::testing; + +using Var = Variable; +using TC = TripleComponent; + +// _____________________________________________________________________________________________ +TEST(GraphStoreProtocolTest, extractTargetGraph) { + // Equivalent to `/?default` + EXPECT_THAT(GraphStoreProtocol::extractTargetGraph({{"default", {""}}}), + DEFAULT{}); + // Equivalent to `/?graph=foo` + EXPECT_THAT(GraphStoreProtocol::extractTargetGraph({{"graph", {"foo"}}}), + iri("")); + // Equivalent to `/?graph=foo&graph=bar` + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::extractTargetGraph({{"graph", {"foo", "bar"}}}), + testing::HasSubstr( + "Parameter \"graph\" must be given exactly once. Is: 2")); + const std::string eitherDefaultOrGraphErrorMsg = + "Exactly one of the query parameters default or graph must be set to " + "identify the graph for the graph store protocol request."; + // Equivalent to `/` or `/?` + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::extractTargetGraph({}), + testing::HasSubstr(eitherDefaultOrGraphErrorMsg)); + // Equivalent to `/?unrelated=a&unrelated=b` + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::extractTargetGraph({{"unrelated", {"a", "b"}}}), + testing::HasSubstr(eitherDefaultOrGraphErrorMsg)); + // Equivalent to `/?default&graph=foo` + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::extractTargetGraph( + {{"default", {""}}, {"graph", {"foo"}}}), + testing::HasSubstr(eitherDefaultOrGraphErrorMsg)); +} + +// _____________________________________________________________________________________________ +TEST(GraphStoreProtocolTest, transformPost) { + auto expectTransformPost = + [](const ad_utility::httpUtils::HttpRequest auto& request, + const testing::Matcher& matcher, + ad_utility::source_location l = + ad_utility::source_location::current()) { + auto trace = generateLocationTrace(l); + const ad_utility::url_parser::ParsedUrl parsedUrl = + ad_utility::url_parser::parseRequestTarget(request.target()); + const GraphOrDefault graph = + GraphStoreProtocol::extractTargetGraph(parsedUrl.parameters_); + EXPECT_THAT(GraphStoreProtocol::transformPost(request, graph), matcher); + }; + + expectTransformPost( + makePostRequest("/?default", "text/turtle", " ."), + m::UpdateClause( + m::GraphUpdate( + {}, {{iri(""), iri(""), iri(""), std::monostate{}}}, + std::nullopt), + m::GraphPattern())); + expectTransformPost( + makePostRequest("/?default", "application/n-triples", " ."), + m::UpdateClause( + m::GraphUpdate( + {}, {{iri(""), iri(""), iri(""), std::monostate{}}}, + std::nullopt), + m::GraphPattern())); + expectTransformPost( + makePostRequest("/?graph=bar", "application/n-triples", " ."), + m::UpdateClause( + m::GraphUpdate({}, + {{iri(""), iri(""), iri(""), Iri("")}}, + std::nullopt), + m::GraphPattern())); + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::transformPost( + ad_utility::testing::makePostRequest( + "/?default", "application/sparql-results+xml", ""), + DEFAULT{}), + testing::HasSubstr( + "Mediatype \"application/sparql-results+xml\" is not supported for " + "SPARQL Graph Store HTTP Protocol in QLever.")); + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::transformPost( + ad_utility::testing::makePostRequest( + "/?default", "application/n-quads", " ."), + DEFAULT{}), + testing::HasSubstr("Not a single media type known to this parser was " + "detected in \"application/n-quads\".")); + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::transformPost( + ad_utility::testing::makePostRequest( + "/?default", "application/unknown", "fantasy"), + DEFAULT{}), + testing::HasSubstr("Not a single media type known to this parser was " + "detected in \"application/unknown\".")); +} + +// _____________________________________________________________________________________________ +TEST(GraphStoreProtocolTest, transformGet) { + auto expectTransformGet = + [](const ad_utility::httpUtils::HttpRequest auto& request, + const testing::Matcher& matcher, + ad_utility::source_location l = + ad_utility::source_location::current()) { + auto trace = generateLocationTrace(l); + const ad_utility::url_parser::ParsedUrl parsedUrl = + ad_utility::url_parser::parseRequestTarget(request.target()); + const GraphOrDefault graph = + GraphStoreProtocol::extractTargetGraph(parsedUrl.parameters_); + EXPECT_THAT(GraphStoreProtocol::transformGet(graph), matcher); + }; + expectTransformGet( + makeGetRequest("/?default"), + m::ConstructQuery({{Var{"?s"}, Var{"?p"}, Var{"?o"}}}, + m::GraphPattern(matchers::Triples({SparqlTriple( + TC(Var{"?s"}), "?p", TC(Var{"?o"}))})))); + expectTransformGet( + makeGetRequest("/?graph=foo"), + m::ConstructQuery({{Var{"?s"}, Var{"?p"}, Var{"?o"}}}, + m::GraphPattern(matchers::Triples({SparqlTriple( + TC(Var{"?s"}), "?p", TC(Var{"?o"}))})), + ScanSpecificationAsTripleComponent::Graphs{ + {TripleComponent(iri(""))}})); +} + +// _____________________________________________________________________________________________ +TEST(GraphStoreProtocolTest, transformGraphStoreProtocol) { + EXPECT_THAT(GraphStoreProtocol::transformGraphStoreProtocol( + ad_utility::testing::makeGetRequest("/?default")), + m::ConstructQuery({{Var{"?s"}, Var{"?p"}, Var{"?o"}}}, + m::GraphPattern(matchers::Triples({SparqlTriple( + TC(Var{"?s"}), "?p", TC(Var{"?o"}))})))); + EXPECT_THAT( + GraphStoreProtocol::transformGraphStoreProtocol( + ad_utility::testing::makePostRequest( + "/?default", "application/n-triples", " .")), + m::UpdateClause(m::GraphUpdate({}, + {{iri(""), iri(""), iri(""), + std::monostate{}}}, + std::nullopt), + m::GraphPattern())); + auto expectUnsupportedMethod = + [](const http::verb method, ad_utility::source_location l = + ad_utility::source_location::current()) { + auto trace = generateLocationTrace(l); + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::transformGraphStoreProtocol( + ad_utility::testing::makeRequest(method, "/?default")), + testing::HasSubstr( + absl::StrCat(std::string{boost::beast::http::to_string(method)}, + " in the SPARQL Graph Store HTTP Protocol"))); + }; + expectUnsupportedMethod(http::verb::put); + expectUnsupportedMethod(http::verb::delete_); + expectUnsupportedMethod(http::verb::head); + expectUnsupportedMethod(http::verb::patch); + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::transformGraphStoreProtocol( + ad_utility::testing::makeRequest(boost::beast::http::verb::connect, + "/?default")), + testing::HasSubstr("Unsupported HTTP method \"CONNECT\"")); +} + +// _____________________________________________________________________________________________ +TEST(GraphStoreProtocolTest, extractMediatype) { + using enum http::field; + auto makeRequest = + [](const ad_utility::HashMap& headers) { + return ad_utility::testing::makeRequest(http::verb::get, "/", headers); + }; + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::extractMediatype(makeRequest({})), + testing::HasSubstr("Mediatype empty or not set.")); + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::extractMediatype(makeRequest({{content_type, ""}})), + testing::HasSubstr("Mediatype empty or not set.")); + EXPECT_THAT(GraphStoreProtocol::extractMediatype( + makeRequest({{content_type, "text/csv"}})), + testing::Eq(ad_utility::MediaType::csv)); + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::extractMediatype( + makeRequest({{content_type, "text/plain"}})), + testing::HasSubstr("Mediatype \"text/plain\" is not supported for SPARQL " + "Graph Store HTTP Protocol in QLever.")); + EXPECT_THAT(GraphStoreProtocol::extractMediatype( + makeRequest({{content_type, "application/n-triples"}})), + testing::Eq(ad_utility::MediaType::ntriples)); +} + +// _____________________________________________________________________________________________ +TEST(GraphStoreProtocolTest, parseTriples) { + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::parseTriples(" ", + ad_utility::MediaType::json), + testing::HasSubstr( + "Mediatype \"application/json\" is not supported for SPARQL " + "Graph Store HTTP Protocol in QLever.")); + const auto expectedTriples = + std::vector{{{iri("")}, {iri("")}, {iri("")}}}; + EXPECT_THAT(GraphStoreProtocol::parseTriples(" .", + ad_utility::MediaType::ntriples), + testing::Eq(expectedTriples)); + EXPECT_THAT(GraphStoreProtocol::parseTriples(" .", + ad_utility::MediaType::turtle), + testing::Eq(expectedTriples)); + EXPECT_THAT( + GraphStoreProtocol::parseTriples("", ad_utility::MediaType::ntriples), + testing::Eq(std::vector{})); + AD_EXPECT_THROW_WITH_MESSAGE( + GraphStoreProtocol::parseTriples(" ", + ad_utility::MediaType::ntriples), + testing::HasSubstr(" Parse error at byte position 7")); +} + +// _____________________________________________________________________________________________ +TEST(GraphStoreProtocolTest, convertTriples) { + auto expectConvert = + [](const GraphOrDefault& graph, std::vector triples, + const std::vector& expectedTriples, + ad_utility::source_location l = + ad_utility::source_location::current()) { + auto trace = generateLocationTrace(l); + EXPECT_THAT( + GraphStoreProtocol::convertTriples(graph, std::move(triples)), + testing::Eq(expectedTriples)); + }; + expectConvert(DEFAULT{}, {}, {}); + expectConvert(iri(""), {}, {}); + expectConvert(DEFAULT{}, {{{iri("")}, {iri("")}, {iri("")}}}, + {SparqlTripleSimpleWithGraph{iri(""), iri(""), iri(""), + std::monostate{}}}); + expectConvert(iri(""), {}, {}); +} diff --git a/test/ServerTest.cpp b/test/ServerTest.cpp index 292d77f12b..72480c9434 100644 --- a/test/ServerTest.cpp +++ b/test/ServerTest.cpp @@ -9,11 +9,13 @@ #include #include "util/GTestHelpers.h" +#include "util/HttpRequestHelpers.h" #include "util/http/HttpUtils.h" #include "util/http/UrlParser.h" using namespace ad_utility::url_parser; using namespace ad_utility::url_parser::sparqlOperation; +using namespace ad_utility::testing; namespace { auto ParsedRequestIs = [](const std::string& path, @@ -27,27 +29,9 @@ auto ParsedRequestIs = [](const std::string& path, AD_FIELD(ad_utility::url_parser::ParsedRequest, operation_, testing::Eq(operation))); }; -auto MakeBasicRequest = [](http::verb method, const std::string& target) { - // version 11 stands for HTTP/1.1 - return http::request{method, target, 11}; -}; -auto MakeGetRequest = [](const std::string& target) { - return MakeBasicRequest(http::verb::get, target); -}; -auto MakePostRequest = [](const std::string& target, - const std::string& contentType, - const std::string& body) { - auto req = MakeBasicRequest(http::verb::post, target); - req.set(http::field::content_type, contentType); - req.body() = body; - req.prepare_payload(); - return req; -}; } // namespace TEST(ServerTest, parseHttpRequest) { - namespace http = boost::beast::http; - auto parse = [](const ad_utility::httpUtils::HttpRequest auto& request) { return Server::parseHttpRequest(request); }; @@ -55,49 +39,49 @@ TEST(ServerTest, parseHttpRequest) { "application/x-www-form-urlencoded;charset=UTF-8"; const std::string QUERY = "application/sparql-query"; const std::string UPDATE = "application/sparql-update"; - EXPECT_THAT(parse(MakeGetRequest("/")), ParsedRequestIs("/", {}, None{})); - EXPECT_THAT(parse(MakeGetRequest("/ping")), + EXPECT_THAT(parse(makeGetRequest("/")), ParsedRequestIs("/", {}, None{})); + EXPECT_THAT(parse(makeGetRequest("/ping")), ParsedRequestIs("/ping", {}, None{})); - EXPECT_THAT(parse(MakeGetRequest("/?cmd=stats")), + EXPECT_THAT(parse(makeGetRequest("/?cmd=stats")), ParsedRequestIs("/", {{"cmd", {"stats"}}}, None{})); - EXPECT_THAT(parse(MakeGetRequest( + EXPECT_THAT(parse(makeGetRequest( "/?query=SELECT+%2A%20WHERE%20%7B%7D&action=csv_export")), ParsedRequestIs("/", {{"action", {"csv_export"}}}, Query{"SELECT * WHERE {}"})); EXPECT_THAT( - parse(MakePostRequest("/", URLENCODED, + parse(makePostRequest("/", URLENCODED, "query=SELECT+%2A%20WHERE%20%7B%7D&send=100")), ParsedRequestIs("/", {{"send", {"100"}}}, Query{"SELECT * WHERE {}"})); AD_EXPECT_THROW_WITH_MESSAGE( - parse(MakePostRequest("/", URLENCODED, + parse(makePostRequest("/", URLENCODED, "ääär y=SELECT+%2A%20WHERE%20%7B%7D&send=100")), ::testing::HasSubstr("Invalid URL-encoded POST request")); AD_EXPECT_THROW_WITH_MESSAGE( - parse(MakeGetRequest("/?query=SELECT%20%2A%20WHERE%20%7B%7D&query=SELECT%" + parse(makeGetRequest("/?query=SELECT%20%2A%20WHERE%20%7B%7D&query=SELECT%" "20%3Ffoo%20WHERE%20%7B%7D")), ::testing::StrEq( "Parameter \"query\" must be given exactly once. Is: 2")); AD_EXPECT_THROW_WITH_MESSAGE( - parse(MakePostRequest("/", URLENCODED, + parse(makePostRequest("/", URLENCODED, "query=SELECT%20%2A%20WHERE%20%7B%7D&update=DELETE%" "20%7B%7D%20WHERE%20%7B%7D")), ::testing::HasSubstr( "Request must only contain one of \"query\" and \"update\".")); AD_EXPECT_THROW_WITH_MESSAGE( - parse(MakePostRequest("/", URLENCODED, + parse(makePostRequest("/", URLENCODED, "update=DELETE%20%7B%7D%20WHERE%20%7B%7D&update=" "DELETE%20%7B%7D%20WHERE%20%7B%7D")), ::testing::StrEq( "Parameter \"update\" must be given exactly once. Is: 2")); EXPECT_THAT( - parse(MakePostRequest("/", "application/x-www-form-urlencoded", + parse(makePostRequest("/", "application/x-www-form-urlencoded", "query=SELECT%20%2A%20WHERE%20%7B%7D&send=100")), ParsedRequestIs("/", {{"send", {"100"}}}, Query{"SELECT * WHERE {}"})); - EXPECT_THAT(parse(MakePostRequest("/", URLENCODED, + EXPECT_THAT(parse(makePostRequest("/", URLENCODED, "query=SELECT%20%2A%20WHERE%20%7B%7D")), ParsedRequestIs("/", {}, Query{"SELECT * WHERE {}"})); EXPECT_THAT( - parse(MakePostRequest( + parse(makePostRequest( "/", URLENCODED, "query=SELECT%20%2A%20WHERE%20%7B%7D&default-graph-uri=https%3A%2F%" "2Fw3.org%2Fdefault&named-graph-uri=https%3A%2F%2Fw3.org%2F1&named-" @@ -108,63 +92,40 @@ TEST(ServerTest, parseHttpRequest) { {"named-graph-uri", {"https://w3.org/1", "https://w3.org/2"}}}, Query{"SELECT * WHERE {}"})); AD_EXPECT_THROW_WITH_MESSAGE( - parse(MakePostRequest("/?send=100", URLENCODED, + parse(makePostRequest("/?send=100", URLENCODED, "query=SELECT%20%2A%20WHERE%20%7B%7D")), testing::StrEq("URL-encoded POST requests must not contain query " "parameters in the URL.")); - EXPECT_THAT(parse(MakePostRequest("/", URLENCODED, "cmd=clear-cache")), + EXPECT_THAT(parse(makePostRequest("/", URLENCODED, "cmd=clear-cache")), ParsedRequestIs("/", {{"cmd", {"clear-cache"}}}, None{})); - EXPECT_THAT(parse(MakePostRequest("/", QUERY, "SELECT * WHERE {}")), + EXPECT_THAT(parse(makePostRequest("/", QUERY, "SELECT * WHERE {}")), ParsedRequestIs("/", {}, Query{"SELECT * WHERE {}"})); EXPECT_THAT( - parse(MakePostRequest("/?send=100", QUERY, "SELECT * WHERE {}")), + parse(makePostRequest("/?send=100", QUERY, "SELECT * WHERE {}")), ParsedRequestIs("/", {{"send", {"100"}}}, Query{"SELECT * WHERE {}"})); AD_EXPECT_THROW_WITH_MESSAGE( - parse(MakeBasicRequest(http::verb::patch, "/")), + parse(makeRequest(http::verb::patch, "/")), testing::StrEq( "Request method \"PATCH\" not supported (has to be GET or POST)")); AD_EXPECT_THROW_WITH_MESSAGE( - parse(MakePostRequest("/", "invalid/content-type", "")), + parse(makePostRequest("/", "invalid/content-type", "")), testing::StrEq( "POST request with content type \"invalid/content-type\" not " "supported (must be \"application/x-www-form-urlencoded\", " "\"application/sparql-query\" or \"application/sparql-update\")")); AD_EXPECT_THROW_WITH_MESSAGE( - parse(MakeGetRequest("/?update=DELETE%20%2A%20WHERE%20%7B%7D")), + parse(makeGetRequest("/?update=DELETE%20%2A%20WHERE%20%7B%7D")), testing::StrEq("SPARQL Update is not allowed as GET request.")); - EXPECT_THAT(parse(MakePostRequest("/", UPDATE, "DELETE * WHERE {}")), + EXPECT_THAT(parse(makePostRequest("/", UPDATE, "DELETE * WHERE {}")), ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}"})); - EXPECT_THAT(parse(MakePostRequest("/", URLENCODED, + EXPECT_THAT(parse(makePostRequest("/", URLENCODED, "update=DELETE%20%2A%20WHERE%20%7B%7D")), ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}"})); - EXPECT_THAT(parse(MakePostRequest("/", URLENCODED, + EXPECT_THAT(parse(makePostRequest("/", URLENCODED, "update=DELETE+%2A+WHERE%20%7B%7D")), ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}"})); } -TEST(ServerTest, checkParameter) { - const ParamValueMap exampleParams = {{"foo", {"bar"}}, - {"baz", {"qux", "quux"}}}; - - EXPECT_THAT(Server::checkParameter(exampleParams, "doesNotExist", ""), - testing::Eq(std::nullopt)); - EXPECT_THAT(Server::checkParameter(exampleParams, "foo", "baz"), - testing::Eq(std::nullopt)); - EXPECT_THAT(Server::checkParameter(exampleParams, "foo", "bar"), - testing::Optional(testing::StrEq("bar"))); - AD_EXPECT_THROW_WITH_MESSAGE( - Server::checkParameter(exampleParams, "baz", "qux"), - testing::StrEq("Parameter \"baz\" must be given exactly once. Is: 2")); - EXPECT_THAT(Server::checkParameter(exampleParams, "foo", std::nullopt), - testing::Optional(testing::StrEq("bar"))); - AD_EXPECT_THROW_WITH_MESSAGE( - Server::checkParameter(exampleParams, "baz", std::nullopt), - testing::StrEq("Parameter \"baz\" must be given exactly once. Is: 2")); - AD_EXPECT_THROW_WITH_MESSAGE( - Server::checkParameter(exampleParams, "baz", std::nullopt), - testing::StrEq("Parameter \"baz\" must be given exactly once. Is: 2")); -} - TEST(ServerTest, determineResultPinning) { EXPECT_THAT(Server::determineResultPinning( {{"pinsubtrees", {"true"}}, {"pinresult", {"true"}}}), @@ -226,9 +187,9 @@ TEST(ServerTest, determineMediaType) { TEST(ServerTest, getQueryId) { using namespace ad_utility::websocket; Server server{9999, 1, ad_utility::MemorySize::megabytes(1), "accessToken"}; - auto reqWithExplicitQueryId = MakeGetRequest("/"); + auto reqWithExplicitQueryId = makeGetRequest("/"); reqWithExplicitQueryId.set("Query-Id", "100"); - const auto req = MakeGetRequest("/"); + const auto req = makeGetRequest("/"); { // A request with a custom query id. auto queryId1 = server.getQueryId(reqWithExplicitQueryId, @@ -250,10 +211,10 @@ TEST(ServerTest, getQueryId) { TEST(ServerTest, createMessageSender) { Server server{9999, 1, ad_utility::MemorySize::megabytes(1), "accessToken"}; - auto reqWithExplicitQueryId = MakeGetRequest("/"); + auto reqWithExplicitQueryId = makeGetRequest("/"); std::string customQueryId = "100"; reqWithExplicitQueryId.set("Query-Id", customQueryId); - const auto req = MakeGetRequest("/"); + const auto req = makeGetRequest("/"); // The query hub is only valid once, the server has been started. AD_EXPECT_THROW_WITH_MESSAGE( server.createMessageSender(server.queryHub_, req, diff --git a/test/TripleComponentTest.cpp b/test/TripleComponentTest.cpp index 2cc5c74cb3..2c67b16823 100644 --- a/test/TripleComponentTest.cpp +++ b/test/TripleComponentTest.cpp @@ -69,6 +69,24 @@ TEST(TripleComponent, setAndGetVariable) { ASSERT_EQ(tc.getVariable(), Variable{"?x"}); } +TEST(TripleComponent, setAndGetId) { + Id id = Id::makeFromVocabIndex(VocabIndex::make(1)); + TripleComponent tc{id}; + ASSERT_TRUE(tc.isId()); + ASSERT_FALSE(tc.isVariable()); + ASSERT_FALSE(tc.isString()); + ASSERT_FALSE(tc.isDouble()); + ASSERT_FALSE(tc.isInt()); + ASSERT_FALSE(tc.isBool()); + ASSERT_FALSE(tc.isIri()); + ASSERT_FALSE(tc.isLiteral()); + ASSERT_FALSE(tc.isUndef()); + ASSERT_EQ(tc, id); + ASSERT_EQ(tc.getId(), id); + const TripleComponent tcConst = std::move(tc); + ASSERT_EQ(tcConst.getId(), id); +} + TEST(TripleComponent, assignmentOperator) { TripleComponent object; object = -12.435; diff --git a/test/UrlParserTest.cpp b/test/UrlParserTest.cpp index eb6aeebc4d..d0b09ab030 100644 --- a/test/UrlParserTest.cpp +++ b/test/UrlParserTest.cpp @@ -128,3 +128,26 @@ TEST(UrlParserTest, parseDatasetClauses) { {iri(""), true}, {iri(""), true}})); } + +TEST(UrlParserTest, checkParameter) { + const url_parser::ParamValueMap exampleParams = {{"foo", {"bar"}}, + {"baz", {"qux", "quux"}}}; + + EXPECT_THAT(url_parser::checkParameter(exampleParams, "doesNotExist", ""), + ::testing::Eq(std::nullopt)); + EXPECT_THAT(url_parser::checkParameter(exampleParams, "foo", "baz"), + ::testing::Eq(std::nullopt)); + EXPECT_THAT(url_parser::checkParameter(exampleParams, "foo", "bar"), + ::testing::Optional(::testing::StrEq("bar"))); + AD_EXPECT_THROW_WITH_MESSAGE( + url_parser::checkParameter(exampleParams, "baz", "qux"), + ::testing::StrEq("Parameter \"baz\" must be given exactly once. Is: 2")); + EXPECT_THAT(url_parser::checkParameter(exampleParams, "foo", std::nullopt), + ::testing::Optional(::testing::StrEq("bar"))); + AD_EXPECT_THROW_WITH_MESSAGE( + url_parser::checkParameter(exampleParams, "baz", std::nullopt), + ::testing::StrEq("Parameter \"baz\" must be given exactly once. Is: 2")); + AD_EXPECT_THROW_WITH_MESSAGE( + url_parser::checkParameter(exampleParams, "baz", std::nullopt), + ::testing::StrEq("Parameter \"baz\" must be given exactly once. Is: 2")); +} diff --git a/test/util/HttpRequestHelpers.h b/test/util/HttpRequestHelpers.h new file mode 100644 index 0000000000..4505e828db --- /dev/null +++ b/test/util/HttpRequestHelpers.h @@ -0,0 +1,46 @@ +// Copyright 2024, University of Freiburg +// Chair of Algorithms and Data Structures +// Authors: Julian Mundhahs + +#pragma once + +#include "util/HashMap.h" +#include "util/http/beast.h" + +namespace ad_utility::testing { + +namespace http = boost::beast::http; + +// Construct a boost::beast request with the HTTP method, target path, headers +// and body. +inline auto makeRequest( + const http::verb method = http::verb::get, const std::string& target = "/", + const ad_utility::HashMap& headers = {}, + const std::optional& body = std::nullopt) { + // version 11 stands for HTTP/1.1 + auto req = http::request{method, target, 11}; + for (const auto& [key, value] : headers) { + req.set(key, value); + } + if (body.has_value()) { + req.body() = body.value(); + req.prepare_payload(); + } + return req; +} + +// Constructs a boost::beast GET request with the target path. +inline auto makeGetRequest(const std::string& target) { + return makeRequest(http::verb::get, target); +} + +// Constructs a boost::beast POST request with the target path, body content +// type and body content. +inline auto makePostRequest(const std::string& target, + const std::string& contentType, + const std::string& body) { + return makeRequest(http::verb::post, target, + {{http::field::content_type, contentType}}, body); +} + +} // namespace ad_utility::testing From 4b7a260a6ad77b9d8f7c15562e94f884f7beb29a Mon Sep 17 00:00:00 2001 From: Julian <14220769+Qup42@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:31:15 +0100 Subject: [PATCH 03/28] Return metadata on executed Update (#1675) SPARQL Update requests sent via HTTP are now answered with a JSON object that contains useful metadata about the request, for example the number of changed triples and timing statistics. --- benchmark/JoinAlgorithmBenchmark.cpp | 2 +- src/engine/ExecuteUpdate.cpp | 15 ++++- src/engine/ExecuteUpdate.h | 20 +++++-- src/engine/Result.h | 4 +- src/engine/Server.cpp | 87 +++++++++++++++++++++++----- src/engine/Server.h | 12 +++- src/engine/idTable/IdTable.h | 2 +- src/index/DeltaTriples.cpp | 58 +++++++++++++++---- src/index/DeltaTriples.h | 19 +++++- src/index/IndexImpl.cpp | 2 +- src/index/StringSortComparator.h | 2 +- src/util/AsioHelpers.h | 2 +- test/CMakeLists.txt | 2 + test/DeltaTriplesCountTest.cpp | 27 +++++++++ test/DeltaTriplesTest.cpp | 6 +- test/DeltaTriplesTestHelpers.h | 7 ++- test/ExecuteUpdateTest.cpp | 4 +- test/ServerTest.cpp | 49 ++++++++++++++++ 18 files changed, 273 insertions(+), 47 deletions(-) create mode 100644 test/DeltaTriplesCountTest.cpp diff --git a/benchmark/JoinAlgorithmBenchmark.cpp b/benchmark/JoinAlgorithmBenchmark.cpp index b06202dbf4..d0717744b8 100644 --- a/benchmark/JoinAlgorithmBenchmark.cpp +++ b/benchmark/JoinAlgorithmBenchmark.cpp @@ -1303,7 +1303,7 @@ class GeneralInterfaceImplementation : public BenchmarkInterface { ColumnNumWithType{toUnderlying(TimeForMergeGallopingJoin)}); // Calculate, how much of a speedup the hash join algorithm has in - // comparison to the merge/galloping join algrithm. + // comparison to the merge/galloping join algorithm. calculateSpeedupOfColumn( table, {toUnderlying(JoinAlgorithmSpeedup)}, {toUnderlying(TimeForHashJoin)}, diff --git a/src/engine/ExecuteUpdate.cpp b/src/engine/ExecuteUpdate.cpp index f6c7ea43a6..3637410d22 100644 --- a/src/engine/ExecuteUpdate.cpp +++ b/src/engine/ExecuteUpdate.cpp @@ -7,18 +7,24 @@ #include "engine/ExportQueryExecutionTrees.h" // _____________________________________________________________________________ -void ExecuteUpdate::executeUpdate( +UpdateMetadata ExecuteUpdate::executeUpdate( const Index& index, const ParsedQuery& query, const QueryExecutionTree& qet, DeltaTriples& deltaTriples, const CancellationHandle& cancellationHandle) { + UpdateMetadata metadata{}; auto [toInsert, toDelete] = - computeGraphUpdateQuads(index, query, qet, cancellationHandle); + computeGraphUpdateQuads(index, query, qet, cancellationHandle, metadata); // "The deletion of the triples happens before the insertion." (SPARQL 1.1 // Update 3.1.3) + ad_utility::Timer timer{ad_utility::Timer::InitialStatus::Started}; deltaTriples.deleteTriples(cancellationHandle, std::move(toDelete.idTriples_)); + metadata.deletionTime_ = timer.msecs(); + timer.reset(); deltaTriples.insertTriples(cancellationHandle, std::move(toInsert.idTriples_)); + metadata.insertionTime_ = timer.msecs(); + return metadata; } // _____________________________________________________________________________ @@ -120,7 +126,7 @@ std::pair ExecuteUpdate::computeGraphUpdateQuads( const Index& index, const ParsedQuery& query, const QueryExecutionTree& qet, - const CancellationHandle& cancellationHandle) { + const CancellationHandle& cancellationHandle, UpdateMetadata& metadata) { AD_CONTRACT_CHECK(query.hasUpdateClause()); auto updateClause = query.updateClause(); if (!std::holds_alternative(updateClause.op_)) { @@ -132,6 +138,8 @@ ExecuteUpdate::computeGraphUpdateQuads( // update. auto result = qet.getResult(false); + // Start the timer once the where clause has been evaluated. + ad_utility::Timer timer{ad_utility::Timer::InitialStatus::Started}; const auto& vocab = index.getVocab(); auto prepareTemplateAndResultContainer = @@ -168,6 +176,7 @@ ExecuteUpdate::computeGraphUpdateQuads( cancellationHandle->throwIfCancelled(); } } + metadata.triplePreparationTime_ = timer.msecs(); return { IdTriplesAndLocalVocab{std::move(toInsert), std::move(localVocabInsert)}, diff --git a/src/engine/ExecuteUpdate.h b/src/engine/ExecuteUpdate.h index 3cf686ed14..0f7e462c44 100644 --- a/src/engine/ExecuteUpdate.h +++ b/src/engine/ExecuteUpdate.h @@ -10,6 +10,15 @@ #include "parser/ParsedQuery.h" #include "util/CancellationHandle.h" +// Metadata about the time spent on different parts of an update. +struct UpdateMetadata { + using Milliseconds = std::chrono::milliseconds; + static constexpr Milliseconds Zero = Milliseconds::zero(); + Milliseconds triplePreparationTime_ = Zero; + Milliseconds insertionTime_ = Zero; + Milliseconds deletionTime_ = Zero; +}; + class ExecuteUpdate { public: using CancellationHandle = ad_utility::SharedCancellationHandle; @@ -18,10 +27,10 @@ class ExecuteUpdate { // Execute an update. This function is comparable to // `ExportQueryExecutionTrees::computeResult` for queries. - static void executeUpdate(const Index& index, const ParsedQuery& query, - const QueryExecutionTree& qet, - DeltaTriples& deltaTriples, - const CancellationHandle& cancellationHandle); + static UpdateMetadata executeUpdate( + const Index& index, const ParsedQuery& query, + const QueryExecutionTree& qet, DeltaTriples& deltaTriples, + const CancellationHandle& cancellationHandle); private: // Resolve all `TripleComponent`s and `Graph`s in a vector of @@ -59,6 +68,7 @@ class ExecuteUpdate { static std::pair computeGraphUpdateQuads(const Index& index, const ParsedQuery& query, const QueryExecutionTree& qet, - const CancellationHandle& cancellationHandle); + const CancellationHandle& cancellationHandle, + UpdateMetadata& metadata); FRIEND_TEST(ExecuteUpdate, computeGraphUpdateQuads); }; diff --git a/src/engine/Result.h b/src/engine/Result.h index 10b7364a3e..bc1eaa68b0 100644 --- a/src/engine/Result.h +++ b/src/engine/Result.h @@ -173,8 +173,8 @@ class Result { // the name of the function called with the local vocab as argument): // // ExportQueryExecutionTrees::idTableToQLeverJSONArray (idToStringAndType) - // ExportQueryExecutionTrees::selectQueryResultToSparqlJSON (dito) - // ExportQueryExecutionTrees::selectQueryResultToStream (dito) + // ExportQueryExecutionTrees::selectQueryResultToSparqlJSON (ditto) + // ExportQueryExecutionTrees::selectQueryResultToStream (ditto) // Filter::computeFilterImpl (evaluationContext) // Variable::evaluate (idToStringAndType) // diff --git a/src/engine/Server.cpp b/src/engine/Server.cpp index 987c69fc7e..1128cd5b22 100644 --- a/src/engine/Server.cpp +++ b/src/engine/Server.cpp @@ -16,6 +16,7 @@ #include "engine/ExportQueryExecutionTrees.h" #include "engine/QueryPlanner.h" #include "global/RuntimeParameters.h" +#include "index/IndexImpl.h" #include "util/AsioHelpers.h" #include "util/MemorySize/MemorySize.h" #include "util/OnDestructionDontThrowDuringStackUnwinding.h" @@ -880,8 +881,63 @@ Awaitable Server::processQuery( co_return; } +json Server::createResponseMetadataForUpdate( + const ad_utility::Timer& requestTimer, const Index& index, + const DeltaTriples& deltaTriples, const PlannedQuery& plannedQuery, + const QueryExecutionTree& qet, const DeltaTriplesCount& countBefore, + const UpdateMetadata& updateMetadata, const DeltaTriplesCount& countAfter) { + auto formatTime = [](std::chrono::milliseconds time) { + return absl::StrCat(time.count(), "ms"); + }; + + json response; + response["update"] = plannedQuery.parsedQuery_._originalString; + response["status"] = "OK"; + auto warnings = qet.collectWarnings(); + warnings.emplace(warnings.begin(), + "SPARQL 1.1 Update for QLever is experimental."); + response["warnings"] = warnings; + RuntimeInformationWholeQuery& runtimeInfoWholeOp = + qet.getRootOperation()->getRuntimeInfoWholeQuery(); + RuntimeInformation& runtimeInfo = qet.getRootOperation()->runtimeInfo(); + response["runtimeInformation"]["meta"] = + nlohmann::ordered_json(runtimeInfoWholeOp); + response["runtimeInformation"]["query_execution_tree"] = + nlohmann::ordered_json(runtimeInfo); + response["delta-triples"]["before"] = nlohmann::json(countBefore); + response["delta-triples"]["after"] = nlohmann::json(countAfter); + response["delta-triples"]["difference"] = + nlohmann::json(countAfter - countBefore); + response["time"]["planning"] = + formatTime(runtimeInfoWholeOp.timeQueryPlanning); + response["time"]["where"] = + formatTime(std::chrono::duration_cast( + runtimeInfo.totalTime_)); + json updateTime{ + {"total", formatTime(updateMetadata.triplePreparationTime_ + + updateMetadata.deletionTime_ + + updateMetadata.insertionTime_)}, + {"preparation", formatTime(updateMetadata.triplePreparationTime_)}, + {"delete", formatTime(updateMetadata.deletionTime_)}, + {"insert", formatTime(updateMetadata.insertionTime_)}}; + response["time"]["update"] = updateTime; + response["time"]["total"] = formatTime(requestTimer.msecs()); + for (auto permutation : Permutation::ALL) { + response["located-triples"][Permutation::toString( + permutation)]["blocks-affected"] = + deltaTriples.getLocatedTriplesForPermutation(permutation).numBlocks(); + auto numBlocks = index.getPimpl() + .getPermutation(permutation) + .metaData() + .blockData() + .size(); + response["located-triples"][Permutation::toString(permutation)] + ["blocks-total"] = numBlocks; + } + return response; +} // ____________________________________________________________________________ -void Server::processUpdateImpl( +json Server::processUpdateImpl( const ad_utility::url_parser::ParamValueMap& params, const string& update, ad_utility::Timer& requestTimer, TimeLimit timeLimit, auto& messageSender, ad_utility::SharedCancellationHandle cancellationHandle, @@ -904,8 +960,11 @@ void Server::processUpdateImpl( "following query was sent instead of an update: ", plannedQuery.parsedQuery_._originalString)); } - ExecuteUpdate::executeUpdate(index_, plannedQuery.parsedQuery_, qet, - deltaTriples, cancellationHandle); + + DeltaTriplesCount countBefore = deltaTriples.getCounts(); + UpdateMetadata updateMetadata = ExecuteUpdate::executeUpdate( + index_, plannedQuery.parsedQuery_, qet, deltaTriples, cancellationHandle); + DeltaTriplesCount countAfter = deltaTriples.getCounts(); LOG(INFO) << "Done processing update" << ", total time was " << requestTimer.msecs().count() << " ms" @@ -917,6 +976,10 @@ void Server::processUpdateImpl( // update anyway (The index of the located triples snapshot is part of the // cache key). cache_.clearAll(); + + return createResponseMetadataForUpdate(requestTimer, index_, deltaTriples, + plannedQuery, qet, countBefore, + updateMetadata, countAfter); } // ____________________________________________________________________________ @@ -930,32 +993,30 @@ Awaitable Server::processUpdate( auto [cancellationHandle, cancelTimeoutOnDestruction] = setupCancellationHandle(messageSender.getQueryId(), timeLimit); - // Update the delta triples. - // Note: We don't directly `co_await` because of lifetime issues (probably // bugs in GCC or Boost) that occur in the Conan build. auto coroutine = computeInNewThread( updateThreadPool_, [this, ¶ms, &update, &requestTimer, &timeLimit, &messageSender, &cancellationHandle] { - index_.deltaTriplesManager().modify( + // Update the delta triples. + return index_.deltaTriplesManager().modify( [this, ¶ms, &update, &requestTimer, &timeLimit, &messageSender, &cancellationHandle](auto& deltaTriples) { // Use `this` explicitly to silence false-positive errors on // captured `this` being unused. - this->processUpdateImpl(params, update, requestTimer, timeLimit, - messageSender, cancellationHandle, - deltaTriples); + return this->processUpdateImpl(params, update, requestTimer, + timeLimit, messageSender, + cancellationHandle, deltaTriples); }); }, cancellationHandle); - co_await std::move(coroutine); + auto response = co_await std::move(coroutine); - // TODO send a proper response // SPARQL 1.1 Protocol 2.2.4 Successful Responses: "The response body of a // successful update request is implementation defined." - co_await send(ad_utility::httpUtils::createOkResponse( - "Update successful", request, MediaType::textPlain)); + co_await send( + ad_utility::httpUtils::createJsonResponse(std::move(response), request)); co_return; } diff --git a/src/engine/Server.h b/src/engine/Server.h index 399a5383d6..566a313b5a 100644 --- a/src/engine/Server.h +++ b/src/engine/Server.h @@ -10,6 +10,7 @@ #include #include +#include "ExecuteUpdate.h" #include "engine/Engine.h" #include "engine/QueryExecutionContext.h" #include "engine/QueryExecutionTree.h" @@ -150,6 +151,15 @@ class Server { ad_utility::Timer& requestTimer, const ad_utility::httpUtils::HttpRequest auto& request, auto&& send, TimeLimit timeLimit); + // For an executed update create a json with some stats on the update (timing, + // number of changed triples, etc.). + static json createResponseMetadataForUpdate( + const ad_utility::Timer& requestTimer, const Index& index, + const DeltaTriples& deltaTriples, const PlannedQuery& plannedQuery, + const QueryExecutionTree& qet, const DeltaTriplesCount& countBefore, + const UpdateMetadata& updateMetadata, + const DeltaTriplesCount& countAfter); + FRIEND_TEST(ServerTest, createResponseMetadata); // Do the actual execution of an update. Awaitable processUpdate( const ad_utility::url_parser::ParamValueMap& params, const string& update, @@ -181,7 +191,7 @@ class Server { const string& operation); // Execute an update operation. The function must have exclusive access to the // DeltaTriples object. - void processUpdateImpl( + json processUpdateImpl( const ad_utility::url_parser::ParamValueMap& params, const string& update, ad_utility::Timer& requestTimer, TimeLimit timeLimit, auto& messageSender, ad_utility::SharedCancellationHandle cancellationHandle, diff --git a/src/engine/idTable/IdTable.h b/src/engine/idTable/IdTable.h index 9e57073602..438b4fc20d 100644 --- a/src/engine/idTable/IdTable.h +++ b/src/engine/idTable/IdTable.h @@ -364,7 +364,7 @@ class IdTable { } // The usual `front` and `back` functions to make the interface similar to - // `std::vector` aand other containers. + // `std::vector` and other containers. // TODO Remove the duplicates via explicit object parameters // ("deducing this"). row_reference_restricted front() requires(!isView) { return (*this)[0]; } diff --git a/src/index/DeltaTriples.cpp b/src/index/DeltaTriples.cpp index 8b69c8d363..c9b01ac21a 100644 --- a/src/index/DeltaTriples.cpp +++ b/src/index/DeltaTriples.cpp @@ -64,6 +64,11 @@ void DeltaTriples::eraseTripleInAllPermutations(LocatedTripleHandles& handles) { } } +// ____________________________________________________________________________ +DeltaTriplesCount DeltaTriples::getCounts() const { + return {numInserted(), numDeleted()}; +} + // ____________________________________________________________________________ void DeltaTriples::insertTriples(CancellationHandle cancellationHandle, Triples triples) { @@ -187,6 +192,20 @@ SharedLocatedTriplesSnapshot DeltaTriples::getSnapshot() { locatedTriples(), localVocab_.clone(), snapshotIndex)}; } +// ____________________________________________________________________________ +void to_json(nlohmann::json& j, const DeltaTriplesCount& count) { + j = nlohmann::json{{"inserted", count.triplesInserted_}, + {"deleted", count.triplesDeleted_}, + {"total", count.triplesInserted_ + count.triplesDeleted_}}; +} + +// ____________________________________________________________________________ +DeltaTriplesCount operator-(const DeltaTriplesCount& lhs, + const DeltaTriplesCount& rhs) { + return {lhs.triplesInserted_ - rhs.triplesInserted_, + lhs.triplesDeleted_ - rhs.triplesDeleted_}; +} + // ____________________________________________________________________________ DeltaTriples::DeltaTriples(const Index& index) : DeltaTriples(index.getImpl()) {} @@ -197,24 +216,41 @@ DeltaTriplesManager::DeltaTriplesManager(const IndexImpl& index) currentLocatedTriplesSnapshot_{deltaTriples_.wlock()->getSnapshot()} {} // _____________________________________________________________________________ -void DeltaTriplesManager::modify( - const std::function& function) { +template +ReturnType DeltaTriplesManager::modify( + const std::function& function) { // While holding the lock for the underlying `DeltaTriples`, perform the // actual `function` (typically some combination of insert and delete // operations) and (while still holding the lock) update the // `currentLocatedTriplesSnapshot_`. - deltaTriples_.withWriteLock([this, &function](DeltaTriples& deltaTriples) { - function(deltaTriples); - auto newSnapshot = deltaTriples.getSnapshot(); - currentLocatedTriplesSnapshot_.withWriteLock( - [&newSnapshot](auto& currentSnapshot) { - currentSnapshot = std::move(newSnapshot); - }); - }); + return deltaTriples_.withWriteLock( + [this, &function](DeltaTriples& deltaTriples) { + auto updateSnapshot = [this, &deltaTriples] { + auto newSnapshot = deltaTriples.getSnapshot(); + currentLocatedTriplesSnapshot_.withWriteLock( + [&newSnapshot](auto& currentSnapshot) { + currentSnapshot = std::move(newSnapshot); + }); + }; + + if constexpr (std::is_void_v) { + function(deltaTriples); + updateSnapshot(); + } else { + ReturnType returnValue = function(deltaTriples); + updateSnapshot(); + return returnValue; + } + }); } +// Explicit instantions +template void DeltaTriplesManager::modify( + std::function const&); +template nlohmann::json DeltaTriplesManager::modify( + const std::function&); // _____________________________________________________________________________ -void DeltaTriplesManager::clear() { modify(&DeltaTriples::clear); } +void DeltaTriplesManager::clear() { modify(&DeltaTriples::clear); } // _____________________________________________________________________________ SharedLocatedTriplesSnapshot DeltaTriplesManager::getCurrentSnapshot() const { diff --git a/src/index/DeltaTriples.h b/src/index/DeltaTriples.h index 89e57b05fe..372cf3ddd8 100644 --- a/src/index/DeltaTriples.h +++ b/src/index/DeltaTriples.h @@ -38,6 +38,21 @@ struct LocatedTriplesSnapshot { class SharedLocatedTriplesSnapshot : public std::shared_ptr {}; +// A class for keeping track of the number of triples of the `DeltaTriples`. +struct DeltaTriplesCount { + size_t triplesInserted_; + size_t triplesDeleted_; + + /// Output as json. The signature of this function is mandated by the json + /// library to allow for implicit conversion. + friend void to_json(nlohmann::json& j, const DeltaTriplesCount& count); + + friend DeltaTriplesCount operator-(const DeltaTriplesCount& lhs, + const DeltaTriplesCount& rhs); + + bool operator==(const DeltaTriplesCount& other) const = default; +}; + // A class for maintaining triples that are inserted or deleted after index // building, we call these delta triples. How it works in principle: // @@ -133,6 +148,7 @@ class DeltaTriples { // The number of delta triples added and subtracted. size_t numInserted() const { return triplesInserted_.size(); } size_t numDeleted() const { return triplesDeleted_.size(); } + DeltaTriplesCount getCounts() const; // Insert triples. void insertTriples(CancellationHandle cancellationHandle, Triples triples); @@ -208,7 +224,8 @@ class DeltaTriplesManager { // serialized, and each call to `getCurrentSnapshot` will either return the // snapshot before or after a modification, but never one of an ongoing // modification. - void modify(const std::function& function); + template + ReturnType modify(const std::function& function); // Reset the updates represented by the underlying `DeltaTriples` and then // update the current snapshot. diff --git a/src/index/IndexImpl.cpp b/src/index/IndexImpl.cpp index 1f7279cd3c..ed06c49916 100644 --- a/src/index/IndexImpl.cpp +++ b/src/index/IndexImpl.cpp @@ -891,7 +891,7 @@ void IndexImpl::createFromOnDiskIndex(const string& onDiskBase) { // `Permutation`class, but we first have to deal with The delta triples for // the additional permutations. auto setMetadata = [this](const Permutation& p) { - deltaTriplesManager().modify([&p](DeltaTriples& deltaTriples) { + deltaTriplesManager().modify([&p](DeltaTriples& deltaTriples) { deltaTriples.setOriginalMetadata(p.permutation(), p.metaData().blockData()); }); diff --git a/src/index/StringSortComparator.h b/src/index/StringSortComparator.h index da0324ff5e..81829f226e 100644 --- a/src/index/StringSortComparator.h +++ b/src/index/StringSortComparator.h @@ -313,7 +313,7 @@ class LocaleManager { * different steps in icu. */ std::unique_ptr _collator[6]; UColAttributeValue _ignorePunctuationStatus = - UCOL_NON_IGNORABLE; // how to sort punctuations etc. + UCOL_NON_IGNORABLE; // how to sort punctuation etc. const icu::Normalizer2* _normalizer = nullptr; // actually locale-independent but useful to be placed here diff --git a/src/util/AsioHelpers.h b/src/util/AsioHelpers.h index 2e75cd2048..6ae990a44d 100644 --- a/src/util/AsioHelpers.h +++ b/src/util/AsioHelpers.h @@ -133,7 +133,7 @@ inline net::awaitable interruptible( running->clear(); net::dispatch(strand, [timer = std::move(timer)]() { timer->cancel(); }); }; - // Provide callback to outer world in order to cancel the timer pre-emptively. + // Provide callback to outer world in order to cancel the timer preemptively. cancelCallback.set_value(cancelTimer); auto timerLoop = [](std::shared_ptr timer, diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5520ce2ff9..3a04b9d201 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -152,6 +152,8 @@ addLinkAndDiscoverTestSerial(IdTripleTest index) addLinkAndDiscoverTestSerial(DeltaTriplesTest index) +addLinkAndDiscoverTest(DeltaTriplesCountTest index) + addLinkAndDiscoverTest(EngineTest engine) addLinkAndDiscoverTest(JoinTest engine) diff --git a/test/DeltaTriplesCountTest.cpp b/test/DeltaTriplesCountTest.cpp new file mode 100644 index 0000000000..1a72f356de --- /dev/null +++ b/test/DeltaTriplesCountTest.cpp @@ -0,0 +1,27 @@ +// Copyright 2025, University of Freiburg +// Chair of Algorithms and Data Structures. +// Authors: +// 2025 Julian Mundhahs + +#include + +#include "index/DeltaTriples.h" + +// _____________________________________________________________________________ +TEST(DeltaTriplesCountTest, toJson) { + constexpr DeltaTriplesCount count{5, 3}; + const nlohmann::json expected = { + {"inserted", 5}, {"deleted", 3}, {"total", 8}}; + nlohmann::json actual; + to_json(actual, count); + EXPECT_THAT(actual, testing::Eq(expected)); +} + +// _____________________________________________________________________________ +TEST(DeltaTriplesCountTest, subtractOperator) { + constexpr DeltaTriplesCount count1{10, 5}; + constexpr DeltaTriplesCount count2{3, 2}; + constexpr DeltaTriplesCount expected{7, 3}; + const DeltaTriplesCount actual = count1 - count2; + EXPECT_THAT(actual, testing::Eq(expected)); +} diff --git a/test/DeltaTriplesTest.cpp b/test/DeltaTriplesTest.cpp index 88ec0c76e5..e0fe673c39 100644 --- a/test/DeltaTriplesTest.cpp +++ b/test/DeltaTriplesTest.cpp @@ -132,7 +132,7 @@ TEST_F(DeltaTriplesTest, insertTriplesAndDeleteTriples) { makeIdTriples(vocab, localVocab, triples))); }; // A matcher that checks the state of a `DeltaTriples`: - // - `numInserted()` and `numDeleted()` + // - `numInserted()` and `numDeleted()` and the derived `getCounts()` // - `numTriples()` for all `LocatedTriplesPerBlock` // - the inserted and deleted triples (unordered) auto StateIs = [&UnorderedTriplesAre]( @@ -350,14 +350,14 @@ TEST_F(DeltaTriplesTest, DeltaTriplesManager) { {" ", absl::StrCat(" "), absl::StrCat(" ")}); // Insert the `triplesToInsert`. - deltaTriplesManager.modify([&](DeltaTriples& deltaTriples) { + deltaTriplesManager.modify([&](DeltaTriples& deltaTriples) { deltaTriples.insertTriples(cancellationHandle, triplesToInsert); }); // We should have successfully completed an update, so the snapshot // pointer should have changed. EXPECT_NE(beforeUpdate, deltaTriplesManager.getCurrentSnapshot()); // Delete the `triplesToDelete`. - deltaTriplesManager.modify([&](DeltaTriples& deltaTriples) { + deltaTriplesManager.modify([&](DeltaTriples& deltaTriples) { deltaTriples.deleteTriples(cancellationHandle, triplesToDelete); }); diff --git a/test/DeltaTriplesTestHelpers.h b/test/DeltaTriplesTestHelpers.h index bf64175a17..17d8e994ce 100644 --- a/test/DeltaTriplesTestHelpers.h +++ b/test/DeltaTriplesTestHelpers.h @@ -37,14 +37,17 @@ inline auto NumTriplesInAllPermutations = return InAllPermutations(AD_PROPERTY(LocatedTriplesPerBlock, numTriples, testing::Eq(expectedNumTriples))); }; -// A matcher that checks `numInserted()` and `numDeleted()` of a `DeltaTriples` -// and `numTriples()` for all `LocatedTriplesPerBlock` of the `DeltaTriples`. +// A matcher that checks `numInserted()`, `numDeleted()` and the derived +// `getCounts()` of a `DeltaTriples` and `numTriples()` for all +// `LocatedTriplesPerBlock` of the `DeltaTriples`. inline auto NumTriples = [](size_t inserted, size_t deleted, size_t inAllPermutations) -> testing::Matcher { return testing::AllOf( AD_PROPERTY(DeltaTriples, numInserted, testing::Eq(inserted)), AD_PROPERTY(DeltaTriples, numDeleted, testing::Eq(deleted)), + AD_PROPERTY(DeltaTriples, getCounts, + testing::Eq(DeltaTriplesCount{inserted, deleted})), NumTriplesInAllPermutations(inAllPermutations)); }; diff --git a/test/ExecuteUpdateTest.cpp b/test/ExecuteUpdateTest.cpp index 83015cac30..b376660785 100644 --- a/test/ExecuteUpdateTest.cpp +++ b/test/ExecuteUpdateTest.cpp @@ -119,7 +119,9 @@ TEST(ExecuteUpdate, computeGraphUpdateQuads) { auto pq = SparqlParser::parseQuery(update); QueryPlanner qp{qec, sharedHandle}; const auto qet = qp.createExecutionTree(pq); - return ExecuteUpdate::computeGraphUpdateQuads(index, pq, qet, sharedHandle); + UpdateMetadata metadata; + return ExecuteUpdate::computeGraphUpdateQuads(index, pq, qet, sharedHandle, + metadata); }; auto expectComputeGraphUpdateQuads = [&executeComputeGraphUpdateQuads]( diff --git a/test/ServerTest.cpp b/test/ServerTest.cpp index 72480c9434..cd5c9413da 100644 --- a/test/ServerTest.cpp +++ b/test/ServerTest.cpp @@ -2,6 +2,7 @@ // Chair of Algorithms and Data Structures. // Author: Julian Mundhahs (mundhahj@tf.uni-freiburg.de) +#include #include #include #include @@ -10,6 +11,7 @@ #include "util/GTestHelpers.h" #include "util/HttpRequestHelpers.h" +#include "util/IndexTestHelpers.h" #include "util/http/HttpUtils.h" #include "util/http/UrlParser.h" @@ -245,3 +247,50 @@ TEST(ServerTest, createMessageSender) { "SELECT * WHERE { ?a ?b ?c }"), testing::HasSubstr("Assertion `queryHubLock` failed.")); } + +TEST(ServerTest, createResponseMetadata) { + // Setup the datastructures + const ad_utility::SharedCancellationHandle handle = + std::make_shared>(); + const ad_utility::Timer requestTimer{ + ad_utility::Timer::InitialStatus::Stopped}; + QueryExecutionContext* qec = ad_utility::testing::getQec(" "); + const Index& index = qec->getIndex(); + DeltaTriples deltaTriples{index}; + const std::string update = "INSERT DATA { }"; + ParsedQuery pq = SparqlParser::parseQuery(update); + QueryPlanner qp(qec, handle); + QueryExecutionTree qet = qp.createExecutionTree(pq); + const Server::PlannedQuery plannedQuery{std::move(pq), std::move(qet)}; + + // Execute the update + DeltaTriplesCount countBefore = deltaTriples.getCounts(); + UpdateMetadata updateMetadata = ExecuteUpdate::executeUpdate( + index, plannedQuery.parsedQuery_, plannedQuery.queryExecutionTree_, + deltaTriples, handle); + DeltaTriplesCount countAfter = deltaTriples.getCounts(); + + // Assertions + json metadata = Server::createResponseMetadataForUpdate( + requestTimer, index, deltaTriples, plannedQuery, + plannedQuery.queryExecutionTree_, countBefore, updateMetadata, + countAfter); + json deltaTriplesJson{ + {"before", {{"inserted", 0}, {"deleted", 0}, {"total", 0}}}, + {"after", {{"inserted", 1}, {"deleted", 0}, {"total", 1}}}, + {"difference", {{"inserted", 1}, {"deleted", 0}, {"total", 1}}}}; + json locatedTriplesJson{ + {"SPO", {{"blocks-affected", 1}, {"blocks-total", 1}}}, + {"POS", {{"blocks-affected", 1}, {"blocks-total", 1}}}, + {"OSP", {{"blocks-affected", 1}, {"blocks-total", 1}}}, + {"SOP", {{"blocks-affected", 1}, {"blocks-total", 1}}}, + {"PSO", {{"blocks-affected", 1}, {"blocks-total", 1}}}, + {"OPS", {{"blocks-affected", 1}, {"blocks-total", 1}}}}; + EXPECT_THAT(metadata["update"], testing::Eq(update)); + EXPECT_THAT(metadata["status"], testing::Eq("OK")); + EXPECT_THAT(metadata["warnings"], + testing::Eq(std::vector{ + "SPARQL 1.1 Update for QLever is experimental."})); + EXPECT_THAT(metadata["delta-triples"], testing::Eq(deltaTriplesJson)); + EXPECT_THAT(metadata["located-triples"], testing::Eq(locatedTriplesJson)); +} From 2cf878cb2429cf7f4861deb274757b11c4aa0fbf Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Thu, 23 Jan 2025 18:11:13 +0100 Subject: [PATCH 04/28] Fix bug in lazy join in case of three or more successive empty blocks (#1690) --- src/engine/Filter.cpp | 4 +++- src/util/JoinAlgorithms/JoinAlgorithms.h | 4 ++++ test/FilterTest.cpp | 4 +--- test/JoinAlgorithmsTest.cpp | 12 ++++++++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/engine/Filter.cpp b/src/engine/Filter.cpp index 9ecdd85f7a..7f20b58108 100644 --- a/src/engine/Filter.cpp +++ b/src/engine/Filter.cpp @@ -75,7 +75,9 @@ ProtoResult Filter::computeResult(bool requestLaziness) { for (auto& [idTable, localVocab] : subRes->idTables()) { IdTable result = self->filterIdTable(subRes->sortedBy(), idTable, localVocab); - co_yield {std::move(result), std::move(localVocab)}; + if (!result.empty()) { + co_yield {std::move(result), std::move(localVocab)}; + } } }(std::move(subRes), this), resultSortedOn()}; diff --git a/src/util/JoinAlgorithms/JoinAlgorithms.h b/src/util/JoinAlgorithms/JoinAlgorithms.h index 89e1b6611f..0b897e1c8e 100644 --- a/src/util/JoinAlgorithms/JoinAlgorithms.h +++ b/src/util/JoinAlgorithms/JoinAlgorithms.h @@ -801,6 +801,10 @@ struct BlockZipperJoinImpl { for (size_t numBlocksRead = 0; it != end && numBlocksRead < 3; ++it, ++numBlocksRead) { if (ql::ranges::empty(*it)) { + // We haven't read a block, so we have to adapt the counter. + // NOTE: If `numBlocksRead == 0`, the underflow is no problem because + // the next operation is `++`. + --numBlocksRead; continue; } if (!eq((*it)[0], currentEl)) { diff --git a/test/FilterTest.cpp b/test/FilterTest.cpp index 7df2617c3f..b2201c2e94 100644 --- a/test/FilterTest.cpp +++ b/test/FilterTest.cpp @@ -87,13 +87,11 @@ TEST(Filter, verifyPredicateIsAppliedCorrectlyOnLazyEvaluation) { auto referenceTable1 = makeIdTableFromVector({{true}, {true}, {true}}, asBool); auto referenceTable2 = makeIdTableFromVector({{true}}, asBool); - IdTable referenceTable3{0, ad_utility::makeUnlimitedAllocator()}; auto m = matchesIdTable; EXPECT_THAT( toVector(std::move(generator)), - ElementsAre(m(referenceTable1), m(referenceTable2), m(referenceTable3), - m(referenceTable3), m(referenceTable2))); + ElementsAre(m(referenceTable1), m(referenceTable2), m(referenceTable2))); } // _____________________________________________________________________________ diff --git a/test/JoinAlgorithmsTest.cpp b/test/JoinAlgorithmsTest.cpp index 6996191e59..cac73cf2c1 100644 --- a/test/JoinAlgorithmsTest.cpp +++ b/test/JoinAlgorithmsTest.cpp @@ -182,6 +182,18 @@ TEST(JoinAlgorithms, JoinWithBlocksExactlyFourBlocksPerElement) { testJoin(a, b, expectedResult); } +// Test the handling of many empty blocks. +TEST(JoinAlgorithms, JoinWithEmptyBlocks) { + NestedBlock a{{{42, 1}, {42, 2}}, {{42, 3}}}; + // The join has to handle all the entries with `42` in the first column at + // once. In `b` there are more than 3 empty blocks between blocks with this + // entry. There was previously a bug in this case. + NestedBlock b{{{42, 16}}, {}, {}, {}, {}, {}, {}, {}, {}, {{42, 23}}}; + JoinResult expectedResult{{42, 1, 16}, {42, 1, 23}, {42, 2, 16}, + {42, 2, 23}, {42, 3, 16}, {42, 3, 23}}; + testJoin(a, b, expectedResult); +} + // ________________________________________________________________________________________ TEST(JoinAlgorithms, JoinWithBlocksMultipleBlocksPerElementBothSides) { NestedBlock a{{{42, 0}}, {{42, 1}, {42, 2}}, {{42, 3}, {67, 0}}}; From 6f6e0c4f48b83a5edb0e1a940cfc696567c209d8 Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Thu, 23 Jan 2025 18:17:44 +0100 Subject: [PATCH 05/28] Fix an out-of-bounds read in the OPTIONAL JOIN implementation (#1710) --- src/util/JoinAlgorithms/JoinAlgorithms.h | 3 +++ test/EngineTest.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/util/JoinAlgorithms/JoinAlgorithms.h b/src/util/JoinAlgorithms/JoinAlgorithms.h index 0b897e1c8e..17288c2599 100644 --- a/src/util/JoinAlgorithms/JoinAlgorithms.h +++ b/src/util/JoinAlgorithms/JoinAlgorithms.h @@ -491,6 +491,9 @@ void specialOptionalJoin( elFromFirstNotFoundAction(it); } it1 = next1; + if (it1 == end1) { + break; + } checkCancellation(); diff --git a/test/EngineTest.cpp b/test/EngineTest.cpp index e6fcc1e76f..860fa5521e 100644 --- a/test/EngineTest.cpp +++ b/test/EngineTest.cpp @@ -278,6 +278,18 @@ TEST(OptionalJoin, specialOptionalJoinTwoColumns) { IdTable expectedResult = makeIdTableFromVector( {{4, 1, 2, U}, {2, 1, 3, 3}, {1, 1, 4, U}, {2, 2, 2, 4}, {1, 3, 1, 1}}); + testOptionalJoin(a, b, jcls, expectedResult); + } + { + // Test a corner case that previously contained a bug. + IdTable a{makeIdTableFromVector({{4, U, 2}})}; + IdTable b{makeIdTableFromVector({{3, 3, 1}})}; + // Join a and b on the column pairs 1,2 and 2,1 (entries from columns 1 of + // a have to equal those of column 2 of b and vice versa). + JoinColumns jcls{{1, 2}, {2, 1}}; + + IdTable expectedResult = makeIdTableFromVector({{4, U, 2, U}}); + testOptionalJoin(a, b, jcls, expectedResult); } } From b10d0b739405eaf3f93ce9ba40388e9a13223ab8 Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Fri, 24 Jan 2025 00:29:53 +0100 Subject: [PATCH 06/28] Avoid reading and decompressing index blocks during query planning (#1725) Avoid reading and decompressing the first and last block of an index scan during query planning (which requires only a size estimate and not the exact size). This added significant performance overhead, when only few blocks were eventually read in the index scan. Instead, we now do the following: First check whether the first and last block fully match the scan specification (this is fast). Second, if this is not the case, assume that the number of elements is a fixed fraction of total number of elements in the block (which we know from the metadata). The fixed fraction is defined by the new runtime parameter `small-index-scan-size-estimate-divisor`. --- src/global/RuntimeParameters.h | 7 ++- src/index/CompressedRelation.cpp | 82 +++++++++++++++++++++++++------- test/engine/IndexScanTest.cpp | 3 +- test/engine/SpatialJoinTest.cpp | 14 +++--- 4 files changed, 81 insertions(+), 25 deletions(-) diff --git a/src/global/RuntimeParameters.h b/src/global/RuntimeParameters.h index 8e60725ffe..cce7e321b9 100644 --- a/src/global/RuntimeParameters.h +++ b/src/global/RuntimeParameters.h @@ -53,7 +53,12 @@ inline auto& RuntimeParameters() { Bool<"throw-on-unbound-variables">{false}, // Control up until which size lazy results should be cached. Caching // does cause significant overhead for this case. - MemorySizeParameter<"lazy-result-max-cache-size">{5_MB}}; + MemorySizeParameter<"lazy-result-max-cache-size">{5_MB}, + // When the result of an index scan is smaller than a single block, then + // its size estimate will be the size of the block divided by this + // value. + SizeT<"small-index-scan-size-estimate-divisor">{5}, + }; }(); return params; } diff --git a/src/index/CompressedRelation.cpp b/src/index/CompressedRelation.cpp index fc306e892f..c11081f66f 100644 --- a/src/index/CompressedRelation.cpp +++ b/src/index/CompressedRelation.cpp @@ -29,6 +29,35 @@ static auto getBeginAndEnd(auto& range) { return std::pair{ql::ranges::begin(range), ql::ranges::end(range)}; } +// Return true iff the `triple` is contained in the `scanSpec`. For example, the +// triple ` 42 0 3 ` is contained in the specs `U U U`, `42 U U` and `42 0 U` , +// but not in `42 2 U` where `U` means "scan for all possible values". +static auto isTripleInSpecification = + [](const ScanSpecification& scanSpec, + const CompressedBlockMetadata::PermutedTriple& triple) { + enum struct M { GuaranteedMatch, Mismatch, MustCheckNextElement }; + auto checkElement = [](const auto& optId, Id id) { + if (!optId.has_value()) { + return M::GuaranteedMatch; + } else if (optId.value() != id) { + return M::Mismatch; + } else { + return M::MustCheckNextElement; + } + }; + auto result = checkElement(scanSpec.col0Id(), triple.col0Id_); + if (result == M::MustCheckNextElement) { + result = checkElement(scanSpec.col1Id(), triple.col1Id_); + } + if (result == M::MustCheckNextElement) { + result = checkElement(scanSpec.col2Id(), triple.col2Id_); + } + // The case `result == M::MustCheckNextElement` can happen in the unlikely + // case that there only is a single triple in the block, which is scanned + // for explicitly. + return result != M::Mismatch; + }; + // modify the `block` according to the `limitOffset`. Also modify the // `limitOffset` to reflect the parts of the LIMIT and OFFSET that have been // performed by pruning this `block`. @@ -631,21 +660,46 @@ std::pair CompressedRelationReader::getResultSizeImpl( // a part of these blocks is actually part of the result, // set up a lambda which allows us to read these blocks, and returns // the size of the result. + size_t numResults = 0; + // Determine the total size of the result. + // First accumulate the complete blocks in the "middle" + std::size_t inserted = 0; + std::size_t deleted = 0; + auto readSizeOfPossiblyIncompleteBlock = [&](const auto& block) { - return readPossiblyIncompleteBlock(scanSpec, config, block, std::nullopt, - locatedTriplesPerBlock) - .numRows(); + if (exactSize) { + numResults += + readPossiblyIncompleteBlock(scanSpec, config, block, std::nullopt, + locatedTriplesPerBlock) + .numRows(); + } else { + // If the first and last triple of the block match, then we know that the + // whole block belongs to the result. + bool isComplete = isTripleInSpecification(scanSpec, block.firstTriple_) && + isTripleInSpecification(scanSpec, block.lastTriple_); + size_t divisor = + isComplete ? 1 + : RuntimeParameters() + .get<"small-index-scan-size-estimate-divisor">(); + const auto [ins, del] = + locatedTriplesPerBlock.numTriples(block.blockIndex_); + auto trunc = [divisor](size_t num) { + return std::max(std::min(num, 1ul), num / divisor); + }; + inserted += trunc(ins); + deleted += trunc(del); + numResults += trunc(block.numRows_); + } }; - size_t numResults = 0; // The first and the last block might be incomplete, compute // and store the partial results from them. if (beginBlock < endBlock) { - numResults += readSizeOfPossiblyIncompleteBlock(*beginBlock); + readSizeOfPossiblyIncompleteBlock(*beginBlock); ++beginBlock; } if (beginBlock < endBlock) { - numResults += readSizeOfPossiblyIncompleteBlock(*(endBlock - 1)); + readSizeOfPossiblyIncompleteBlock(*(endBlock - 1)); --endBlock; } @@ -653,10 +707,6 @@ std::pair CompressedRelationReader::getResultSizeImpl( return {numResults, numResults}; } - // Determine the total size of the result. - // First accumulate the complete blocks in the "middle" - std::size_t inserted = 0; - std::size_t deleted = 0; ql::ranges::for_each( ql::ranges::subrange{beginBlock, endBlock}, [&](const auto& block) { const auto [ins, del] = @@ -666,8 +716,8 @@ std::pair CompressedRelationReader::getResultSizeImpl( deleted += del; numResults += block.numRows_; } else { - // TODO We could cache the exact size as soon as we have - // merged the block once since the last update. + // TODO We could cache the exact size as soon as we + // have merged the block once since the last update. auto b = readAndDecompressBlock(block, config); numResults += b.has_value() ? b.value().block_.numRows() : 0u; } @@ -1366,10 +1416,10 @@ auto CompressedRelationWriter::createPermutationPair( // relation as its overhead is far too high for small relations. relation.swapColumns(c1Idx, c2Idx); - // We only need to sort by the columns of the triple + the graph column, - // not the additional payload. Note: We could also use - // `compareWithoutLocalVocab` to compare the IDs cheaper, but this sort - // is far from being a performance bottleneck. + // We only need to sort by the columns of the triple + the graph + // column, not the additional payload. Note: We could also use + // `compareWithoutLocalVocab` to compare the IDs cheaper, but this + // sort is far from being a performance bottleneck. auto compare = [](const auto& a, const auto& b) { return std::tie(a[0], a[1], a[2], a[3]) < std::tie(b[0], b[1], b[2], b[3]); diff --git a/test/engine/IndexScanTest.cpp b/test/engine/IndexScanTest.cpp index 62f01647a0..395a21261a 100644 --- a/test/engine/IndexScanTest.cpp +++ b/test/engine/IndexScanTest.cpp @@ -498,7 +498,8 @@ TEST(IndexScan, getResultSizeOfScan) { SparqlTripleSimple scanTriple{I::fromIriref(""), I::fromIriref("

"), I::fromIriref("")}; IndexScan scan{qec, Permutation::Enum::POS, scanTriple}; - EXPECT_EQ(scan.getSizeEstimate(), 0); + EXPECT_EQ(scan.getSizeEstimate(), 1); + EXPECT_EQ(scan.getExactSize(), 0); } { SparqlTripleSimple scanTriple{I::fromIriref(""), I::fromIriref("

"), diff --git a/test/engine/SpatialJoinTest.cpp b/test/engine/SpatialJoinTest.cpp index ed49d54e86..766801ee3b 100644 --- a/test/engine/SpatialJoinTest.cpp +++ b/test/engine/SpatialJoinTest.cpp @@ -926,22 +926,22 @@ class SpatialJoinMultiplicityAndSizeEstimateTest spatialJoin = static_cast(spJoin2.get()); auto varColsMap = spatialJoin->getExternallyVisibleVariableColumns(); - assertMultiplicity(subj1.getVariable(), 9.8, spatialJoin, varColsMap); - assertMultiplicity(obj1.getVariable(), 7.0, spatialJoin, varColsMap); - assertMultiplicity(subj2.getVariable(), 9.8, spatialJoin, varColsMap); - assertMultiplicity(obj2.getVariable(), 7.0, spatialJoin, varColsMap); + assertMultiplicity(subj1.getVariable(), 4.2, spatialJoin, varColsMap); + assertMultiplicity(obj1.getVariable(), 3.0, spatialJoin, varColsMap); + assertMultiplicity(subj2.getVariable(), 4.2, spatialJoin, varColsMap); + assertMultiplicity(obj2.getVariable(), 3.0, spatialJoin, varColsMap); ASSERT_TRUE( spatialJoin->onlyForTestingGetDistanceVariable().has_value()); assertMultiplicity(Variable{"?distanceForTesting"}, 1, spatialJoin, varColsMap); } else { - ASSERT_EQ(leftChild->getSizeEstimate(), 7); - ASSERT_EQ(rightChild->getSizeEstimate(), 7); + auto leftEstimate = leftChild->getSizeEstimate(); + auto rightEstimate = rightChild->getSizeEstimate(); auto spJoin1 = spatialJoin->addChild(firstChild, firstVariable); spatialJoin = static_cast(spJoin1.get()); auto spJoin2 = spatialJoin->addChild(secondChild, secondVariable); spatialJoin = static_cast(spJoin2.get()); - ASSERT_LE(spatialJoin->getSizeEstimate(), 49); + ASSERT_LE(spatialJoin->getSizeEstimate(), leftEstimate * rightEstimate); } } } From ff479223aa6d7597e886b3fee24f1167e643cf5e Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Fri, 24 Jan 2025 00:31:05 +0100 Subject: [PATCH 07/28] Various efficiency improvements for the query planner (#1724) These are rather minor changes in the code, but with a very significant effect for "large" queries (that is, queries with many operations): 1. Replace `std::optional` by `boost::optional`; before this change, many copies of `TripleGraph`s were made for large queries; note that `std::optional` does not work for `const &` 2. Avoid re-building the cache key from scratch for each call to `Operation::getCacheKey` 3. Avoid re-computing the result width from scratch for each call to `Operation::getResultWidth` 4. We can now call `setLimit` for a whole `QueryExecutionTree` (before: only for an individual `Operation`); this invalidates the cache key and the result width 5. Make the variable name check (which calls the ANTLR parser) an expensive check, so that these are only executed in test and in debug mode --- src/engine/CartesianProductJoin.cpp | 9 +++++---- src/engine/QueryExecutionTree.cpp | 2 +- src/engine/QueryExecutionTree.h | 18 +++++++++++++++++- src/engine/QueryPlanner.cpp | 8 ++++---- src/engine/QueryPlanner.h | 3 ++- src/parser/data/Variable.cpp | 2 +- test/SparqlDataTypesTest.cpp | 15 ++++++++++----- test/parser/data/VariableTest.cpp | 14 +++++++++----- 8 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/engine/CartesianProductJoin.cpp b/src/engine/CartesianProductJoin.cpp index cedb832648..d99285479c 100644 --- a/src/engine/CartesianProductJoin.cpp +++ b/src/engine/CartesianProductJoin.cpp @@ -255,13 +255,14 @@ CartesianProductJoin::calculateSubResults(bool requestLaziness) { auto children = childView(); AD_CORRECTNESS_CHECK(!ql::ranges::empty(children)); // Get all child results (possibly with limit, see above). - for (Operation& child : children) { - if (limitIfPresent.has_value() && child.supportsLimit()) { - child.setLimit(limitIfPresent.value()); + for (std::shared_ptr& childTree : children_) { + if (limitIfPresent.has_value() && childTree->supportsLimit()) { + childTree->setLimit(limitIfPresent.value()); } + auto& child = *childTree->getRootOperation(); // To preserve order of the columns we can only consume the first child // lazily. In the future this restriction may be lifted by permutating the - // columns afterwards. + // columns afterward. bool isLast = &child == &children.back(); bool requestLazy = requestLaziness && isLast; auto result = child.getResult( diff --git a/src/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index 7f22de2020..9b58d0bb78 100644 --- a/src/engine/QueryExecutionTree.cpp +++ b/src/engine/QueryExecutionTree.cpp @@ -24,7 +24,7 @@ QueryExecutionTree::QueryExecutionTree(QueryExecutionContext* const qec) // _____________________________________________________________________________ std::string QueryExecutionTree::getCacheKey() const { - return rootOperation_->getCacheKey(); + return cacheKey_.value(); } // _____________________________________________________________________________ diff --git a/src/engine/QueryExecutionTree.h b/src/engine/QueryExecutionTree.h index 0eac785f16..c8bd4f5fc9 100644 --- a/src/engine/QueryExecutionTree.h +++ b/src/engine/QueryExecutionTree.h @@ -25,6 +25,8 @@ class QueryExecutionTree { std::shared_ptr operation) : QueryExecutionTree(qec) { rootOperation_ = std::move(operation); + resultWidth_ = rootOperation_->getResultWidth(); + cacheKey_ = rootOperation_->getCacheKey(); readFromCache(); } @@ -58,7 +60,7 @@ class QueryExecutionTree { std::optional getVariableColumnOrNullopt( const Variable& variable) const; - size_t getResultWidth() const { return rootOperation_->getResultWidth(); } + size_t getResultWidth() const { return resultWidth_.value(); } std::shared_ptr getResult(bool requestLaziness = false) const { return rootOperation_->getResult( @@ -203,11 +205,25 @@ class QueryExecutionTree { s << tree.getRootOperation()->getDescriptor(); } + bool supportsLimit() const { return getRootOperation()->supportsLimit(); } + + // Set the value of the `LIMIT` clause that will be applied to the result of + // this operation. + void setLimit(const LimitOffsetClause& limitOffsetClause) { + getRootOperation()->setLimit(limitOffsetClause); + // Setting the limit invalidates the `cacheKey` as well as the + // `sizeEstimate`. + cacheKey_ = getRootOperation()->getCacheKey(); + sizeEstimate_ = getRootOperation()->getSizeEstimate(); + } + private: QueryExecutionContext* qec_; // No ownership std::shared_ptr rootOperation_ = nullptr; // Owned child. Will be deleted at deconstruction. std::optional sizeEstimate_ = std::nullopt; + std::optional cacheKey_ = std::nullopt; + std::optional resultWidth_ = std::nullopt; bool isRoot_ = false; // used to distinguish the root from child // operations/subtrees when pinning only the result. diff --git a/src/engine/QueryPlanner.cpp b/src/engine/QueryPlanner.cpp index 9dd6b5599c..60ea4feeb6 100644 --- a/src/engine/QueryPlanner.cpp +++ b/src/engine/QueryPlanner.cpp @@ -185,7 +185,7 @@ std::vector QueryPlanner::createExecutionTrees( for (auto& plan : lastRow) { if (plan._qet->getRootOperation()->supportsLimit()) { - plan._qet->getRootOperation()->setLimit(pq._limitOffset); + plan._qet->setLimit(pq._limitOffset); } } @@ -1793,7 +1793,7 @@ size_t QueryPlanner::findSmallestExecutionTree( // _____________________________________________________________________________ std::vector QueryPlanner::createJoinCandidates( const SubtreePlan& ain, const SubtreePlan& bin, - std::optional tg) const { + boost::optional tg) const { bool swapForTesting = isInTestMode() && bin.type != SubtreePlan::OPTIONAL && ain._qet->getCacheKey() < bin._qet->getCacheKey(); const auto& a = !swapForTesting ? ain : bin; @@ -2261,7 +2261,7 @@ void QueryPlanner::GraphPatternPlanner::visitGroupOptionalOrMinus( // whether `b` is from an OPTIONAL or MINUS. for (const auto& a : candidatePlans_.at(0)) { for (const auto& b : candidates) { - auto vec = planner_.createJoinCandidates(a, b, std::nullopt); + auto vec = planner_.createJoinCandidates(a, b, boost::none); nextCandidates.insert(nextCandidates.end(), std::make_move_iterator(vec.begin()), std::make_move_iterator(vec.end())); @@ -2572,7 +2572,7 @@ void QueryPlanner::GraphPatternPlanner::visitSubquery( ql::ranges::for_each(candidatesForSubquery, setSelectedVariables); // A subquery must also respect LIMIT and OFFSET clauses ql::ranges::for_each(candidatesForSubquery, [&](SubtreePlan& plan) { - plan._qet->getRootOperation()->setLimit(arg.get()._limitOffset); + plan._qet->setLimit(arg.get()._limitOffset); }); visitGroupOptionalOrMinus(std::move(candidatesForSubquery)); } diff --git a/src/engine/QueryPlanner.h b/src/engine/QueryPlanner.h index 52ee540a0a..201ad7e3c5 100644 --- a/src/engine/QueryPlanner.h +++ b/src/engine/QueryPlanner.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include "engine/CheckUsePatternTrick.h" @@ -324,7 +325,7 @@ class QueryPlanner { [[nodiscard]] std::vector createJoinCandidates( const SubtreePlan& a, const SubtreePlan& b, - std::optional tg) const; + boost::optional tg) const; // Used internally by `createJoinCandidates`. If `a` or `b` is a transitive // path operation and the other input can be bound to this transitive path diff --git a/src/parser/data/Variable.cpp b/src/parser/data/Variable.cpp index 00371c3537..bf6a774008 100644 --- a/src/parser/data/Variable.cpp +++ b/src/parser/data/Variable.cpp @@ -14,7 +14,7 @@ // ___________________________________________________________________________ Variable::Variable(std::string name, bool checkName) : _name{std::move(name)} { - if (checkName) { + if (checkName && ad_utility::areExpensiveChecksEnabled) { AD_CONTRACT_CHECK(isValidVariableName(_name), [this]() { return absl::StrCat("\"", _name, "\" is not a valid SPARQL variable"); }); diff --git a/test/SparqlDataTypesTest.cpp b/test/SparqlDataTypesTest.cpp index a84e39f8d4..57a0ec1e85 100644 --- a/test/SparqlDataTypesTest.cpp +++ b/test/SparqlDataTypesTest.cpp @@ -198,11 +198,16 @@ TEST(SparqlDataTypesTest, VariableNormalizesDollarSign) { } TEST(SparqlDataTypesTest, VariableInvalidNamesThrowException) { - EXPECT_THROW(Variable{"no_leading_var_or_dollar"}, ad_utility::Exception); - EXPECT_THROW(Variable{""}, ad_utility::Exception); - EXPECT_THROW(Variable{"? var with space"}, ad_utility::Exception); - EXPECT_THROW(Variable{"?"}, ad_utility::Exception); - EXPECT_THROW(Variable{"$"}, ad_utility::Exception); + if constexpr (!ad_utility::areExpensiveChecksEnabled) { + GTEST_SKIP() + << "validity of variable names is only checked with expensive checks"; + } + EXPECT_THROW(Variable("no_leading_var_or_dollar", true), + ad_utility::Exception); + EXPECT_THROW(Variable("", true), ad_utility::Exception); + EXPECT_THROW(Variable("? var with space", true), ad_utility::Exception); + EXPECT_THROW(Variable("?", true), ad_utility::Exception); + EXPECT_THROW(Variable("$", true), ad_utility::Exception); } TEST(SparqlDataTypesTest, VariableEvaluatesCorrectlyBasedOnContext) { diff --git a/test/parser/data/VariableTest.cpp b/test/parser/data/VariableTest.cpp index 38e16743ff..a3cf9812ae 100644 --- a/test/parser/data/VariableTest.cpp +++ b/test/parser/data/VariableTest.cpp @@ -9,14 +9,18 @@ // _____________________________________________________________________________ TEST(Variable, legalAndIllegalNames) { - EXPECT_NO_THROW(Variable("?x")); - EXPECT_NO_THROW(Variable("$x")); - EXPECT_NO_THROW(Variable("?ql_matching_word_thür")); + if constexpr (!ad_utility::areExpensiveChecksEnabled) { + GTEST_SKIP() + << "legality of variable names is only checked with expensive checks"; + } + EXPECT_NO_THROW(Variable("?x", true)); + EXPECT_NO_THROW(Variable("$x", true)); + EXPECT_NO_THROW(Variable("?ql_matching_word_thür", true)); // No leading ? or $ auto matcher = ::testing::HasSubstr("not a valid SPARQL variable"); - AD_EXPECT_THROW_WITH_MESSAGE(Variable("x"), matcher); - AD_EXPECT_THROW_WITH_MESSAGE(Variable("?x spaceInVar"), matcher); + AD_EXPECT_THROW_WITH_MESSAGE(Variable("x", true), matcher); + AD_EXPECT_THROW_WITH_MESSAGE(Variable("?x spaceInVar", true), matcher); } // _____________________________________________________________________________ From d79cf60f2f1f23f7c54316262d4190c6ae0dab31 Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Fri, 24 Jan 2025 16:56:51 +0100 Subject: [PATCH 08/28] Allow the disabling of websocket updates (#1726) The websocket updates (for the live updates of the "Analysis" tree) can significantly slow down queries with very many cheap operations. This change adds a runtime parameter `websocket-updates-enabled` for controlling whether websocket updates should be performed or not. --- src/engine/Operation.cpp | 2 +- src/engine/QueryExecutionContext.h | 12 ++++++++++++ src/global/RuntimeParameters.h | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/engine/Operation.cpp b/src/engine/Operation.cpp index 3a25e7521c..ed5d9d3cc6 100644 --- a/src/engine/Operation.cpp +++ b/src/engine/Operation.cpp @@ -598,7 +598,7 @@ const vector& Operation::getResultSortedOn() const { // _____________________________________________________________________________ void Operation::signalQueryUpdate() const { - if (_executionContext) { + if (_executionContext && _executionContext->areWebsocketUpdatesEnabled()) { _executionContext->signalQueryUpdate(*_rootRuntimeInfo); } } diff --git a/src/engine/QueryExecutionContext.h b/src/engine/QueryExecutionContext.h index dd9adf12b2..1e891a398f 100644 --- a/src/engine/QueryExecutionContext.h +++ b/src/engine/QueryExecutionContext.h @@ -5,6 +5,8 @@ #pragma once +#include + #include #include @@ -143,6 +145,12 @@ class QueryExecutionContext { bool _pinSubtrees; bool _pinResult; + // If false, then no updates of the runtime information should be sent via the + // websocket connection for performance reasons. + bool areWebsocketUpdatesEnabled() const { + return areWebsocketUpdatesEnabled_; + } + private: const Index& _index; @@ -158,4 +166,8 @@ class QueryExecutionContext { QueryPlanningCostFactors _costFactors; SortPerformanceEstimator _sortPerformanceEstimator; std::function updateCallback_; + // Cache the state of that runtime parameter to reduce the contention of the + // mutex. + bool areWebsocketUpdatesEnabled_ = + RuntimeParameters().get<"websocket-updates-enabled">(); }; diff --git a/src/global/RuntimeParameters.h b/src/global/RuntimeParameters.h index cce7e321b9..c082b14ca4 100644 --- a/src/global/RuntimeParameters.h +++ b/src/global/RuntimeParameters.h @@ -20,6 +20,7 @@ inline auto& RuntimeParameters() { // clearly misunderstand something about static initialization. static ad_utility::Parameters params = []() { using namespace std::chrono_literals; + using namespace ad_utility::memory_literals; auto ensureStrictPositivity = [](auto&& parameter) { parameter.setParameterConstraint( [](std::chrono::seconds value, std::string_view parameterName) { @@ -54,6 +55,7 @@ inline auto& RuntimeParameters() { // Control up until which size lazy results should be cached. Caching // does cause significant overhead for this case. MemorySizeParameter<"lazy-result-max-cache-size">{5_MB}, + Bool<"websocket-updates-enabled">{true}, // When the result of an index scan is smaller than a single block, then // its size estimate will be the size of the block divided by this // value. From 432cd644b34bd04f8e0f2c005e797e44d87e0f33 Mon Sep 17 00:00:00 2001 From: gpicciuca Date: Fri, 24 Jan 2025 17:57:22 +0100 Subject: [PATCH 09/28] Consistently replace `std::ranges` by `ql::ranges` (#1729) When building with `-DUSE_CPP_17_BACKPORT=ON`, then `range-v3` is used instead of `std::ranges` in all places (types, functions, concepts). --- benchmark/GroupByHashMapBenchmark.cpp | 12 ++-- benchmark/JoinAlgorithmBenchmark.cpp | 44 +++++++------- benchmark/ParallelMergeBenchmark.cpp | 4 +- benchmark/infrastructure/BenchmarkMain.cpp | 16 ++--- .../BenchmarkMeasurementContainer.cpp | 8 +-- benchmark/infrastructure/BenchmarkToJson.cpp | 4 +- benchmark/util/ResultTableColumnOperations.h | 4 +- src/backports/algorithm.h | 4 +- src/engine/AddCombinedRowToTable.h | 2 +- src/engine/CartesianProductJoin.cpp | 16 ++--- src/engine/CartesianProductJoin.h | 13 ++--- src/engine/Describe.cpp | 9 ++- src/engine/Distinct.cpp | 2 +- src/engine/LocalVocab.h | 4 +- src/engine/MultiColumnJoin.cpp | 2 +- src/engine/QueryPlanner.cpp | 8 ++- src/engine/Result.h | 8 ++- src/engine/Service.cpp | 2 +- src/engine/TextIndexScanForEntity.cpp | 6 +- src/engine/TransitivePathBase.h | 2 +- src/engine/TransitivePathImpl.h | 6 +- .../idTable/CompressedExternalIdTable.h | 6 +- src/engine/idTable/IdTable.h | 15 +++-- .../sparqlExpressions/AggregateExpression.h | 4 +- .../PrefilterExpressionIndex.cpp | 6 +- .../RelationalExpressions.cpp | 2 +- .../SparqlExpressionGenerators.h | 8 +-- src/global/ValueIdComparators.h | 2 +- src/index/CompressedRelation.cpp | 29 ++++++---- src/index/DeltaTriples.cpp | 4 +- src/index/IndexImpl.Text.cpp | 2 +- src/index/IndexImpl.cpp | 11 ++-- .../VocabularyInMemoryBinSearch.cpp | 2 +- .../vocabulary/VocabularyInMemoryBinSearch.h | 2 +- .../vocabulary/VocabularyInternalExternal.h | 4 +- .../sparqlParser/SparqlQleverVisitor.cpp | 4 +- src/util/Algorithm.h | 9 +-- src/util/BlankNodeManager.h | 4 +- src/util/ChunkedForLoop.h | 14 ++--- src/util/Generators.h | 2 +- src/util/JoinAlgorithms/JoinAlgorithms.h | 58 ++++++++++--------- src/util/ParallelMultiwayMerge.h | 8 +-- src/util/StringUtils.h | 34 ++++++----- test/OperationTest.cpp | 2 +- 44 files changed, 219 insertions(+), 189 deletions(-) diff --git a/benchmark/GroupByHashMapBenchmark.cpp b/benchmark/GroupByHashMapBenchmark.cpp index 346c9da931..780785e9bc 100644 --- a/benchmark/GroupByHashMapBenchmark.cpp +++ b/benchmark/GroupByHashMapBenchmark.cpp @@ -364,7 +364,7 @@ class GroupByHashMapBenchmark : public BenchmarkInterface { } else { firstColumn = generateRandomGroupVec(numInputRows, numGroups); } - std::ranges::transform( + ql::ranges::transform( firstColumn.begin(), firstColumn.end(), groupValues.begin(), [](size_t value) { return ValueId::makeFromInt(static_cast(value)); @@ -375,7 +375,7 @@ class GroupByHashMapBenchmark : public BenchmarkInterface { auto localVocab = LocalVocab{}; if (valueTypes != ValueIdType::Strings) { auto secondColumn = generateRandomDoubleVec(numInputRows); - std::ranges::transform( + ql::ranges::transform( secondColumn.begin(), secondColumn.end(), otherValues.begin(), [&](double value) { if (valueTypes == ValueIdType::OnlyDouble) @@ -396,10 +396,10 @@ class GroupByHashMapBenchmark : public BenchmarkInterface { numInputRows, randomStringLength); localVocab = std::move(newLocalVocab); - std::ranges::transform(indices.begin(), indices.end(), - otherValues.begin(), [&](LocalVocabIndex idx) { - return ValueId::makeFromLocalVocabIndex(idx); - }); + ql::ranges::transform(indices.begin(), indices.end(), otherValues.begin(), + [&](LocalVocabIndex idx) { + return ValueId::makeFromLocalVocabIndex(idx); + }); } std::vector> variables = {Variable{"?a"}, diff --git a/benchmark/JoinAlgorithmBenchmark.cpp b/benchmark/JoinAlgorithmBenchmark.cpp index d0717744b8..553785609f 100644 --- a/benchmark/JoinAlgorithmBenchmark.cpp +++ b/benchmark/JoinAlgorithmBenchmark.cpp @@ -115,7 +115,7 @@ struct SetOfIdTableColumnElements { */ explicit SetOfIdTableColumnElements( const std::span& idTableColumnRef) { - std::ranges::for_each(idTableColumnRef, [this](const ValueId& id) { + ql::ranges::for_each(idTableColumnRef, [this](const ValueId& id) { if (auto numOccurrencesIterator = numOccurrences_.find(id); numOccurrencesIterator != numOccurrences_.end()) { (numOccurrencesIterator->second)++; @@ -190,7 +190,7 @@ static size_t createOverlapRandomly(IdTableAndJoinColumn* const smallerTable, // Create the overlap. ad_utility::HashMap> smallerTableElementToNewElement{}; - std::ranges::for_each( + ql::ranges::for_each( smallerTableJoinColumnRef, [&randomDouble, &probabilityToCreateOverlap, &smallerTableElementToNewElement, &randomBiggerTableElement, @@ -295,7 +295,7 @@ static size_t createOverlapRandomly(IdTableAndJoinColumn* const smallerTable, size_t newOverlapMatches{0}; ad_utility::HashMap> smallerTableElementToNewElement{}; - std::ranges::for_each( + ql::ranges::for_each( smallerTableJoinColumnSet.uniqueElements_, [&randomBiggerTableElement, &wantedNumNewOverlapMatches, &newOverlapMatches, &smallerTableElementToNewElement, @@ -326,7 +326,7 @@ static size_t createOverlapRandomly(IdTableAndJoinColumn* const smallerTable, }); // Overwrite the designated values in the smaller table. - std::ranges::for_each( + ql::ranges::for_each( smallerTableJoinColumnRef, [&smallerTableElementToNewElement](auto& id) { if (auto newValueIterator = smallerTableElementToNewElement.find(id); newValueIterator != smallerTableElementToNewElement.end()) { @@ -465,17 +465,17 @@ static std::vector mergeSortedVectors( std::vector mergedVector{}; // Merge. - std::ranges::for_each(intervals, [&mergedVector](std::vector elements) { + ql::ranges::for_each(intervals, [&mergedVector](std::vector elements) { if (mergedVector.empty() || elements.empty()) { - std::ranges::copy(elements, std::back_inserter(mergedVector)); + ql::ranges::copy(elements, std::back_inserter(mergedVector)); return; } const size_t idxOldLastElem = mergedVector.size() - 1; - std::ranges::copy(elements, std::back_inserter(mergedVector)); + ql::ranges::copy(elements, std::back_inserter(mergedVector)); if (mergedVector.at(idxOldLastElem) > mergedVector.at(idxOldLastElem + 1)) { - std::ranges::inplace_merge( + ql::ranges::inplace_merge( mergedVector, - std::ranges::next(mergedVector.begin(), idxOldLastElem + 1)); + ql::ranges::next(mergedVector.begin(), idxOldLastElem + 1)); } }); @@ -935,7 +935,7 @@ class GeneralInterfaceImplementation : public BenchmarkInterface { "' must be bigger than, or equal to, 0.")}; config.addValidator( [](const benchmarkSampleSizeRatiosValueType& vec) { - return std::ranges::all_of( + return ql::ranges::all_of( vec, [](const benchmarkSampleSizeRatiosValueType::value_type ratio) { return ratio >= 0.f; @@ -961,7 +961,7 @@ class GeneralInterfaceImplementation : public BenchmarkInterface { ".")}; config.addValidator( [](const benchmarkSampleSizeRatiosValueType& vec) { - return std::ranges::max(vec) <= + return ql::ranges::max(vec) <= getMaxValue() - 1.f; }, @@ -1056,9 +1056,9 @@ class GeneralInterfaceImplementation : public BenchmarkInterface { }, descriptor, descriptor, option); }; - std::ranges::for_each(std::vector{minBiggerTableRows, maxBiggerTableRows, - minSmallerTableRows}, - addCastableValidator); + ql::ranges::for_each(std::vector{minBiggerTableRows, maxBiggerTableRows, + minSmallerTableRows}, + addCastableValidator); } /* @@ -1684,7 +1684,7 @@ class BmOnlyBiggerTableSizeChanges final static_cast(getConfigVariables().minBiggerTableRows_) / static_cast(smallerTableNumRows)))}; auto growthFunction = createDefaultGrowthLambda( - 10.f, std::ranges::max(minRatio, 10.f), + 10.f, ql::ranges::max(minRatio, 10.f), generateNaturalNumberSequenceInterval(minRatio, 9.f)); ResultTable& table = makeGrowingBenchmarkTable( &results, tableName, "Row ratio", alwaysFalse, @@ -1742,8 +1742,8 @@ class BmOnlySmallerTableSizeChanges final for (const float ratioRows : mergeSortedVectors( {generateNaturalNumberSequenceInterval( getConfigVariables().minRatioRows_, - std::ranges::min(getConfigVariables().maxRatioRows_, - 10.f)), + ql::ranges::min(getConfigVariables().maxRatioRows_, + 10.f)), generateExponentInterval( 10.f, getConfigVariables().minRatioRows_, getConfigVariables().maxRatioRows_)})) { @@ -1755,7 +1755,7 @@ class BmOnlySmallerTableSizeChanges final // Returns the amount of rows in the smaller `IdTable`, used for the // measurements in a given row. auto growthFunction = createDefaultGrowthLambda( - 10UL, std::ranges::max( + 10UL, ql::ranges::max( static_cast( static_cast( getConfigVariables().minBiggerTableRows_) / @@ -1867,7 +1867,7 @@ class BmSampleSizeRatio final : public GeneralInterfaceImplementation { BenchmarkResults runAllBenchmarks() override { BenchmarkResults results{}; const auto& ratios{getConfigVariables().benchmarkSampleSizeRatios_}; - const float maxSampleSizeRatio{std::ranges::max(ratios)}; + const float maxSampleSizeRatio{ql::ranges::max(ratios)}; /* We work with the biggest possible smaller and bigger table. That should make @@ -2097,17 +2097,17 @@ class BmSmallerTableGrowsBiggerTableRemainsSameSize final static_cast(biggerTableNumRows) / static_cast(getConfigVariables().minSmallerTableRows_))}; std::vector smallerTableRows; - std::ranges::transform( + ql::ranges::transform( mergeSortedVectors( {generateNaturalNumberSequenceInterval( - 1.f, std::ranges::min(10.f, biggestRowRatio)), + 1.f, ql::ranges::min(10.f, biggestRowRatio)), generateExponentInterval(10.f, 10.f, biggestRowRatio)}), std::back_inserter(smallerTableRows), [&biggerTableNumRows](const float ratio) { return static_cast( static_cast(biggerTableNumRows) / ratio); }); - std::ranges::reverse(smallerTableRows); + ql::ranges::reverse(smallerTableRows); const size_t lastSmallerTableRow{smallerTableRows.back()}; auto growthFunction = createDefaultGrowthLambda( 10UL, lastSmallerTableRow + 1UL, std::move(smallerTableRows)); diff --git a/benchmark/ParallelMergeBenchmark.cpp b/benchmark/ParallelMergeBenchmark.cpp index 1f33b996ba..98535bc29b 100644 --- a/benchmark/ParallelMergeBenchmark.cpp +++ b/benchmark/ParallelMergeBenchmark.cpp @@ -28,12 +28,12 @@ class IdTableCompressedWriterBenchmark : public BenchmarkInterface { ad_utility::integerRange(numInputRows)) { res.push_back(gen()); } - std::ranges::sort(res); + ql::ranges::sort(res); return res; }; std::vector> inputs; inputs.resize(numInputs); - std::ranges::generate(inputs, generateRandomVec); + ql::ranges::generate(inputs, generateRandomVec); auto run = [&inputs]() { auto merger = ad_utility::parallelMultiwayMerge( diff --git a/benchmark/infrastructure/BenchmarkMain.cpp b/benchmark/infrastructure/BenchmarkMain.cpp index b605ed7ac0..8a720ff526 100644 --- a/benchmark/infrastructure/BenchmarkMain.cpp +++ b/benchmark/infrastructure/BenchmarkMain.cpp @@ -90,7 +90,7 @@ static void writeBenchmarkClassAndBenchmarkResultsToJsonFile( Print the configuration documentation of all registered benchmarks. */ static __attribute__((noreturn)) void printConfigurationOptionsAndExit() { - std::ranges::for_each( + ql::ranges::for_each( BenchmarkRegister::getAllRegisteredBenchmarks(), [](const BenchmarkInterface* bench) { std::cerr << createCategoryTitle( @@ -211,13 +211,13 @@ int main(int argc, char** argv) { // Actually processing the arguments. if (vm.count("print")) { // Print the results and metadata. - std::ranges::for_each(benchmarkClassAndResults, - [](const auto& pair) { - std::cout << benchmarkResultsToString(pair.first, - pair.second) - << "\n\n"; - }, - {}); + ql::ranges::for_each(benchmarkClassAndResults, + [](const auto& pair) { + std::cout << benchmarkResultsToString(pair.first, + pair.second) + << "\n\n"; + }, + {}); } if (vm.count("write")) { diff --git a/benchmark/infrastructure/BenchmarkMeasurementContainer.cpp b/benchmark/infrastructure/BenchmarkMeasurementContainer.cpp index 04bd28f41a..29f54e543d 100644 --- a/benchmark/infrastructure/BenchmarkMeasurementContainer.cpp +++ b/benchmark/infrastructure/BenchmarkMeasurementContainer.cpp @@ -129,7 +129,7 @@ void ResultTable::init(const std::string& descriptor, descriptorForLog_ = std::move(descriptorForLog); columnNames_ = columnNames; entries_.resize(rowNames.size()); - std::ranges::fill(entries_, std::vector(columnNames.size())); + ql::ranges::fill(entries_, std::vector(columnNames.size())); // Setting the row names. for (size_t row = 0; row < rowNames.size(); row++) { @@ -287,10 +287,10 @@ ResultTable::operator std::string() const { }); // Which of the entries is the longest? - columnMaxStringWidth.at(column) = std::ranges::max(stringWidthOfRow); + columnMaxStringWidth.at(column) = ql::ranges::max(stringWidthOfRow); // Is the name of the column bigger? - columnMaxStringWidth.at(column) = std::ranges::max( + columnMaxStringWidth.at(column) = ql::ranges::max( columnMaxStringWidth.at(column), columnNames_.at(column).length()); } @@ -384,7 +384,7 @@ void ResultGroup::deleteEntryImpl(T& entry) { }(); // Delete `entry`. - auto entryIterator{std::ranges::find( + auto entryIterator{ql::ranges::find( vec, &entry, [](const ad_utility::CopyableUniquePtr& pointer) { return pointer.get(); })}; diff --git a/benchmark/infrastructure/BenchmarkToJson.cpp b/benchmark/infrastructure/BenchmarkToJson.cpp index c00b478c4c..b5430304bf 100644 --- a/benchmark/infrastructure/BenchmarkToJson.cpp +++ b/benchmark/infrastructure/BenchmarkToJson.cpp @@ -46,8 +46,8 @@ static nlohmann::json transformIntoJsonArray( */ nlohmann::ordered_json jsonArray = nlohmann::ordered_json::array(); - std::ranges::transform(vec, std::back_inserter(jsonArray), - translationFunction); + ql::ranges::transform(vec, std::back_inserter(jsonArray), + translationFunction); return jsonArray; } diff --git a/benchmark/util/ResultTableColumnOperations.h b/benchmark/util/ResultTableColumnOperations.h index bf0ea96f6d..46883c6208 100644 --- a/benchmark/util/ResultTableColumnOperations.h +++ b/benchmark/util/ResultTableColumnOperations.h @@ -37,8 +37,8 @@ requires(sizeof...(ColumnInputTypes) > 0) void generateColumnWithColumnInput( // Using a column more than once is the sign of an error. std::array allColumnNums{ {inputColumns.columnNum_...}}; - std::ranges::sort(allColumnNums); - AD_CONTRACT_CHECK(std::ranges::adjacent_find(allColumnNums) == + ql::ranges::sort(allColumnNums); + AD_CONTRACT_CHECK(ql::ranges::adjacent_find(allColumnNums) == allColumnNums.end()); // Fill the result column. diff --git a/src/backports/algorithm.h b/src/backports/algorithm.h index 86b8ecec8d..2c14823a31 100644 --- a/src/backports/algorithm.h +++ b/src/backports/algorithm.h @@ -12,9 +12,9 @@ #include "backports/concepts.h" // The following defines namespaces `ql::ranges` and `ql::views` that are almost -// drop-in replacements for `std::ranges` and `std::views`. In C++20 mode (when +// drop-in replacements for `ql::ranges` and `std::views`. In C++20 mode (when // the `QLEVER_CPP_17` macro is not used), these namespaces are simply aliases -// for `std::ranges` and `std::views`. In C++17 mode they contain the ranges and +// for `ql::ranges` and `std::views`. In C++17 mode they contain the ranges and // views from Erice Niebler's `range-v3` library. NOTE: `ql::ranges::unique` // currently doesn't work, because the interface to this function is different // in both implementations. NOTE: There might be other caveats which we are diff --git a/src/engine/AddCombinedRowToTable.h b/src/engine/AddCombinedRowToTable.h index 45f51fb527..6aa8186631 100644 --- a/src/engine/AddCombinedRowToTable.h +++ b/src/engine/AddCombinedRowToTable.h @@ -356,7 +356,7 @@ class AddCombinedRowToIdTable { // Only merge non-null vocabs. auto range = currentVocabs_ | ql::views::filter(toBool) | ql::views::transform(dereference); - mergedVocab_.mergeWith(std::ranges::ref_view{range}); + mergedVocab_.mergeWith(ql::ranges::ref_view{range}); } } const IdTableView<0>& inputLeft() const { diff --git a/src/engine/CartesianProductJoin.cpp b/src/engine/CartesianProductJoin.cpp index d99285479c..fdd22d6542 100644 --- a/src/engine/CartesianProductJoin.cpp +++ b/src/engine/CartesianProductJoin.cpp @@ -182,9 +182,9 @@ VariableToColumnMap CartesianProductJoin::computeVariableToColumnMap() const { } // _____________________________________________________________________________ -IdTable CartesianProductJoin::writeAllColumns( - std::ranges::random_access_range auto idTables, size_t offset, size_t limit, - size_t lastTableOffset) const { +CPP_template_def(typename R)(requires ql::ranges::random_access_range) + IdTable CartesianProductJoin::writeAllColumns( + R idTables, size_t offset, size_t limit, size_t lastTableOffset) const { AD_CORRECTNESS_CHECK(offset >= lastTableOffset); IdTable result{getResultWidth(), getExecutionContext()->getAllocator()}; // TODO Find a solution to cheaply handle the case, that only a @@ -303,12 +303,14 @@ CartesianProductJoin::calculateSubResults(bool requestLaziness) { } // _____________________________________________________________________________ -Result::Generator CartesianProductJoin::produceTablesLazily( - LocalVocab mergedVocab, std::ranges::range auto idTables, size_t offset, - size_t limit, size_t lastTableOffset) const { +CPP_template_def(typename R)(requires ql::ranges::range) Result::Generator + CartesianProductJoin::produceTablesLazily(LocalVocab mergedVocab, + R idTables, size_t offset, + size_t limit, + size_t lastTableOffset) const { while (limit > 0) { uint64_t limitWithChunkSize = std::min(limit, chunkSize_); - IdTable idTable = writeAllColumns(std::ranges::ref_view(idTables), offset, + IdTable idTable = writeAllColumns(ql::ranges::ref_view(idTables), offset, limitWithChunkSize, lastTableOffset); size_t tableSize = idTable.size(); AD_CORRECTNESS_CHECK(tableSize <= limit); diff --git a/src/engine/CartesianProductJoin.h b/src/engine/CartesianProductJoin.h index 8c0a071c98..9988cb72e6 100644 --- a/src/engine/CartesianProductJoin.h +++ b/src/engine/CartesianProductJoin.h @@ -94,9 +94,9 @@ class CartesianProductJoin : public Operation { // rows to write at most. `lastTableOffset` is the offset of the last table, // to account for cases where the last table does not cover the whole result // and so index 0 of a table does not correspond to row 0 of the result. - IdTable writeAllColumns(std::ranges::random_access_range auto idTables, - size_t offset, size_t limit, - size_t lastTableOffset = 0) const; + CPP_template(typename R)(requires ql::ranges::random_access_range) IdTable + writeAllColumns(R idTables, size_t offset, size_t limit, + size_t lastTableOffset = 0) const; // Calculate the subresults of the children and store them into a vector. If // the rightmost child can produce a lazy result, it will be stored outside of @@ -114,10 +114,9 @@ class CartesianProductJoin : public Operation { // `lastTableOffset` is the offset of the last table in the range. This is // used to handle `IdTable`s yielded by generators where the range of indices // they represent do not cover the whole result. - Result::Generator produceTablesLazily(LocalVocab mergedVocab, - std::ranges::range auto idTables, - size_t offset, size_t limit, - size_t lastTableOffset = 0) const; + CPP_template(typename R)(requires ql::ranges::range) Result::Generator + produceTablesLazily(LocalVocab mergedVocab, R idTables, size_t offset, + size_t limit, size_t lastTableOffset = 0) const; // Similar to `produceTablesLazily` but can handle a single lazy result. Result::Generator createLazyConsumer( diff --git a/src/engine/Describe.cpp b/src/engine/Describe.cpp index 5fb01dbe6c..a0c43222d2 100644 --- a/src/engine/Describe.cpp +++ b/src/engine/Describe.cpp @@ -53,10 +53,9 @@ string Describe::getCacheKeyImpl() const { const auto& defaultGraphs = describe_.datasetClauses_.defaultGraphs_; if (defaultGraphs.has_value()) { std::vector graphIdVec; - std::ranges::transform(defaultGraphs.value(), - std::back_inserter(graphIdVec), - &TripleComponent::toRdfLiteral); - std::ranges::sort(graphIdVec); + ql::ranges::transform(defaultGraphs.value(), std::back_inserter(graphIdVec), + &TripleComponent::toRdfLiteral); + ql::ranges::sort(graphIdVec); absl::StrAppend(&result, "\nFiltered by Graphs:", absl::StrJoin(graphIdVec, " ")); } @@ -218,7 +217,7 @@ IdTable Describe::getIdsToDescribe(const Result& result, // Copy the `Id`s from the hash set to an `IdTable`. IdTable idsAsTable{1, allocator()}; idsAsTable.resize(idsToDescribe.size()); - std::ranges::copy(idsToDescribe, idsAsTable.getColumn(0).begin()); + ql::ranges::copy(idsToDescribe, idsAsTable.getColumn(0).begin()); return idsAsTable; } diff --git a/src/engine/Distinct.cpp b/src/engine/Distinct.cpp index 2938bc30e6..244e5d5c7c 100644 --- a/src/engine/Distinct.cpp +++ b/src/engine/Distinct.cpp @@ -97,7 +97,7 @@ IdTable Distinct::distinct( 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 + // Variant of `ql::ranges::unique` that allows to skip the begin rows of // elements found in the previous table. auto begin = ql::ranges::find_if(result, [this, &previousRow](const auto& row) { diff --git a/src/engine/LocalVocab.h b/src/engine/LocalVocab.h index e8bd3be550..4dea0e31cc 100644 --- a/src/engine/LocalVocab.h +++ b/src/engine/LocalVocab.h @@ -113,8 +113,8 @@ class LocalVocab { // to this local vocab. The purpose is to keep all the contained // `LocalVocabEntry`s alive as long as this `LocalVocab` is alive. The // primary set of this `LocalVocab` remains unchanged. - template - void mergeWith(const R& vocabs) { + CPP_template(typename R)(requires ql::ranges::range) void mergeWith( + const R& vocabs) { using ql::views::filter; auto addWordSet = [this](const std::shared_ptr& set) { bool added = otherWordSets_.insert(set).second; diff --git a/src/engine/MultiColumnJoin.cpp b/src/engine/MultiColumnJoin.cpp index bb3e4e5995..75852fb69a 100644 --- a/src/engine/MultiColumnJoin.cpp +++ b/src/engine/MultiColumnJoin.cpp @@ -247,7 +247,7 @@ void MultiColumnJoin::computeMultiColumnJoin( // this case we can use a much cheaper algorithm. // TODO There are many other cases where a cheaper implementation can // be chosen, but we leave those for another PR, this is the most common case. - namespace stdr = std::ranges; + namespace stdr = ql::ranges; bool isCheap = stdr::none_of(joinColumns, [&](const auto& jcs) { auto [leftCol, rightCol] = jcs; return (stdr::any_of(right.getColumn(rightCol), &Id::isUndefined)) || diff --git a/src/engine/QueryPlanner.cpp b/src/engine/QueryPlanner.cpp index 60ea4feeb6..2331419209 100644 --- a/src/engine/QueryPlanner.cpp +++ b/src/engine/QueryPlanner.cpp @@ -1342,8 +1342,12 @@ size_t QueryPlanner::countSubgraphs( // Remove duplicate plans from `graph`. auto getId = [](const SubtreePlan* v) { return v->_idsOfIncludedNodes; }; ql::ranges::sort(graph, ql::ranges::less{}, getId); - graph.erase(std::ranges::unique(graph, ql::ranges::equal_to{}, getId).begin(), - graph.end()); + auto uniqueIter = ql::ranges::unique(graph, ql::ranges::equal_to{}, getId); +#ifdef QLEVER_CPP_17 + graph.erase(uniqueIter, graph.end()); +#else + graph.erase(uniqueIter.begin(), graph.end()); +#endif // Qlever currently limits the number of triples etc. per group to be <= 64 // anyway, so we can simply assert here. diff --git a/src/engine/Result.h b/src/engine/Result.h index bc1eaa68b0..c372cf7102 100644 --- a/src/engine/Result.h +++ b/src/engine/Result.h @@ -197,9 +197,11 @@ class Result { const Result& result2); // Overload for more than two `Results` - template - requires std::convertible_to, const Result&> - static SharedLocalVocabWrapper getMergedLocalVocab(R&& subResults) { + CPP_template(typename R)( + requires ql::ranges::forward_range CPP_and + std::convertible_to, + const Result&>) static SharedLocalVocabWrapper + getMergedLocalVocab(R&& subResults) { std::vector vocabs; for (const Result& table : subResults) { vocabs.push_back(&table.localVocab()); diff --git a/src/engine/Service.cpp b/src/engine/Service.cpp index 21338be71b..a2791055c0 100644 --- a/src/engine/Service.cpp +++ b/src/engine/Service.cpp @@ -565,7 +565,7 @@ void Service::precomputeSiblingResult(std::shared_ptr left, auto partialResultGenerator = [](std::vector pairs, Result::LazyResult prevGenerator, - std::ranges::iterator_t it) -> Result::Generator { + ql::ranges::iterator_t it) -> Result::Generator { for (auto& pair : pairs) { co_yield pair; } diff --git a/src/engine/TextIndexScanForEntity.cpp b/src/engine/TextIndexScanForEntity.cpp index 6dbce07ef5..276bd4af75 100644 --- a/src/engine/TextIndexScanForEntity.cpp +++ b/src/engine/TextIndexScanForEntity.cpp @@ -20,10 +20,14 @@ ProtoResult TextIndexScanForEntity::computeResult( word_, getExecutionContext()->getAllocator()); if (hasFixedEntity()) { - auto beginErase = std::ranges::remove_if(idTable, [this](const auto& row) { + auto beginErase = ql::ranges::remove_if(idTable, [this](const auto& row) { return row[1].getVocabIndex() != getVocabIndexOfFixedEntity(); }); +#ifdef QLEVER_CPP_17 + idTable.erase(beginErase, idTable.end()); +#else idTable.erase(beginErase.begin(), idTable.end()); +#endif idTable.setColumnSubset(std::vector{0, 2}); } diff --git a/src/engine/TransitivePathBase.h b/src/engine/TransitivePathBase.h index a223e06d95..71607eed5e 100644 --- a/src/engine/TransitivePathBase.h +++ b/src/engine/TransitivePathBase.h @@ -52,7 +52,7 @@ struct TransitivePathSide { auto [tree, col] = treeAndCol_.value(); const std::vector& sortedOn = tree->getRootOperation()->getResultSortedOn(); - // TODO use std::ranges::starts_with + // TODO use ql::ranges::starts_with return (!sortedOn.empty() && sortedOn[0] == col); } }; diff --git a/src/engine/TransitivePathImpl.h b/src/engine/TransitivePathImpl.h index 3e15141114..6898468595 100644 --- a/src/engine/TransitivePathImpl.h +++ b/src/engine/TransitivePathImpl.h @@ -248,9 +248,9 @@ class TransitivePathImpl : public TransitivePathBase { * LocalVocab is a no-op). * @return Map Maps each Id to its connected Ids in the transitive hull */ - NodeGenerator transitiveHull(const T& edges, LocalVocab edgesVocab, - std::ranges::range auto startNodes, - std::optional target, bool yieldOnce) const { + CPP_template(typename Node)(requires ql::ranges::range) NodeGenerator + transitiveHull(const T& edges, LocalVocab edgesVocab, Node startNodes, + std::optional target, bool yieldOnce) const { ad_utility::Timer timer{ad_utility::Timer::Stopped}; for (auto&& tableColumn : startNodes) { timer.cont(); diff --git a/src/engine/idTable/CompressedExternalIdTable.h b/src/engine/idTable/CompressedExternalIdTable.h index 12bc406e16..970777c52d 100644 --- a/src/engine/idTable/CompressedExternalIdTable.h +++ b/src/engine/idTable/CompressedExternalIdTable.h @@ -714,20 +714,20 @@ class CompressedExternalIdTableSorter for (auto& gen : rowGenerators) { pq.emplace_back(gen.begin(), gen.end()); } - std::ranges::make_heap(pq, comp); + ql::ranges::make_heap(pq, comp); IdTableStatic result(this->writer_.numColumns(), this->writer_.allocator()); result.reserve(blockSizeOutput); size_t numPopped = 0; while (!pq.empty()) { - std::ranges::pop_heap(pq, comp); + ql::ranges::pop_heap(pq, comp); auto& min = pq.back(); result.push_back(*min.first); ++(min.first); if (min.first == min.second) { pq.pop_back(); } else { - std::ranges::push_heap(pq, comp); + ql::ranges::push_heap(pq, comp); } if (result.size() >= blockSizeOutput) { numPopped += result.numRows(); diff --git a/src/engine/idTable/IdTable.h b/src/engine/idTable/IdTable.h index 438b4fc20d..5a046b80f6 100644 --- a/src/engine/idTable/IdTable.h +++ b/src/engine/idTable/IdTable.h @@ -13,6 +13,7 @@ #include #include +#include "backports/algorithm.h" #include "engine/idTable/IdTableRow.h" #include "engine/idTable/VectorWithElementwiseMove.h" #include "global/Id.h" @@ -203,8 +204,8 @@ class IdTable { // fails. Additional columns (if `columns.size() > numColumns`) are deleted. // This behavior is useful for unit tests Where we can just generically pass // in more columns than are needed in any test. - IdTable(size_t numColumns, std::ranges::forward_range auto columns) - requires(!isView) + CPP_template(typename ColT)(requires ql::ranges::forward_range) + IdTable(size_t numColumns, ColT columns) requires(!isView) : data_{std::make_move_iterator(columns.begin()), std::make_move_iterator(columns.end())}, numColumns_{numColumns} { @@ -430,9 +431,11 @@ class IdTable { // otherwise the behavior is undefined (in Release mode) or an assertion will // fail (in Debug mode). The `newRow` can be any random access range that // stores the right type and has the right size. - template - requires std::same_as, T> - void push_back(const RowLike& newRow) requires(!isView) { + CPP_template(typename RowLike)( + requires ql::ranges::random_access_range CPP_and + std::same_as, + T>) void push_back(const RowLike& newRow) + requires(!isView) { AD_EXPENSIVE_CHECK(newRow.size() == numColumns()); ++numRows_; ql::ranges::for_each(ad_utility::integerRange(numColumns()), @@ -442,7 +445,7 @@ class IdTable { } void push_back(const std::initializer_list& newRow) requires(!isView) { - push_back(std::ranges::ref_view{newRow}); + push_back(ql::ranges::ref_view{newRow}); } // True iff we can make a copy (via the `clone` function below), because the diff --git a/src/engine/sparqlExpressions/AggregateExpression.h b/src/engine/sparqlExpressions/AggregateExpression.h index c5f77b67a1..71a0a6b36f 100644 --- a/src/engine/sparqlExpressions/AggregateExpression.h +++ b/src/engine/sparqlExpressions/AggregateExpression.h @@ -28,9 +28,9 @@ inline auto getUniqueElements = []( const EvaluationContext* context, size_t inputSize, OperandGenerator operandGenerator) - -> cppcoro::generator> { + -> cppcoro::generator> { ad_utility::HashSetWithMemoryLimit< - std::ranges::range_value_t> + ql::ranges::range_value_t> uniqueHashSet(inputSize, context->_allocator); for (auto& operand : operandGenerator) { if (uniqueHashSet.insert(operand).second) { diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index 616d432429..f32097547a 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -124,9 +124,9 @@ static auto getSetUnion(const std::vector& blocks1, return b1.blockIndex_ < b2.blockIndex_; }; // Given that we have vectors with sorted (BlockMedata) values, we can - // use std::ranges::set_union. Thus the complexity is O(n + m). - std::ranges::set_union(blocks1, blocks2, std::back_inserter(mergedVectors), - blockLessThanBlock); + // use ql::ranges::set_union. Thus the complexity is O(n + m). + ql::ranges::set_union(blocks1, blocks2, std::back_inserter(mergedVectors), + blockLessThanBlock); mergedVectors.shrink_to_fit(); return mergedVectors; } diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index 603e334c69..a23ce3da95 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -150,7 +150,7 @@ requires AreComparable ExpressionResult evaluateRelationalExpression( auto impl = [&](const auto& value2) -> std::optional { auto columnIndex = context->getColumnIndexForVariable(value1); auto valueId = makeValueId(value2, context); - // TODO Use `std::ranges::starts_with`. + // TODO Use `ql::ranges::starts_with`. if (const auto& cols = context->_columnsByWhichResultIsSorted; !cols.empty() && cols[0] == columnIndex) { constexpr static bool value2IsString = diff --git a/src/engine/sparqlExpressions/SparqlExpressionGenerators.h b/src/engine/sparqlExpressions/SparqlExpressionGenerators.h index 33b46914d0..d7209cacab 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionGenerators.h +++ b/src/engine/sparqlExpressions/SparqlExpressionGenerators.h @@ -50,9 +50,9 @@ cppcoro::generator>> } } -template -requires(std::ranges::input_range) -auto resultGenerator(T&& vector, size_t numItems, Transformation transformation = {}) { +CPP_template(typename T, typename Transformation = std::identity)( + requires ql::ranges::input_range) auto resultGenerator(T&& vector, size_t numItems, + Transformation transformation = {}) { AD_CONTRACT_CHECK(numItems == vector.size()); return ad_utility::allView(AD_FWD(vector)) | ql::views::transform(std::move(transformation)); } @@ -110,7 +110,7 @@ inline auto valueGetterGenerator = []( Function&& function, size_t numItems, Generators... generators) -> cppcoro::generator< - std::invoke_result_t...>> { + std::invoke_result_t...>> { // A tuple holding one iterator to each of the generators. std::tuple iterators{generators.begin()...}; diff --git a/src/global/ValueIdComparators.h b/src/global/ValueIdComparators.h index bfbf7bb551..62338ef4ed 100644 --- a/src/global/ValueIdComparators.h +++ b/src/global/ValueIdComparators.h @@ -361,7 +361,7 @@ inline auto simplifyRanges = return input; } // Merge directly adjacent ranges. - // TODO use `std::ranges` + // TODO use `ql::ranges` decltype(input) result; result.push_back(input.front()); for (auto it = input.begin() + 1; it != input.end(); ++it) { diff --git a/src/index/CompressedRelation.cpp b/src/index/CompressedRelation.cpp index c11081f66f..63c51bd464 100644 --- a/src/index/CompressedRelation.cpp +++ b/src/index/CompressedRelation.cpp @@ -207,9 +207,13 @@ bool CompressedRelationReader::FilterDuplicatesAndGraphs:: }; }; if (needsFilteringByGraph) { - auto [beginOfRemoved, _] = std::ranges::remove_if( + auto removedRange = ql::ranges::remove_if( block, std::not_fn(isDesiredGraphId()), graphIdFromRow); - block.erase(beginOfRemoved, block.end()); +#ifdef QLEVER_CPP_17 + block.erase(removedRange, block.end()); +#else + block.erase(removedRange.begin(), block.end()); +#endif } else { AD_EXPENSIVE_CHECK( !desiredGraphs_.has_value() || @@ -223,10 +227,10 @@ bool CompressedRelationReader::FilterDuplicatesAndGraphs:: filterDuplicatesIfNecessary(IdTable& block, const CompressedBlockMetadata& blockMetadata) { if (!blockMetadata.containsDuplicatesWithDifferentGraphs_) { - AD_EXPENSIVE_CHECK(std::ranges::unique(block).begin() == block.end()); + AD_EXPENSIVE_CHECK(std::unique(block.begin(), block.end()) == block.end()); return false; } - auto [endUnique, _] = std::ranges::unique(block); + auto endUnique = std::unique(block.begin(), block.end()); block.erase(endUnique, block.end()); return true; } @@ -429,7 +433,7 @@ std::vector CompressedRelationReader::getBlocksForJoin( }; // `blockLessThanBlock` (a dummy) and `std::less` are only needed to - // fulfill a concept for the `std::ranges` algorithms. + // fulfill a concept for the `ql::ranges` algorithms. auto blockLessThanBlock = [](const CompressedBlockMetadata&, const CompressedBlockMetadata&) @@ -451,7 +455,8 @@ std::vector CompressedRelationReader::getBlocksForJoin( ql::ranges::copy(relevantBlocks | ql::views::filter(blockIsNeeded), std::back_inserter(result)); // The following check is cheap as there are only few blocks. - AD_CORRECTNESS_CHECK(std::ranges::unique(result).empty()); + AD_CORRECTNESS_CHECK(std::unique(result.begin(), result.end()) == + result.end()); return result; } @@ -511,7 +516,8 @@ CompressedRelationReader::getBlocksForJoin( } } // The following check isn't expensive as there are only few blocks. - AD_CORRECTNESS_CHECK(std::ranges::unique(result).begin() == result.end()); + AD_CORRECTNESS_CHECK(std::unique(result.begin(), result.end()) == + result.end()); return result; }; @@ -998,7 +1004,7 @@ static std::pair>> getGraphInfo( ql::ranges::copy(block->getColumn(ADDITIONAL_COLUMN_GRAPH_ID), std::back_inserter(graphColumn)); ql::ranges::sort(graphColumn); - auto [endOfUnique, _] = std::ranges::unique(graphColumn); + auto endOfUnique = std::unique(graphColumn.begin(), graphColumn.end()); size_t numGraphs = endOfUnique - graphColumn.begin(); if (numGraphs > MAX_NUM_GRAPHS_STORED_IN_BLOCK_METADATA) { return std::nullopt; @@ -1572,9 +1578,10 @@ CompressedRelationReader::getMetadataForSmallRelation( } // The `col1` is sorted, so we compute the multiplicity using - // `std::ranges::unique`. - auto endOfUnique = std::ranges::unique(block.getColumn(0)); - size_t numDistinct = endOfUnique.begin() - block.getColumn(0).begin(); + // `std::unique`. + const auto& blockCol = block.getColumn(0); + auto endOfUnique = std::unique(blockCol.begin(), blockCol.end()); + size_t numDistinct = endOfUnique - blockCol.begin(); metadata.numRows_ = block.size(); metadata.multiplicityCol1_ = CompressedRelationWriter::computeMultiplicity(block.size(), numDistinct); diff --git a/src/index/DeltaTriples.cpp b/src/index/DeltaTriples.cpp index c9b01ac21a..ab02e2e2ed 100644 --- a/src/index/DeltaTriples.cpp +++ b/src/index/DeltaTriples.cpp @@ -151,8 +151,8 @@ void DeltaTriples::modifyTriplesImpl(CancellationHandle cancellationHandle, TriplesToHandlesMap& inverseMap) { rewriteLocalVocabEntriesAndBlankNodes(triples); ql::ranges::sort(triples); - auto [first, last] = std::ranges::unique(triples); - triples.erase(first, last); + auto first = std::unique(triples.begin(), triples.end()); + triples.erase(first, triples.end()); std::erase_if(triples, [&targetMap](const IdTriple<0>& triple) { return targetMap.contains(triple); }); diff --git a/src/index/IndexImpl.Text.cpp b/src/index/IndexImpl.Text.cpp index d1e6a70777..81c135fdea 100644 --- a/src/index/IndexImpl.Text.cpp +++ b/src/index/IndexImpl.Text.cpp @@ -747,7 +747,7 @@ IdTable IndexImpl::readWordCl( idTable.getColumn(1).begin(), [](WordIndex id) { return Id::makeFromWordVocabIndex(WordVocabIndex::make(id)); }); - std::ranges::transform( + ql::ranges::transform( readFreqComprList(tbmd._cl._nofElements, tbmd._cl._startScorelist, static_cast(tbmd._cl._lastByte + 1 - tbmd._cl._startScorelist)), diff --git a/src/index/IndexImpl.cpp b/src/index/IndexImpl.cpp index ed06c49916..72efec5307 100644 --- a/src/index/IndexImpl.cpp +++ b/src/index/IndexImpl.cpp @@ -684,10 +684,11 @@ auto IndexImpl::convertPartialToGlobalIds( for (Buffer::row_reference triple : *triples) { transformTriple(triple, *idMap); } - auto [beginInternal, endInternal] = std::ranges::partition( - *triples, [&isQLeverInternalTriple](const auto& row) { - return !isQLeverInternalTriple(row); - }); + auto beginInternal = + std::partition(triples->begin(), triples->end(), + [&isQLeverInternalTriple](const auto& row) { + return !isQLeverInternalTriple(row); + }); IdTableStatic internalTriples( triples->getAllocator()); // TODO We could leave the partitioned complete block as is, @@ -695,7 +696,7 @@ auto IndexImpl::convertPartialToGlobalIds( // push only a part of a block. We then would safe the copy of the // internal triples here, but I am not sure whether this is worth it. internalTriples.insertAtEnd(*triples, beginInternal - triples->begin(), - endInternal - triples->begin()); + triples->end() - triples->begin()); triples->resize(beginInternal - triples->begin()); Buffers buffers{std::move(*triples), std::move(internalTriples)}; diff --git a/src/index/vocabulary/VocabularyInMemoryBinSearch.cpp b/src/index/vocabulary/VocabularyInMemoryBinSearch.cpp index 268cc33721..95d082ce5a 100644 --- a/src/index/vocabulary/VocabularyInMemoryBinSearch.cpp +++ b/src/index/vocabulary/VocabularyInMemoryBinSearch.cpp @@ -33,7 +33,7 @@ std::optional VocabularyInMemoryBinSearch::operator[]( // _____________________________________________________________________________ WordAndIndex VocabularyInMemoryBinSearch::iteratorToWordAndIndex( - std::ranges::iterator_t it) const { + ql::ranges::iterator_t it) const { if (it == words_.end()) { return WordAndIndex::end(); } diff --git a/src/index/vocabulary/VocabularyInMemoryBinSearch.h b/src/index/vocabulary/VocabularyInMemoryBinSearch.h index 494caeaf66..8367c1e965 100644 --- a/src/index/vocabulary/VocabularyInMemoryBinSearch.h +++ b/src/index/vocabulary/VocabularyInMemoryBinSearch.h @@ -60,7 +60,7 @@ class VocabularyInMemoryBinSearch std::optional operator[](uint64_t index) const; // Convert an iterator to a `WordAndIndex`. Required for the mixin. - WordAndIndex iteratorToWordAndIndex(std::ranges::iterator_t it) const; + WordAndIndex iteratorToWordAndIndex(ql::ranges::iterator_t it) const; // A helper type that can be used to directly write a vocabulary to disk // word-by-word, without having to materialize it in RAM first. diff --git a/src/index/vocabulary/VocabularyInternalExternal.h b/src/index/vocabulary/VocabularyInternalExternal.h index b73066a5f1..f9024369bd 100644 --- a/src/index/vocabulary/VocabularyInternalExternal.h +++ b/src/index/vocabulary/VocabularyInternalExternal.h @@ -123,11 +123,11 @@ class VocabularyInternalExternal { // Convert an iterator (which can be an iterator to the external or internal // vocabulary) into the corresponding index by (logically) subtracting // `begin()`. - uint64_t iteratorToIndex(std::ranges::iterator_t it) const { + uint64_t iteratorToIndex(ql::ranges::iterator_t it) const { return it - externalVocab_.begin(); } uint64_t iteratorToIndex( - std::ranges::iterator_t it) const { + ql::ranges::iterator_t it) const { return internalVocab_.indices().at(it - internalVocab_.begin()); } diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index f23530f820..e22454cfd7 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -311,8 +311,8 @@ ParsedQuery Visitor::visit(Parser::DescribeQueryContext* ctx) { if (describedResources.empty()) { const auto& visibleVariables = parsedQuery_.selectClause().getVisibleVariables(); - std::ranges::copy(visibleVariables, - std::back_inserter(describeClause.resources_)); + ql::ranges::copy(visibleVariables, + std::back_inserter(describeClause.resources_)); describedVariables = visibleVariables; } auto& selectClause = parsedQuery_.selectClause(); diff --git a/src/util/Algorithm.h b/src/util/Algorithm.h index c2229e0e82..50e23f8244 100644 --- a/src/util/Algorithm.h +++ b/src/util/Algorithm.h @@ -129,11 +129,12 @@ std::vector flatten(std::vector>&& input) { // used to keep track of which values we have already seen. One of these // copies could be avoided, but our current uses of this function are // currently not at all performance-critical (small `input` and small `T`). -template -auto removeDuplicates(const Range& input) -> std::vector< - typename std::iterator_traits>::value_type> { +CPP_template(typename Range)(requires ql::ranges::forward_range< + Range>) auto removeDuplicates(const Range& input) + -> std::vector>::value_type> { using T = - typename std::iterator_traits>::value_type; + typename std::iterator_traits>::value_type; std::vector result; ad_utility::HashSet distinctElements; for (const T& element : input) { diff --git a/src/util/BlankNodeManager.h b/src/util/BlankNodeManager.h index 3ff7613768..75e4427c2e 100644 --- a/src/util/BlankNodeManager.h +++ b/src/util/BlankNodeManager.h @@ -90,8 +90,8 @@ class BlankNodeManager { // Merge passed `LocalBlankNodeManager`s to keep alive their reserved // BlankNodeIndex blocks. - template - void mergeWith(const R& localBlankNodeManagers) { + CPP_template(typename R)(requires ql::ranges::range) void mergeWith( + const R& localBlankNodeManagers) { auto inserter = std::back_inserter(otherBlocks_); for (const auto& l : localBlankNodeManagers) { if (l == nullptr) { diff --git a/src/util/ChunkedForLoop.h b/src/util/ChunkedForLoop.h index 3e2dbdc4e8..97d2efff93 100644 --- a/src/util/ChunkedForLoop.h +++ b/src/util/ChunkedForLoop.h @@ -68,22 +68,22 @@ inline void chunkedForLoop(std::size_t start, std::size_t end, // Helper concept that combines the sized range and input range concepts. template concept SizedInputRange = - std::ranges::sized_range && std::ranges::input_range; + ql::ranges::sized_range && ql::ranges::input_range; // Similar to `ql::ranges::copy`, but invokes `chunkOperation` every // `chunkSize` elements. (Round up to the next chunk size if the range size is // not a multiple of `chunkSize`.) template inline void chunkedCopy(R&& inputRange, O result, - std::ranges::range_difference_t chunkSize, + ql::ranges::range_difference_t chunkSize, const std::invocable auto& chunkOperation) - requires std::indirectly_copyable, O> { + requires std::indirectly_copyable, O> { auto begin = ql::ranges::begin(inputRange); auto end = ql::ranges::end(inputRange); auto target = result; while (ql::ranges::distance(begin, end) >= chunkSize) { auto start = begin; - std::ranges::advance(begin, chunkSize); + ql::ranges::advance(begin, chunkSize); target = ql::ranges::copy(start, begin, target).out; chunkOperation(); } @@ -94,20 +94,20 @@ inline void chunkedCopy(R&& inputRange, O result, // Helper concept that combines the sized range and output range concepts. template concept SizedOutputRange = - std::ranges::sized_range && std::ranges::output_range; + ql::ranges::sized_range && ql::ranges::output_range; // Similar to `ql::ranges::fill`, but invokes `chunkOperation` every // `chunkSize` elements. (Round up to the next chunk size if the range size is // not a multiple of `chunkSize`.) template R> inline void chunkedFill(R&& outputRange, const T& value, - std::ranges::range_difference_t chunkSize, + ql::ranges::range_difference_t chunkSize, const std::invocable auto& chunkOperation) { auto begin = ql::ranges::begin(outputRange); auto end = ql::ranges::end(outputRange); while (ql::ranges::distance(begin, end) >= chunkSize) { auto start = begin; - std::ranges::advance(begin, chunkSize); + ql::ranges::advance(begin, chunkSize); ql::ranges::fill(start, begin, value); chunkOperation(); } diff --git a/src/util/Generators.h b/src/util/Generators.h index db232d0188..6e9d9becd2 100644 --- a/src/util/Generators.h +++ b/src/util/Generators.h @@ -20,7 +20,7 @@ namespace ad_utility { // discarded. If the cached value is still present once the generator is fully // consumed, `onFullyCached` is called with the cached value. template > + typename T = ql::ranges::range_value_t> cppcoro::generator wrapGeneratorWithCache( InputRange generator, InvocableWithExactReturnType&, const T&> auto diff --git a/src/util/JoinAlgorithms/JoinAlgorithms.h b/src/util/JoinAlgorithms/JoinAlgorithms.h index 17288c2599..889fed0078 100644 --- a/src/util/JoinAlgorithms/JoinAlgorithms.h +++ b/src/util/JoinAlgorithms/JoinAlgorithms.h @@ -26,14 +26,14 @@ namespace ad_utility { // single argument of the `Range`'s iterator type (NOT value type). template concept UnaryIteratorFunction = - std::invocable>; + std::invocable>; // A function `F` fulfills `BinaryIteratorFunction` if it can be called with // two arguments of the `Range`'s iterator type (NOT value type). template concept BinaryIteratorFunction = - std::invocable, - std::ranges::iterator_t>; + std::invocable, + ql::ranges::iterator_t>; // Note: In the following functions, two rows of IDs are called `compatible` if // for each position they are equal, or at least one of them is UNDEF. This is @@ -81,19 +81,20 @@ concept BinaryIteratorFunction = * described cases leads to two sorted ranges in the output, this can possibly * be exploited to fix the result in a cheaper way than a full sort. */ -template -[[nodiscard]] auto zipperJoinWithUndef( - const Range1& left, const Range2& right, const LessThan& lessThan, - const auto& compatibleRowAction, - const FindSmallerUndefRangesLeft& findSmallerUndefRangesLeft, - const FindSmallerUndefRangesRight& findSmallerUndefRangesRight, - ElFromFirstNotFoundAction elFromFirstNotFoundAction = {}, - CheckCancellation checkCancellation = {}) { +CPP_template(typename Range1, typename Range2, typename LessThan, + typename FindSmallerUndefRangesLeft, + typename FindSmallerUndefRangesRight, + typename ElFromFirstNotFoundAction = decltype(noop), + typename CheckCancellation = decltype(noop))( + requires ql::ranges::random_access_range CPP_and + ql::ranges::random_access_range) + [[nodiscard]] auto zipperJoinWithUndef( + const Range1& left, const Range2& right, const LessThan& lessThan, + const auto& compatibleRowAction, + const FindSmallerUndefRangesLeft& findSmallerUndefRangesLeft, + const FindSmallerUndefRangesRight& findSmallerUndefRangesRight, + ElFromFirstNotFoundAction elFromFirstNotFoundAction = {}, + CheckCancellation checkCancellation = {}) { // If this is not an OPTIONAL join or a MINUS we can apply several // optimizations, so we store this information. static constexpr bool hasNotFoundAction = @@ -320,15 +321,20 @@ template -void gallopingJoin( - const RangeSmaller& smaller, const RangeLarger& larger, - auto const& lessThan, auto const& action, - ElementFromSmallerNotFoundAction elementFromSmallerNotFoundAction = {}, - CheckCancellation checkCancellation = {}) { +CPP_template(typename RangeSmaller, typename RangeLarger, + typename ElementFromSmallerNotFoundAction = Noop, + typename CheckCancellation = Noop)( + requires ql::ranges::random_access_range CPP_and + ql::ranges::random_access_range< + RangeLarger>) void gallopingJoin(const RangeSmaller& smaller, + const RangeLarger& larger, + auto const& lessThan, + auto const& action, + ElementFromSmallerNotFoundAction + elementFromSmallerNotFoundAction = + {}, + CheckCancellation + checkCancellation = {}) { auto itSmall = std::begin(smaller); auto endSmall = std::end(smaller); auto itLarge = std::begin(larger); @@ -672,7 +678,7 @@ struct JoinSide { CurrentBlocks undefBlocks_{}; // Type aliases for a single element from a block from the left/right input. - using value_type = std::ranges::range_value_t>; + using value_type = ql::ranges::range_value_t>; // Type alias for the result of the projection. using ProjectedEl = std::decay_t>; diff --git a/src/util/ParallelMultiwayMerge.h b/src/util/ParallelMultiwayMerge.h index 27db2e4e69..fa17cd57a8 100644 --- a/src/util/ParallelMultiwayMerge.h +++ b/src/util/ParallelMultiwayMerge.h @@ -35,15 +35,15 @@ constexpr auto pushSingleElement = // This concept is fulfilled if `Range` is a range that stores values of type // `T`. template -concept RangeWithValue = std::ranges::range && - std::same_as, T>; +concept RangeWithValue = ql::ranges::range && + std::same_as, T>; // Fulfilled if `Range` is a random access range the elements of which are // ranges of elements of type `T`, e.g. `std::vector>`. template concept RandomAccessRangeOfRanges = - std::ranges::random_access_range && - RangeWithValue, T>; + ql::ranges::random_access_range && + RangeWithValue, T>; // Merge the elements from the presorted ranges `range1` and `range2` according // to the `comparator`. The result of the merging will be yielded in blocks of diff --git a/src/util/StringUtils.h b/src/util/StringUtils.h index 493e7759cc..9a629cc72d 100644 --- a/src/util/StringUtils.h +++ b/src/util/StringUtils.h @@ -105,17 +105,18 @@ streams. @param separator Will be put between each of the string representations of the range elements. */ -template -requires ad_utility::Streamable< - std::iter_reference_t>> -void lazyStrJoin(std::ostream* stream, Range&& r, std::string_view separator); +CPP_template(typename Range)( + requires ql::ranges::input_range CPP_and + ad_utility::Streamable>>) void lazyStrJoin(std::ostream* stream, Range&& r, + std::string_view separator); // Similar to the overload of `lazyStrJoin` above, but the result is returned as // a string. -template -requires ad_utility::Streamable< - std::iter_reference_t>> -std::string lazyStrJoin(Range&& r, std::string_view separator); +CPP_template(typename Range)( + requires ql::ranges::input_range CPP_and ad_utility::Streamable< + std::iter_reference_t>>) std::string + lazyStrJoin(Range&& r, std::string_view separator); /* @brief Adds indentation before the given string and directly after new line @@ -190,10 +191,11 @@ constexpr bool constantTimeEquals(std::string_view view1, } // _________________________________________________________________________ -template -requires ad_utility::Streamable< - std::iter_reference_t>> -void lazyStrJoin(std::ostream* stream, Range&& r, std::string_view separator) { +CPP_template(typename Range)( + requires ql::ranges::input_range CPP_and + ad_utility::Streamable>>) void lazyStrJoin(std::ostream* stream, Range&& r, + std::string_view separator) { auto begin = std::begin(r); auto end = std::end(r); @@ -215,10 +217,10 @@ void lazyStrJoin(std::ostream* stream, Range&& r, std::string_view separator) { } // _________________________________________________________________________ -template -requires ad_utility::Streamable< - std::iter_reference_t>> -std::string lazyStrJoin(Range&& r, std::string_view separator) { +CPP_template(typename Range)( + requires ql::ranges::input_range CPP_and ad_utility::Streamable< + std::iter_reference_t>>) std::string + lazyStrJoin(Range&& r, std::string_view separator) { std::ostringstream stream; lazyStrJoin(&stream, AD_FWD(r), separator); return std::move(stream).str(); diff --git a/test/OperationTest.cpp b/test/OperationTest.cpp index ec9ab35c7f..2afbe38a83 100644 --- a/test/OperationTest.cpp +++ b/test/OperationTest.cpp @@ -24,7 +24,7 @@ using Status = RuntimeInformation::Status; namespace { // Helper function to perform actions at various stages of a generator -template > +template > auto expectAtEachStageOfGenerator( Range generator, std::vector> functions, ad_utility::source_location l = ad_utility::source_location::current()) { From 93c8af20166f50a3c1bb1542be661414f2ea6765 Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Sat, 25 Jan 2025 14:23:47 +0100 Subject: [PATCH 10/28] =?UTF-8?q?In=20the=20greedy=20query=20planner,=20co?= =?UTF-8?q?mpute=20only=20`O(n=C2=B2)`=20trees=20instead=20of=20`O(n=C2=B3?= =?UTF-8?q?)`=20(#1722)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First, a quick recap: For a connected component of `n` triples, the greedy query planner maintains a set of disjoint query execution trees. The initial set are `n` simple query execution trees, each consisting of one index scan. In each step, the cheapest possible join of two trees that are not yet connected, is computed. The worst case is when each pair of triples is connected, so that the induced triple graph is a clique. In the implementation so far, a possible join is computed and its cost estimated (which involved constructing the respective tree) potentially many times, namely in each step until it is picked or no longer possible. Now, each possible join (and the respective query execution tree) is computed and evaluated exactly once and then discarded when it has been picked or is no longer possible. Since each step except the first adds exactly one tree (and throws out two trees, namely those that were joined), only a linear number of new trees have to be constructed in each step. In the code so far, this was a quadratic number in the worst case. This cost significant time for large connected components. NOTE: The cheapest possible join in each step is still computed by a linear scan over all candidates, which in the worst case is a quadratic number in the first half of the steps. This could be improved by using a priority queues and other tricks. However, we would need to evaluate first whether these minimum computations actually cost significant time. In the code so far, the expensive part was the construction of the trees. --- src/engine/QueryPlanner.cpp | 106 ++++++++++++++++++++++++++---------- src/engine/QueryPlanner.h | 8 ++- 2 files changed, 83 insertions(+), 31 deletions(-) diff --git a/src/engine/QueryPlanner.cpp b/src/engine/QueryPlanner.cpp index 2331419209..43caa71f02 100644 --- a/src/engine/QueryPlanner.cpp +++ b/src/engine/QueryPlanner.cpp @@ -246,7 +246,9 @@ std::vector QueryPlanner::optimize( // (it might be, that the last join was optional and introduced new variables) if (!candidatePlans.empty()) { applyFiltersIfPossible(candidatePlans[0], rootPattern->_filters); - applyTextLimitsIfPossible(candidatePlans[0], rootPattern->textLimits_, + applyTextLimitsIfPossible(candidatePlans[0], + TextLimitVec{rootPattern->textLimits_.begin(), + rootPattern->textLimits_.end()}, true); checkCancellation(); } @@ -1232,7 +1234,7 @@ void QueryPlanner::applyFiltersIfPossible( // _____________________________________________________________________________ void QueryPlanner::applyTextLimitsIfPossible( - vector& row, const TextLimitMap& textLimits, + vector& row, const TextLimitVec& textLimits, bool replace) const { // Apply text limits if possible. // A text limit can be applied to a plan if: @@ -1307,7 +1309,7 @@ size_t QueryPlanner::findUniqueNodeIds( std::vector QueryPlanner::runDynamicProgrammingOnConnectedComponent( std::vector connectedComponent, - const vector& filters, const TextLimitMap& textLimits, + const vector& filters, const TextLimitVec& textLimits, const TripleGraph& tg) const { vector> dpTab; // find the unique number of nodes in the current connected component @@ -1333,7 +1335,10 @@ QueryPlanner::runDynamicProgrammingOnConnectedComponent( // be nonempty. AD_CORRECTNESS_CHECK(!dpTab[k - 1].empty()); } - return std::move(dpTab.back()); + auto& result = dpTab.back(); + applyFiltersIfPossible(result, filters); + applyTextLimitsIfPossible(result, textLimits, true); + return std::move(result); } // _____________________________________________________________________________ @@ -1377,32 +1382,75 @@ size_t QueryPlanner::countSubgraphs( std::vector QueryPlanner::runGreedyPlanningOnConnectedComponent( std::vector connectedComponent, - const vector& filters, const TextLimitMap& textLimits, + const vector& filters, const TextLimitVec& textLimits, const TripleGraph& tg) const { - auto& result = connectedComponent; - applyFiltersIfPossible(result, filters); - applyTextLimitsIfPossible(result, textLimits, true); - size_t numSeeds = findUniqueNodeIds(result); - - while (numSeeds > 1) { + applyFiltersIfPossible(connectedComponent, filters); + applyTextLimitsIfPossible(connectedComponent, textLimits, true); + const size_t numSeeds = findUniqueNodeIds(connectedComponent); + if (numSeeds <= 1) { + // Only 0 or 1 nodes in the input, nothing to plan. + return connectedComponent; + } + + // Intermediate variables that will be filled by the `greedyStep` lambda + // below. + using Plans = std::vector; + + // Perform a single step of greedy query planning. + // `nextBestPlan` contains the result of the last step of greedy query + // planning. `currentPlans` contains all plans that have been chosen/combined + // so far (which might still be the initial start plans), except for the most + // recently chosen plan, which is stored in `nextResult`. `cache` contains all + // the plans that can be obtained by combining two plans in `input`. The + // function then performs one additional step of greedy planning and + // reinforces the above pre-/postconditions. Exception: if `isFirstStep` then + // `cache` and `nextResult` must be empty, and the first step of greedy + // planning is performed, which also establishes the pre-/postconditions. + auto greedyStep = [this, &tg, &filters, &textLimits, + currentPlans = std::move(connectedComponent), + cache = Plans{}](Plans& nextBestPlan, + bool isFirstStep) mutable { checkCancellation(); - auto newPlans = merge(result, result, tg); + // Normally, we already have all combinations of two nodes in `currentPlans` + // in the cache, so we only have to add the combinations between + // `currentPlans` and `nextResult`. In the first step, we need to initially + // compute all possible combinations. + auto newPlans = isFirstStep ? merge(currentPlans, currentPlans, tg) + : merge(currentPlans, nextBestPlan, tg); applyFiltersIfPossible(newPlans, filters); applyTextLimitsIfPossible(newPlans, textLimits, true); - auto smallestIdx = findSmallestExecutionTree(newPlans); - auto& cheapestNewTree = newPlans.at(smallestIdx); - size_t oldSize = result.size(); - std::erase_if(result, [&cheapestNewTree](const auto& plan) { - // TODO We can also assert some other invariants here. - return (cheapestNewTree._idsOfIncludedNodes & plan._idsOfIncludedNodes) != - 0; - }); - result.push_back(std::move(cheapestNewTree)); - AD_CORRECTNESS_CHECK(result.size() < oldSize); - numSeeds--; + AD_CORRECTNESS_CHECK(!newPlans.empty()); + ql::ranges::move(newPlans, std::back_inserter(cache)); + ql::ranges::move(nextBestPlan, std::back_inserter(currentPlans)); + + // All candidates for the next greedy step are in the `cache`, choose the + // cheapest one, remove it from the cache and make it the `nextResult` + { + auto smallestIdxNew = findSmallestExecutionTree(cache); + auto& cheapestNewTree = cache.at(smallestIdxNew); + std::swap(cheapestNewTree, cache.back()); + nextBestPlan.clear(); + nextBestPlan.push_back(std::move(cache.back())); + cache.pop_back(); + } + + // All plans which have a node in common with the chosen plan have to be + // deleted from the `currentPlans` and therefore also from the `cache`. + auto shouldBeErased = [&nextTree = nextBestPlan.front()](const auto& plan) { + return (nextTree._idsOfIncludedNodes & plan._idsOfIncludedNodes) != 0; + }; + std::erase_if(currentPlans, shouldBeErased); + std::erase_if(cache, shouldBeErased); + }; + + bool first = true; + Plans result; + for ([[maybe_unused]] size_t i : ad_utility::integerRange(numSeeds - 1)) { + greedyStep(result, first); + first = false; } // TODO Assert that all seeds are covered by the result. - return std::move(result); + return result; } // _____________________________________________________________________________ @@ -1422,6 +1470,7 @@ vector> QueryPlanner::fillDpTab( components[componentIndices.at(i)].push_back(std::move(initialPlans.at(i))); } vector> lastDpRowFromComponents; + TextLimitVec textLimitVec(textLimits.begin(), textLimits.end()); for (auto& component : components | ql::views::values) { std::vector g; for (const auto& plan : component) { @@ -1437,8 +1486,8 @@ vector> QueryPlanner::fillDpTab( auto impl = useGreedyPlanning ? &QueryPlanner::runGreedyPlanningOnConnectedComponent : &QueryPlanner::runDynamicProgrammingOnConnectedComponent; - lastDpRowFromComponents.push_back( - std::invoke(impl, this, std::move(component), filters, textLimits, tg)); + lastDpRowFromComponents.push_back(std::invoke( + impl, this, std::move(component), filters, textLimitVec, tg)); checkCancellation(); } size_t numConnectedComponents = lastDpRowFromComponents.size(); @@ -1451,7 +1500,8 @@ vector> QueryPlanner::fillDpTab( if (numConnectedComponents == 1) { // A Cartesian product is not needed if there is only one component. applyFiltersIfPossible(lastDpRowFromComponents.back(), filters); - applyTextLimitsIfPossible(lastDpRowFromComponents.back(), textLimits, true); + applyTextLimitsIfPossible(lastDpRowFromComponents.back(), textLimitVec, + true); return lastDpRowFromComponents; } // More than one connected component, set up a Cartesian product. @@ -1482,7 +1532,7 @@ vector> QueryPlanner::fillDpTab( plan._idsOfIncludedFilters = filterIds; plan.idsOfIncludedTextLimits_ = textLimitIds; applyFiltersIfPossible(result.at(0), filters); - applyTextLimitsIfPossible(result.at(0), textLimits, true); + applyTextLimitsIfPossible(result.at(0), textLimitVec, true); return result; } diff --git a/src/engine/QueryPlanner.h b/src/engine/QueryPlanner.h index 201ad7e3c5..b51523baed 100644 --- a/src/engine/QueryPlanner.h +++ b/src/engine/QueryPlanner.h @@ -18,6 +18,8 @@ class QueryPlanner { using TextLimitMap = ad_utility::HashMap; + using TextLimitVec = + std::vector>; using CancellationHandle = ad_utility::SharedCancellationHandle; template using vector = std::vector; @@ -401,7 +403,7 @@ class QueryPlanner { // 1) There is no text operation for the text record column left. // 2) The text limit has not already been applied to the plan. void applyTextLimitsIfPossible(std::vector& row, - const TextLimitMap& textLimits, + const TextLimitVec& textLimits, bool replaceInsteadOfAddPlans) const; /** @@ -471,7 +473,7 @@ class QueryPlanner { std::vector runDynamicProgrammingOnConnectedComponent( std::vector connectedComponent, - const vector& filters, const TextLimitMap& textLimits, + const vector& filters, const TextLimitVec& textLimits, const TripleGraph& tg) const; // Same as `runDynamicProgrammingOnConnectedComponent`, but uses a greedy @@ -479,7 +481,7 @@ class QueryPlanner { // join operations using the "Greedy Operator Ordering (GOO)" algorithm. std::vector runGreedyPlanningOnConnectedComponent( std::vector connectedComponent, - const vector& filters, const TextLimitMap& textLimits, + const vector& filters, const TextLimitVec& textLimits, const TripleGraph& tg) const; // Return the number of connected subgraphs is the `graph`, or `budget + 1`, From 50e51084511fc1d90dc6e10f44755eb23af6b729 Mon Sep 17 00:00:00 2001 From: Julian <14220769+Qup42@users.noreply.github.com> Date: Mon, 27 Jan 2025 16:26:04 +0100 Subject: [PATCH 11/28] Add datasets from parameters for Query and Update (#1714) The default graphs and named graphs for a query or update can now also be specified as parameters to the HTTP GET/POST request (in addition to specifying them via `FROM [NAMED]` clauses, which was also supported previously. In particular, the parameters `default-graph-uri` and `named-graph-uri` are now supported by queries, and the parameters `using-graph-uri` and `using-named-graph-uri` are supported by updates. Note that these parameters are part of the standardized SPARQL protocol. Fixes #1712 --- src/engine/Server.cpp | 141 +++++++++++++++++++++--------------- src/engine/Server.h | 20 ++--- src/util/http/UrlParser.cpp | 20 ++--- src/util/http/UrlParser.h | 9 ++- test/ServerTest.cpp | 100 ++++++++++++++++++++++--- test/UrlParserTest.cpp | 34 +++++---- 6 files changed, 213 insertions(+), 111 deletions(-) diff --git a/src/engine/Server.cpp b/src/engine/Server.cpp index 1128cd5b22..8c1248203c 100644 --- a/src/engine/Server.cpp +++ b/src/engine/Server.cpp @@ -21,10 +21,13 @@ #include "util/MemorySize/MemorySize.h" #include "util/OnDestructionDontThrowDuringStackUnwinding.h" #include "util/ParseableDuration.h" +#include "util/TypeIdentity.h" +#include "util/TypeTraits.h" #include "util/http/HttpUtils.h" #include "util/http/websocket/MessageSender.h" using namespace std::string_literals; +using namespace ad_utility::url_parser::sparqlOperation; template using Awaitable = Server::Awaitable; @@ -169,9 +172,9 @@ void Server::run(const string& indexBaseName, bool useText, bool usePatterns, // _____________________________________________________________________________ ad_utility::url_parser::ParsedRequest Server::parseHttpRequest( const ad_utility::httpUtils::HttpRequest auto& request) { + using namespace ad_utility::use_type_identity; // For an HTTP request, `request.target()` yields the HTTP Request-URI. // This is a concatenation of the URL path and the query strings. - using namespace ad_utility::url_parser::sparqlOperation; auto parsedUrl = ad_utility::url_parser::parseRequestTarget(request.target()); ad_utility::url_parser::ParsedRequest parsedRequest{ std::move(parsedUrl.path_), std::move(parsedUrl.parameters_), None{}}; @@ -179,18 +182,37 @@ ad_utility::url_parser::ParsedRequest Server::parseHttpRequest( // Some valid requests (e.g. QLever's custom commands like retrieving index // statistics) don't have a query. So an empty operation is not necessarily an // error. - auto setOperationIfSpecifiedInParams = - [&parsedRequest](string_view paramName) { - auto operation = ad_utility::url_parser::getParameterCheckAtMostOnce( - parsedRequest.parameters_, paramName); - if (operation.has_value()) { - parsedRequest.operation_ = Operation{operation.value()}; - parsedRequest.parameters_.erase(paramName); - } - }; + auto setOperationIfSpecifiedInParams = [&parsedRequest]( + TI, + string_view paramName) { + auto operation = ad_utility::url_parser::getParameterCheckAtMostOnce( + parsedRequest.parameters_, paramName); + if (operation.has_value()) { + parsedRequest.operation_ = Operation{operation.value(), {}}; + parsedRequest.parameters_.erase(paramName); + } + }; + auto addToDatasetClausesIfOperationIs = [&parsedRequest]( + TI, + const std::string& key, + bool isNamed) { + if (Operation* op = std::get_if(&parsedRequest.operation_)) { + ad_utility::appendVector(op->datasetClauses_, + ad_utility::url_parser::parseDatasetClausesFrom( + parsedRequest.parameters_, key, isNamed)); + } + }; + auto addDatasetClauses = [&addToDatasetClausesIfOperationIs] { + addToDatasetClausesIfOperationIs(ti, "default-graph-uri", false); + addToDatasetClausesIfOperationIs(ti, "named-graph-uri", true); + addToDatasetClausesIfOperationIs(ti, "using-graph-uri", false); + addToDatasetClausesIfOperationIs(ti, "using-named-graph-uri", true); + }; if (request.method() == http::verb::get) { - setOperationIfSpecifiedInParams.template operator()("query"); + setOperationIfSpecifiedInParams(ti, "query"); + addDatasetClauses(); + if (parsedRequest.parameters_.contains("update")) { throw std::runtime_error("SPARQL Update is not allowed as GET request."); } @@ -258,17 +280,19 @@ ad_utility::url_parser::ParsedRequest Server::parseHttpRequest( throw std::runtime_error( R"(Request must only contain one of "query" and "update".)"); } - setOperationIfSpecifiedInParams.template operator()("query"); - setOperationIfSpecifiedInParams.template operator()("update"); - + setOperationIfSpecifiedInParams(ti, "query"); + setOperationIfSpecifiedInParams(ti, "update"); + addDatasetClauses(); return parsedRequest; } if (contentType.starts_with(contentTypeSparqlQuery)) { - parsedRequest.operation_ = Query{request.body()}; + parsedRequest.operation_ = Query{request.body(), {}}; + addDatasetClauses(); return parsedRequest; } if (contentType.starts_with(contentTypeSparqlUpdate)) { - parsedRequest.operation_ = Update{request.body()}; + parsedRequest.operation_ = Update{request.body(), {}}; + addDatasetClauses(); return parsedRequest; } throw std::runtime_error(absl::StrCat( @@ -337,16 +361,6 @@ Awaitable Server::process( const auto parsedHttpRequest = parseHttpRequest(request); const auto& parameters = parsedHttpRequest.parameters_; - auto checkParameterNotPresent = [¶meters]( - const std::string& parameterName) { - if (parameters.contains(parameterName)) { - throw NotSupportedException(absl::StrCat( - parameterName, " parameter is currently not supported by QLever.")); - } - }; - checkParameterNotPresent("default-graph-uri"); - checkParameterNotPresent("named-graph-uri"); - // We always want to call `Server::checkParameter` with the same first // parameter. auto checkParameter = std::bind_front(&ad_utility::url_parser::checkParameter, @@ -476,14 +490,13 @@ Awaitable Server::process( auto visitQuery = [&checkParameter, &accessTokenOk, &request, &send, ¶meters, &requestTimer, - this](ad_utility::url_parser::sparqlOperation::Query query) - -> Awaitable { + this](Query query) -> Awaitable { if (auto timeLimit = co_await verifyUserSubmittedQueryTimeout( checkParameter("timeout", std::nullopt), accessTokenOk, request, send)) { - co_return co_await processQueryOrUpdate( - parameters, query.query_, requestTimer, std::move(request), send, - timeLimit.value()); + co_return co_await processQueryOrUpdate(parameters, query, requestTimer, + std::move(request), send, + timeLimit.value()); } else { // If the optional is empty, this indicates an error response has been // sent to the client already. We can stop here. @@ -492,25 +505,22 @@ Awaitable Server::process( }; auto visitUpdate = [&checkParameter, &accessTokenOk, &request, &send, ¶meters, - &requestTimer, this, &requireValidAccessToken]( - const ad_utility::url_parser::sparqlOperation::Update& update) - -> Awaitable { + &requestTimer, this, + &requireValidAccessToken](const Update& update) -> Awaitable { requireValidAccessToken("SPARQL Update"); if (auto timeLimit = co_await verifyUserSubmittedQueryTimeout( checkParameter("timeout", std::nullopt), accessTokenOk, request, send)) { - co_return co_await processQueryOrUpdate( - parameters, update.update_, requestTimer, std::move(request), send, - timeLimit.value()); + co_return co_await processQueryOrUpdate(parameters, update, requestTimer, + std::move(request), send, + timeLimit.value()); } else { // If the optional is empty, this indicates an error response has been // sent to the client already. We can stop here. co_return; } }; - auto visitNone = - [&response, &send, &request]( - ad_utility::url_parser::sparqlOperation::None) -> Awaitable { + auto visitNone = [&response, &send, &request](None) -> Awaitable { // If there was no "query", but any of the URL parameters processed before // produced a `response`, send that now. Note that if multiple URL // parameters were processed, only the `response` from the last one is sent. @@ -549,11 +559,10 @@ std::pair Server::determineResultPinning( // ____________________________________________________________________________ Server::PlannedQuery Server::setupPlannedQuery( - const ad_utility::url_parser::ParamValueMap& params, + const std::vector& queryDatasets, const std::string& operation, QueryExecutionContext& qec, SharedCancellationHandle handle, TimeLimit timeLimit, const ad_utility::Timer& requestTimer) const { - auto queryDatasets = ad_utility::url_parser::parseDatasetClauses(params); PlannedQuery plannedQuery = parseAndPlan(operation, queryDatasets, qec, handle, timeLimit); auto& qet = plannedQuery.queryExecutionTree_; @@ -784,7 +793,7 @@ ad_utility::websocket::MessageSender Server::createMessageSender( // ____________________________________________________________________________ Awaitable Server::processQuery( - const ad_utility::url_parser::ParamValueMap& params, const string& query, + const ad_utility::url_parser::ParamValueMap& params, const Query& query, ad_utility::Timer& requestTimer, const ad_utility::httpUtils::HttpRequest auto& request, auto&& send, TimeLimit timeLimit) { @@ -793,7 +802,7 @@ Awaitable Server::processQuery( << ad_utility::toString(mediaType) << "\"" << std::endl; ad_utility::websocket::MessageSender messageSender = - createMessageSender(queryHub_, request, query); + createMessageSender(queryHub_, request, query.query_); auto [cancellationHandle, cancelTimeoutOnDestruction] = setupCancellationHandle(messageSender.getQueryId(), timeLimit); @@ -803,7 +812,7 @@ Awaitable Server::processQuery( LOG(INFO) << "Processing the following SPARQL query:" << (pinResult ? " [pin result]" : "") << (pinSubtrees ? " [pin subresults]" : "") << "\n" - << query << std::endl; + << query.query_ << std::endl; QueryExecutionContext qec(index_, &cache_, allocator_, sortPerformanceEstimator_, std::ref(messageSender), pinSubtrees, pinResult); @@ -817,10 +826,10 @@ Awaitable Server::processQuery( // an explicit variable instead of directly `co_await`-ing it. auto coroutine = computeInNewThread( queryThreadPool_, - [this, ¶ms, &query, &qec, cancellationHandle, &timeLimit, + [this, &query, &qec, cancellationHandle, &timeLimit, &requestTimer]() -> std::optional { - return setupPlannedQuery(params, query, qec, cancellationHandle, - timeLimit, requestTimer); + return setupPlannedQuery(query.datasetClauses_, query.query_, qec, + cancellationHandle, timeLimit, requestTimer); }, cancellationHandle); auto plannedQueryOpt = co_await std::move(coroutine); @@ -938,7 +947,7 @@ json Server::createResponseMetadataForUpdate( } // ____________________________________________________________________________ json Server::processUpdateImpl( - const ad_utility::url_parser::ParamValueMap& params, const string& update, + const ad_utility::url_parser::ParamValueMap& params, const Update& update, ad_utility::Timer& requestTimer, TimeLimit timeLimit, auto& messageSender, ad_utility::SharedCancellationHandle cancellationHandle, DeltaTriples& deltaTriples) { @@ -946,12 +955,13 @@ json Server::processUpdateImpl( LOG(INFO) << "Processing the following SPARQL update:" << (pinResult ? " [pin result]" : "") << (pinSubtrees ? " [pin subresults]" : "") << "\n" - << update << std::endl; + << update.update_ << std::endl; QueryExecutionContext qec(index_, &cache_, allocator_, sortPerformanceEstimator_, std::ref(messageSender), pinSubtrees, pinResult); - auto plannedQuery = setupPlannedQuery(params, update, qec, cancellationHandle, - timeLimit, requestTimer); + auto plannedQuery = + setupPlannedQuery(update.datasetClauses_, update.update_, qec, + cancellationHandle, timeLimit, requestTimer); auto qet = plannedQuery.queryExecutionTree_; if (!plannedQuery.parsedQuery_.hasUpdateClause()) { @@ -984,11 +994,11 @@ json Server::processUpdateImpl( // ____________________________________________________________________________ Awaitable Server::processUpdate( - const ad_utility::url_parser::ParamValueMap& params, const string& update, + const ad_utility::url_parser::ParamValueMap& params, const Update& update, ad_utility::Timer& requestTimer, const ad_utility::httpUtils::HttpRequest auto& request, auto&& send, TimeLimit timeLimit) { - auto messageSender = createMessageSender(queryHub_, request, update); + auto messageSender = createMessageSender(queryHub_, request, update.update_); auto [cancellationHandle, cancelTimeoutOnDestruction] = setupCancellationHandle(messageSender.getQueryId(), timeLimit); @@ -1021,14 +1031,16 @@ Awaitable Server::processUpdate( } // ____________________________________________________________________________ -template +template Awaitable Server::processQueryOrUpdate( const ad_utility::url_parser::ParamValueMap& params, - const string& queryOrUpdate, ad_utility::Timer& requestTimer, + const Operation& operation, ad_utility::Timer& requestTimer, const ad_utility::httpUtils::HttpRequest auto& request, auto&& send, TimeLimit timeLimit) { using namespace ad_utility::httpUtils; + static_assert(ad_utility::SameAsAny); + http::status responseStatus = http::status::ok; // Put the whole query processing in a try-catch block. If any exception @@ -1041,11 +1053,12 @@ Awaitable Server::processQueryOrUpdate( // access to the runtimeInformation in the case of an error. std::optional plannedQuery; try { - if constexpr (type == OperationType::Query) { - co_await processQuery(params, queryOrUpdate, requestTimer, request, send, + if constexpr (std::is_same_v) { + co_await processQuery(params, operation, requestTimer, request, send, timeLimit); } else { - co_await processUpdate(params, queryOrUpdate, requestTimer, request, send, + static_assert(std::is_same_v); + co_await processUpdate(params, operation, requestTimer, request, send, timeLimit); } } catch (const ParseException& e) { @@ -1086,8 +1099,16 @@ Awaitable Server::processQueryOrUpdate( LOG(ERROR) << metadata.value().query_ << std::endl; } } + const std::string& operationStr = [&operation]() -> const std::string& { + if constexpr (std::is_same_v) { + return operation.query_; + } else { + static_assert(std::is_same_v); + return operation.update_; + } + }(); auto errorResponseJson = composeErrorResponseJson( - queryOrUpdate, exceptionErrorMsg.value(), requestTimer, metadata); + operationStr, exceptionErrorMsg.value(), requestTimer, metadata); if (plannedQuery.has_value()) { errorResponseJson["runtimeInformation"] = nlohmann::ordered_json(plannedQuery.value() diff --git a/src/engine/Server.h b/src/engine/Server.h index 566a313b5a..4e0889b48a 100644 --- a/src/engine/Server.h +++ b/src/engine/Server.h @@ -124,14 +124,11 @@ class Server { Awaitable process( const ad_utility::httpUtils::HttpRequest auto& request, auto&& send); - // Indicates which type of operation is being processed. - enum class OperationType { Query, Update }; - /// Handle a http request that asks for the processing of an query or update. /// This is only a wrapper for `processQuery` and `processUpdate` which /// does the error handling. /// \param params The key-value-pairs sent in the HTTP GET request. - /// \param queryOrUpdate The query or update. + /// \param operation Must be Query or Update. /// \param requestTimer Timer that measure the total processing /// time of this request. /// \param request The HTTP request. @@ -139,15 +136,16 @@ class Server { /// `HttpServer.h` for documentation). /// \param timeLimit Duration in seconds after which the query will be /// cancelled. - template + template Awaitable processQueryOrUpdate( const ad_utility::url_parser::ParamValueMap& params, - const string& queryOrUpdate, ad_utility::Timer& requestTimer, + const Operation& operation, ad_utility::Timer& requestTimer, const ad_utility::httpUtils::HttpRequest auto& request, auto&& send, TimeLimit timeLimit); // Do the actual execution of a query. Awaitable processQuery( - const ad_utility::url_parser::ParamValueMap& params, const string& query, + const ad_utility::url_parser::ParamValueMap& params, + const ad_utility::url_parser::sparqlOperation::Query& query, ad_utility::Timer& requestTimer, const ad_utility::httpUtils::HttpRequest auto& request, auto&& send, TimeLimit timeLimit); @@ -162,7 +160,8 @@ class Server { FRIEND_TEST(ServerTest, createResponseMetadata); // Do the actual execution of an update. Awaitable processUpdate( - const ad_utility::url_parser::ParamValueMap& params, const string& update, + const ad_utility::url_parser::ParamValueMap& params, + const ad_utility::url_parser::sparqlOperation::Update& update, ad_utility::Timer& requestTimer, const ad_utility::httpUtils::HttpRequest auto& request, auto&& send, TimeLimit timeLimit); @@ -180,7 +179,7 @@ class Server { FRIEND_TEST(ServerTest, determineResultPinning); // Sets up the PlannedQuery s.t. it is ready to be executed. PlannedQuery setupPlannedQuery( - const ad_utility::url_parser::ParamValueMap& params, + const std::vector& queryDatasets, const std::string& operation, QueryExecutionContext& qec, SharedCancellationHandle handle, TimeLimit timeLimit, const ad_utility::Timer& requestTimer) const; @@ -192,7 +191,8 @@ class Server { // Execute an update operation. The function must have exclusive access to the // DeltaTriples object. json processUpdateImpl( - const ad_utility::url_parser::ParamValueMap& params, const string& update, + const ad_utility::url_parser::ParamValueMap& params, + const ad_utility::url_parser::sparqlOperation::Update& update, ad_utility::Timer& requestTimer, TimeLimit timeLimit, auto& messageSender, ad_utility::SharedCancellationHandle cancellationHandle, DeltaTriples& deltaTriples); diff --git a/src/util/http/UrlParser.cpp b/src/util/http/UrlParser.cpp index 1670fa937d..0f37415ba4 100644 --- a/src/util/http/UrlParser.cpp +++ b/src/util/http/UrlParser.cpp @@ -66,20 +66,14 @@ ParamValueMap ad_utility::url_parser::paramsToMap( } // _____________________________________________________________________________ -std::vector ad_utility::url_parser::parseDatasetClauses( - const ParamValueMap& params) { +std::vector ad_utility::url_parser::parseDatasetClausesFrom( + const ParamValueMap& params, const std::string& key, bool isNamed) { std::vector datasetClauses; - auto readDatasetClauses = [¶ms, &datasetClauses](const std::string& key, - bool isNamed) { - if (params.contains(key)) { - for (const auto& uri : params.at(key)) { - datasetClauses.emplace_back( - ad_utility::triple_component::Iri::fromIrirefWithoutBrackets(uri), - isNamed); - } + if (params.contains(key)) { + for (const auto& uri : params.at(key)) { + datasetClauses.emplace_back( + triple_component::Iri::fromIrirefWithoutBrackets(uri), isNamed); } - }; - readDatasetClauses("default-graph-uri", false); - readDatasetClauses("named-graph-uri", true); + } return datasetClauses; } diff --git a/src/util/http/UrlParser.h b/src/util/http/UrlParser.h index 47edd8404f..8c1d272aac 100644 --- a/src/util/http/UrlParser.h +++ b/src/util/http/UrlParser.h @@ -49,6 +49,7 @@ namespace sparqlOperation { // A SPARQL 1.1 Query struct Query { std::string query_; + std::vector datasetClauses_; bool operator==(const Query& rhs) const = default; }; @@ -56,6 +57,7 @@ struct Query { // A SPARQL 1.1 Update struct Update { std::string update_; + std::vector datasetClauses_; bool operator==(const Update& rhs) const = default; }; @@ -86,8 +88,11 @@ ParsedUrl parseRequestTarget(std::string_view target); // string to vectors of strings). ParamValueMap paramsToMap(boost::urls::params_view params); -// Parse default and named graphs URIs from the parameters. -std::vector parseDatasetClauses(const ParamValueMap& params); +// Parse the dataset clauses from the given key in the parameters. +std::vector parseDatasetClausesFrom(const ParamValueMap& params, + const std::string& key, + bool isNamed); + } // namespace ad_utility::url_parser #endif // QLEVER_URLPARSER_H diff --git a/test/ServerTest.cpp b/test/ServerTest.cpp index cd5c9413da..dd7d776da7 100644 --- a/test/ServerTest.cpp +++ b/test/ServerTest.cpp @@ -49,11 +49,12 @@ TEST(ServerTest, parseHttpRequest) { EXPECT_THAT(parse(makeGetRequest( "/?query=SELECT+%2A%20WHERE%20%7B%7D&action=csv_export")), ParsedRequestIs("/", {{"action", {"csv_export"}}}, - Query{"SELECT * WHERE {}"})); + Query{"SELECT * WHERE {}", {}})); EXPECT_THAT( parse(makePostRequest("/", URLENCODED, "query=SELECT+%2A%20WHERE%20%7B%7D&send=100")), - ParsedRequestIs("/", {{"send", {"100"}}}, Query{"SELECT * WHERE {}"})); + ParsedRequestIs("/", {{"send", {"100"}}}, + Query{"SELECT * WHERE {}", {}})); AD_EXPECT_THROW_WITH_MESSAGE( parse(makePostRequest("/", URLENCODED, "ääär y=SELECT+%2A%20WHERE%20%7B%7D&send=100")), @@ -78,10 +79,12 @@ TEST(ServerTest, parseHttpRequest) { EXPECT_THAT( parse(makePostRequest("/", "application/x-www-form-urlencoded", "query=SELECT%20%2A%20WHERE%20%7B%7D&send=100")), - ParsedRequestIs("/", {{"send", {"100"}}}, Query{"SELECT * WHERE {}"})); + ParsedRequestIs("/", {{"send", {"100"}}}, + Query{"SELECT * WHERE {}", {}})); EXPECT_THAT(parse(makePostRequest("/", URLENCODED, "query=SELECT%20%2A%20WHERE%20%7B%7D")), - ParsedRequestIs("/", {}, Query{"SELECT * WHERE {}"})); + ParsedRequestIs("/", {}, Query{"SELECT * WHERE {}", {}})); + auto Iri = ad_utility::triple_component::Iri::fromIriref; EXPECT_THAT( parse(makePostRequest( "/", URLENCODED, @@ -92,7 +95,11 @@ TEST(ServerTest, parseHttpRequest) { "/", {{"default-graph-uri", {"https://w3.org/default"}}, {"named-graph-uri", {"https://w3.org/1", "https://w3.org/2"}}}, - Query{"SELECT * WHERE {}"})); + Query{"SELECT * WHERE {}", + {DatasetClause{Iri(""), false}, + DatasetClause{Iri(""), true}, + DatasetClause{Iri(""), true}}})); + ; AD_EXPECT_THROW_WITH_MESSAGE( parse(makePostRequest("/?send=100", URLENCODED, "query=SELECT%20%2A%20WHERE%20%7B%7D")), @@ -101,10 +108,10 @@ TEST(ServerTest, parseHttpRequest) { EXPECT_THAT(parse(makePostRequest("/", URLENCODED, "cmd=clear-cache")), ParsedRequestIs("/", {{"cmd", {"clear-cache"}}}, None{})); EXPECT_THAT(parse(makePostRequest("/", QUERY, "SELECT * WHERE {}")), - ParsedRequestIs("/", {}, Query{"SELECT * WHERE {}"})); - EXPECT_THAT( - parse(makePostRequest("/?send=100", QUERY, "SELECT * WHERE {}")), - ParsedRequestIs("/", {{"send", {"100"}}}, Query{"SELECT * WHERE {}"})); + ParsedRequestIs("/", {}, Query{"SELECT * WHERE {}", {}})); + EXPECT_THAT(parse(makePostRequest("/?send=100", QUERY, "SELECT * WHERE {}")), + ParsedRequestIs("/", {{"send", {"100"}}}, + Query{"SELECT * WHERE {}", {}})); AD_EXPECT_THROW_WITH_MESSAGE( parse(makeRequest(http::verb::patch, "/")), testing::StrEq( @@ -119,13 +126,82 @@ TEST(ServerTest, parseHttpRequest) { parse(makeGetRequest("/?update=DELETE%20%2A%20WHERE%20%7B%7D")), testing::StrEq("SPARQL Update is not allowed as GET request.")); EXPECT_THAT(parse(makePostRequest("/", UPDATE, "DELETE * WHERE {}")), - ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}"})); + ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}", {}})); EXPECT_THAT(parse(makePostRequest("/", URLENCODED, "update=DELETE%20%2A%20WHERE%20%7B%7D")), - ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}"})); + ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}", {}})); EXPECT_THAT(parse(makePostRequest("/", URLENCODED, "update=DELETE+%2A+WHERE%20%7B%7D")), - ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}"})); + ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}", {}})); + // Check that the correct datasets for the method (GET or POST) are added + EXPECT_THAT( + parse(makeGetRequest("/?query=SELECT%20%2A%20WHERE%20%7B%7D&default-" + "graph-uri=foo&named-graph-uri=bar&using-graph-uri=" + "baz&using-named-graph-uri=cat")), + ParsedRequestIs("/", + {{"default-graph-uri", {"foo"}}, + {"named-graph-uri", {"bar"}}, + {"using-graph-uri", {"baz"}}, + {"using-named-graph-uri", {"cat"}}}, + Query{"SELECT * WHERE {}", + {DatasetClause{Iri(""), false}, + DatasetClause{Iri(""), true}}})); + EXPECT_THAT( + parse(makePostRequest("/?default-" + "graph-uri=foo&named-graph-uri=bar&using-graph-uri=" + "baz&using-named-graph-uri=cat", + QUERY, "SELECT * WHERE {}")), + ParsedRequestIs("/", + {{"default-graph-uri", {"foo"}}, + {"named-graph-uri", {"bar"}}, + {"using-graph-uri", {"baz"}}, + {"using-named-graph-uri", {"cat"}}}, + Query{"SELECT * WHERE {}", + {DatasetClause{Iri(""), false}, + DatasetClause{Iri(""), true}}})); + EXPECT_THAT( + parse(makePostRequest("/", URLENCODED, + "query=SELECT%20%2A%20WHERE%20%7B%7D&default-graph-" + "uri=foo&named-graph-uri=bar&using-graph-uri=baz&" + "using-named-graph-uri=cat")), + ParsedRequestIs("/", + {{"default-graph-uri", {"foo"}}, + {"named-graph-uri", {"bar"}}, + {"using-graph-uri", {"baz"}}, + {"using-named-graph-uri", {"cat"}}}, + Query{"SELECT * WHERE {}", + {DatasetClause{Iri(""), false}, + DatasetClause{Iri(""), true}}})); + EXPECT_THAT( + parse(makePostRequest("/", URLENCODED, + "update=INSERT%20DATA%20%7B%7D&default-graph-uri=" + "foo&named-graph-uri=bar&using-graph-uri=baz&" + "using-named-graph-uri=cat")), + ParsedRequestIs("/", + { + {"default-graph-uri", {"foo"}}, + {"named-graph-uri", {"bar"}}, + {"using-graph-uri", {"baz"}}, + {"using-named-graph-uri", {"cat"}}, + }, + Update{"INSERT DATA {}", + {DatasetClause{Iri(""), false}, + DatasetClause{Iri(""), true}}})); + EXPECT_THAT( + parse(makePostRequest( + "/?default-graph-uri=foo&named-graph-uri=bar&using-graph-uri=baz&" + "using-named-graph-uri=cat", + UPDATE, "INSERT DATA {}")), + ParsedRequestIs("/", + { + {"default-graph-uri", {"foo"}}, + {"named-graph-uri", {"bar"}}, + {"using-graph-uri", {"baz"}}, + {"using-named-graph-uri", {"cat"}}, + }, + Update{"INSERT DATA {}", + {DatasetClause{Iri(""), false}, + DatasetClause{Iri(""), true}}})); } TEST(ServerTest, determineResultPinning) { diff --git a/test/UrlParserTest.cpp b/test/UrlParserTest.cpp index d0b09ab030..c3db56d9bc 100644 --- a/test/UrlParserTest.cpp +++ b/test/UrlParserTest.cpp @@ -100,7 +100,7 @@ TEST(UrlParserTest, parseRequestTarget) { ::testing::StrEq("Failed to parse URL: \"file://more-than-target\".")); } -TEST(UrlParserTest, parseDatasetClauses) { +TEST(UrlParserTest, parseDatasetClausesFrom) { using namespace ad_utility::url_parser; // Construct the vector from an initializer list without specifying the type. @@ -109,23 +109,29 @@ TEST(UrlParserTest, parseDatasetClauses) { return ::testing::ContainerEq(datasetClauses); }; - EXPECT_THAT(parseDatasetClauses({}), IsDatasets({})); + EXPECT_THAT(parseDatasetClausesFrom({}, "default-grpah-uri", false), + IsDatasets({})); EXPECT_THAT( - parseDatasetClauses({{"default-graph-uri", {"https://w3.org/1"}}}), + parseDatasetClausesFrom({{"default-graph-uri", {"https://w3.org/1"}}}, + "default-graph-uri", false), IsDatasets({{iri(""), false}})); - EXPECT_THAT(parseDatasetClauses({{"named-graph-uri", {"https://w3.org/1"}}}), - IsDatasets({{iri(""), true}})); - EXPECT_THAT(parseDatasetClauses({{"default-graph-uri", {"https://w3.org/1"}}, - {"named-graph-uri", {"https://w3.org/2"}}}), - IsDatasets({{iri(""), false}, - {iri(""), true}})); EXPECT_THAT( - parseDatasetClauses( + parseDatasetClausesFrom({{"named-graph-uri", {"https://w3.org/1"}}}, + "named-graph-uri", true), + IsDatasets({{iri(""), true}})); + EXPECT_THAT( + parseDatasetClausesFrom({{"default-graph-uri", {"https://w3.org/1"}}, + {"named-graph-uri", {"https://w3.org/2"}}}, + "default-graph-uri", false), + IsDatasets({ + {iri(""), false}, + })); + EXPECT_THAT( + parseDatasetClausesFrom( {{"default-graph-uri", {"https://w3.org/1", "https://w3.org/2"}}, - {"named-graph-uri", {"https://w3.org/3", "https://w3.org/4"}}}), - IsDatasets({{iri(""), false}, - {iri(""), false}, - {iri(""), true}, + {"named-graph-uri", {"https://w3.org/3", "https://w3.org/4"}}}, + "named-graph-uri", true), + IsDatasets({{iri(""), true}, {iri(""), true}})); } From d06044449a00f97837b1fac96723ca1879d45e2b Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Tue, 28 Jan 2025 14:37:13 +0100 Subject: [PATCH 12/28] Fix the Docker build on ARM (#1733) The cross-compilation of the Docker image for ARM64 has been failing for a few days now, because a buggy QEMU version was downloaded by the used GitHub actions. We have now fixed QEMU to a working version. In the future we should build the ARM container natively and avoid the cross-compilation completely. --- .github/workflows/docker-publish.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index c567f8e97b..b8f8ddff6e 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -22,6 +22,13 @@ jobs: submodules: 'recursive' - name: Set up QEMU uses: docker/setup-qemu-action@v3 + with: + # As of Jan, 28, 2025 the default value here (`binfmt:latest`) + # downloads a QEMU version that leads to segfaults in the compiler. + # We therefore fix a working version + # TODO GitHub actions now has ARM runners, + # avoid cross-compilation completely + image : 'tonistiigi/binfmt:desktop-v8.1.5-44' - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub @@ -56,7 +63,7 @@ jobs: tags: adfreiburg/qlever:test - name: E2E in Docker - run: | + run: | sudo mkdir ${{github.workspace}}/e2e_data sudo chmod a+rwx ${{github.workspace}}/e2e_data sudo docker run -i --rm -v "${{github.workspace}}/e2e_data:/app/e2e_data/" --entrypoint e2e/e2e.sh adfreiburg/qlever:test From 49934c0013cc1861d665ec3647e748df3b593457 Mon Sep 17 00:00:00 2001 From: Felix Meisen <85636111+Flixtastic@users.noreply.github.com> Date: Thu, 30 Jan 2025 09:04:43 +0100 Subject: [PATCH 13/28] Refactor the writing and reading of the fulltext index (#1699) * Move the code for reading and writing the data structures of the text index to a separate class, making the large `IndexImpl` class significantly smaller. * While doing so, also completely refactor and modernize this code. --- src/index/CMakeLists.txt | 2 +- src/index/IndexImpl.Text.cpp | 337 +++++------------------------ src/index/IndexImpl.h | 40 +--- src/index/Postings.h | 10 + src/index/TextIndexReadWrite.cpp | 151 +++++++++++++ src/index/TextIndexReadWrite.h | 359 +++++++++++++++++++++++++++++++ 6 files changed, 573 insertions(+), 326 deletions(-) create mode 100644 src/index/Postings.h create mode 100644 src/index/TextIndexReadWrite.cpp create mode 100644 src/index/TextIndexReadWrite.h diff --git a/src/index/CMakeLists.txt b/src/index/CMakeLists.txt index 1fc8773721..4a226bdfdd 100644 --- a/src/index/CMakeLists.txt +++ b/src/index/CMakeLists.txt @@ -6,5 +6,5 @@ add_library(index DocsDB.cpp FTSAlgorithms.cpp PrefixHeuristic.cpp CompressedRelation.cpp PatternCreator.cpp ScanSpecification.cpp - DeltaTriples.cpp LocalVocabEntry.cpp) + DeltaTriples.cpp LocalVocabEntry.cpp TextIndexReadWrite.cpp) qlever_target_link_libraries(index util parser vocabulary ${STXXL_LIBRARIES}) diff --git a/src/index/IndexImpl.Text.cpp b/src/index/IndexImpl.Text.cpp index 81c135fdea..3b872eb39c 100644 --- a/src/index/IndexImpl.Text.cpp +++ b/src/index/IndexImpl.Text.cpp @@ -17,9 +17,9 @@ #include "backports/algorithm.h" #include "engine/CallFixedSize.h" #include "index/FTSAlgorithms.h" +#include "index/TextIndexReadWrite.h" #include "parser/WordsAndDocsFileParser.h" #include "util/Conversions.h" -#include "util/Simple8bCode.h" // _____________________________________________________________________________ cppcoro::generator IndexImpl::wordsInTextRecords( @@ -356,8 +356,10 @@ void IndexImpl::createTextIndex(const string& filename, if (std::get<0>(*reader) != currentBlockIndex) { AD_CONTRACT_CHECK(!classicPostings.empty()); - ContextListMetaData classic = writePostings(out, classicPostings, true); - ContextListMetaData entity = writePostings(out, entityPostings, false); + ContextListMetaData classic = textIndexReadWrite::writePostings( + out, classicPostings, true, currenttOffset_); + ContextListMetaData entity = textIndexReadWrite::writePostings( + out, entityPostings, false, currenttOffset_); textMeta_.addBlock(TextBlockMetaData( currentMinWordIndex, currentMaxWordIndex, classic, entity)); classicPostings.clear(); @@ -383,8 +385,10 @@ void IndexImpl::createTextIndex(const string& filename, } // Write the last block AD_CONTRACT_CHECK(!classicPostings.empty()); - ContextListMetaData classic = writePostings(out, classicPostings, true); - ContextListMetaData entity = writePostings(out, entityPostings, false); + ContextListMetaData classic = textIndexReadWrite::writePostings( + out, classicPostings, true, currenttOffset_); + ContextListMetaData entity = textIndexReadWrite::writePostings( + out, entityPostings, false, currenttOffset_); textMeta_.addBlock(TextBlockMetaData(currentMinWordIndex, currentMaxWordIndex, classic, entity)); classicPostings.clear(); @@ -403,89 +407,6 @@ void IndexImpl::createTextIndex(const string& filename, LOG(INFO) << "Text index build completed" << std::endl; } -// _____________________________________________________________________________ -ContextListMetaData IndexImpl::writePostings(ad_utility::File& out, - const vector& postings, - bool skipWordlistIfAllTheSame) { - ContextListMetaData meta; - meta._nofElements = postings.size(); - if (meta._nofElements == 0) { - meta._startContextlist = currenttOffset_; - meta._startWordlist = currenttOffset_; - meta._startScorelist = currenttOffset_; - meta._lastByte = currenttOffset_ - 1; - return meta; - } - - // Collect the individual lists - // Context lists are gap encoded, word and score lists frequency encoded. - // TODO these are gap encoded contextIds, maybe also create a type - // for this. - auto contextList = new uint64_t[meta._nofElements]; - WordIndex* wordList = new WordIndex[meta._nofElements]; - Score* scoreList = new Score[meta._nofElements]; - - size_t n = 0; - - WordCodeMap wordCodeMap; - WordCodebook wordCodebook; - ScoreCodeMap scoreCodeMap; - ScoreCodebook scoreCodebook; - - createCodebooks(postings, wordCodeMap, wordCodebook, scoreCodeMap, - scoreCodebook); - - TextRecordIndex lastContext = std::get<0>(postings[0]); - contextList[n] = lastContext.get(); - wordList[n] = wordCodeMap[std::get<1>(postings[0])]; - scoreList[n] = scoreCodeMap[std::get<2>(postings[0])]; - ++n; - - for (auto it = postings.begin() + 1; it < postings.end(); ++it) { - uint64_t gap = std::get<0>(*it).get() - lastContext.get(); - contextList[n] = gap; - lastContext = std::get<0>(*it); - wordList[n] = wordCodeMap[std::get<1>(*it)]; - scoreList[n] = scoreCodeMap[std::get<2>(*it)]; - ++n; - } - - AD_CONTRACT_CHECK(meta._nofElements == n); - - // Do the actual writing: - size_t bytes = 0; - - // Write context list: - meta._startContextlist = currenttOffset_; - bytes = writeList(contextList, meta._nofElements, out); - currenttOffset_ += bytes; - - // Write word list: - // This can be skipped if we're writing classic lists and there - // is only one distinct wordId in the block, since this Id is already - // stored in the meta data. - meta._startWordlist = currenttOffset_; - if (!skipWordlistIfAllTheSame || wordCodebook.size() > 1) { - currenttOffset_ += writeCodebook(wordCodebook, out); - bytes = writeList(wordList, meta._nofElements, out); - currenttOffset_ += bytes; - } - - // Write scores - meta._startScorelist = currenttOffset_; - currenttOffset_ += writeCodebook(scoreCodebook, out); - bytes = writeList(scoreList, meta._nofElements, out); - currenttOffset_ += bytes; - - meta._lastByte = currenttOffset_ - 1; - - delete[] contextList; - delete[] wordList; - delete[] scoreList; - - return meta; -} - /// yields aaaa, aaab, ..., zzzz static cppcoro::generator fourLetterPrefixes() { static_assert( @@ -640,82 +561,6 @@ TextBlockIndex IndexImpl::getWordBlockId(WordIndex wordIndex) const { blockBoundaries_.begin(); } -// _____________________________________________________________________________ -template -size_t IndexImpl::writeList(Numeric* data, size_t nofElements, - ad_utility::File& file) const { - if (nofElements > 0) { - uint64_t* encoded = new uint64_t[nofElements]; - size_t size = ad_utility::Simple8bCode::encode(data, nofElements, encoded); - size_t ret = file.write(encoded, size); - AD_CONTRACT_CHECK(size == ret); - delete[] encoded; - return size; - } else { - return 0; - } -} - -// _____________________________________________________________________________ -void IndexImpl::createCodebooks(const vector& postings, - IndexImpl::WordCodeMap& wordCodemap, - IndexImpl::WordCodebook& wordCodebook, - IndexImpl::ScoreCodeMap& scoreCodemap, - IndexImpl::ScoreCodebook& scoreCodebook) const { - // There should be a more efficient way to do this (Felix Meisen) - ad_utility::HashMap wfMap; - ad_utility::HashMap sfMap; - for (const auto& p : postings) { - wfMap[std::get<1>(p)] = 0; - sfMap[std::get<2>(p)] = 0; - } - for (const auto& p : postings) { - ++wfMap[std::get<1>(p)]; - ++sfMap[std::get<2>(p)]; - } - vector> wfVec; - wfVec.resize(wfMap.size()); - size_t i = 0; - for (auto it = wfMap.begin(); it != wfMap.end(); ++it) { - wfVec[i].first = it->first; - wfVec[i].second = it->second; - ++i; - } - vector> sfVec; - sfVec.resize(sfMap.size()); - i = 0; - for (auto it = sfMap.begin(); it != sfMap.end(); ++it) { - sfVec[i].first = it->first; - sfVec[i].second = it->second; - ++i; - } - std::sort(wfVec.begin(), wfVec.end(), - [](const auto& a, const auto& b) { return a.second > b.second; }); - std::sort( - sfVec.begin(), sfVec.end(), - [](const std::pair& a, const std::pair& b) { - return a.second > b.second; - }); - for (size_t j = 0; j < wfVec.size(); ++j) { - wordCodebook.push_back(wfVec[j].first); - wordCodemap[wfVec[j].first] = j; - } - for (size_t j = 0; j < sfVec.size(); ++j) { - scoreCodebook.push_back(sfVec[j].first); - scoreCodemap[sfVec[j].first] = static_cast(j); - } -} - -// _____________________________________________________________________________ -template -size_t IndexImpl::writeCodebook(const vector& codebook, - ad_utility::File& file) const { - size_t byteSizeOfCodebook = sizeof(T) * codebook.size(); - file.write(&byteSizeOfCodebook, sizeof(byteSizeOfCodebook)); - file.write(codebook.data(), byteSizeOfCodebook); - return byteSizeOfCodebook + sizeof(byteSizeOfCodebook); -} - // _____________________________________________________________________________ void IndexImpl::openTextFileHandle() { AD_CONTRACT_CHECK(!onDiskBase_.empty()); @@ -732,26 +577,28 @@ IdTable IndexImpl::readWordCl( const TextBlockMetaData& tbmd, const ad_utility::AllocatorWithLimit& allocator) const { IdTable idTable{3, allocator}; - vector cids = readGapComprList( - tbmd._cl._nofElements, tbmd._cl._startContextlist, + idTable.resize(tbmd._cl._nofElements); + textIndexReadWrite::readGapComprList( + idTable.getColumn(0).begin(), tbmd._cl._nofElements, + tbmd._cl._startContextlist, static_cast(tbmd._cl._startWordlist - tbmd._cl._startContextlist), - &TextRecordIndex::make); - idTable.resize(cids.size()); - ql::ranges::transform(cids, idTable.getColumn(0).begin(), - &Id::makeFromTextRecordIndex); - ql::ranges::transform( - readFreqComprList( - tbmd._cl._nofElements, tbmd._cl._startWordlist, - static_cast(tbmd._cl._startScorelist - - tbmd._cl._startWordlist)), - idTable.getColumn(1).begin(), [](WordIndex id) { + textIndexFile_, [](uint64_t id) { + return Id::makeFromTextRecordIndex(TextRecordIndex::make(id)); + }); + + textIndexReadWrite::readFreqComprList( + idTable.getColumn(1).begin(), tbmd._cl._nofElements, + tbmd._cl._startWordlist, + static_cast(tbmd._cl._startScorelist - tbmd._cl._startWordlist), + textIndexFile_, [](WordIndex id) { return Id::makeFromWordVocabIndex(WordVocabIndex::make(id)); }); - ql::ranges::transform( - readFreqComprList(tbmd._cl._nofElements, tbmd._cl._startScorelist, - static_cast(tbmd._cl._lastByte + 1 - - tbmd._cl._startScorelist)), - idTable.getColumn(2).begin(), &Id::makeFromInt); + + textIndexReadWrite::readFreqComprList( + idTable.getColumn(2).begin(), tbmd._cl._nofElements, + tbmd._cl._startScorelist, + static_cast(tbmd._cl._lastByte + 1 - tbmd._cl._startScorelist), + textIndexFile_, &Id::makeFromInt); return idTable; } @@ -760,27 +607,31 @@ IdTable IndexImpl::readWordEntityCl( const TextBlockMetaData& tbmd, const ad_utility::AllocatorWithLimit& allocator) const { IdTable idTable{3, allocator}; - vector cids = readGapComprList( - tbmd._entityCl._nofElements, tbmd._entityCl._startContextlist, + idTable.resize(tbmd._entityCl._nofElements); + textIndexReadWrite::readGapComprList( + idTable.getColumn(0).begin(), tbmd._entityCl._nofElements, + tbmd._entityCl._startContextlist, static_cast(tbmd._entityCl._startWordlist - tbmd._entityCl._startContextlist), - &TextRecordIndex::make); - idTable.resize(cids.size()); - ql::ranges::transform(cids, idTable.getColumn(0).begin(), - &Id::makeFromTextRecordIndex); - ql::ranges::copy( - readFreqComprList(tbmd._entityCl._nofElements, - tbmd._entityCl._startWordlist, - static_cast(tbmd._entityCl._startScorelist - - tbmd._entityCl._startWordlist), - &Id::fromBits), - idTable.getColumn(1).begin()); - ql::ranges::transform( - readFreqComprList( - tbmd._entityCl._nofElements, tbmd._entityCl._startScorelist, - static_cast(tbmd._entityCl._lastByte + 1 - - tbmd._entityCl._startScorelist)), - idTable.getColumn(2).begin(), &Id::makeFromInt); + textIndexFile_, [](uint64_t id) { + return Id::makeFromTextRecordIndex(TextRecordIndex::make(id)); + }); + + textIndexReadWrite::readFreqComprList( + idTable.getColumn(1).begin(), tbmd._entityCl._nofElements, + tbmd._entityCl._startWordlist, + static_cast(tbmd._entityCl._startScorelist - + tbmd._entityCl._startWordlist), + textIndexFile_, [](uint64_t from) { + return Id::makeFromVocabIndex(VocabIndex::make(from)); + }); + + textIndexReadWrite::readFreqComprList( + idTable.getColumn(2).begin(), tbmd._entityCl._nofElements, + tbmd._entityCl._startScorelist, + static_cast(tbmd._entityCl._lastByte + 1 - + tbmd._entityCl._startScorelist), + textIndexFile_, &Id::makeFromInt); return idTable; } @@ -818,92 +669,6 @@ IdTable IndexImpl::getEntityMentionsForWord( return readWordEntityCl(tbmd, allocator); } -// _____________________________________________________________________________ -template -vector IndexImpl::readGapComprList(size_t nofElements, off_t from, - size_t nofBytes, - MakeFromUint64t makeFromUint64t) const { - LOG(DEBUG) << "Reading gap-encoded list from disk...\n"; - LOG(TRACE) << "NofElements: " << nofElements << ", from: " << from - << ", nofBytes: " << nofBytes << '\n'; - vector result; - result.resize(nofElements + 250); - uint64_t* encoded = new uint64_t[nofBytes / 8]; - textIndexFile_.read(encoded, nofBytes, from); - LOG(DEBUG) << "Decoding Simple8b code...\n"; - ad_utility::Simple8bCode::decode(encoded, nofElements, result.data(), - makeFromUint64t); - LOG(DEBUG) << "Reverting gaps to actual IDs...\n"; - - // TODO make this hack unnecessary, probably by a proper output - // iterator. - if constexpr (requires { T::make(0); }) { - uint64_t id = 0; - for (size_t i = 0; i < result.size(); ++i) { - id += result[i].get(); - result[i] = T::make(id); - } - } else { - T id = 0; - for (size_t i = 0; i < result.size(); ++i) { - id += result[i]; - result[i] = id; - } - } - result.resize(nofElements); - delete[] encoded; - LOG(DEBUG) << "Done reading gap-encoded list. Size: " << result.size() - << "\n"; - return result; -} - -// _____________________________________________________________________________ -template -vector IndexImpl::readFreqComprList(size_t nofElements, off_t from, - size_t nofBytes, - MakeFromUint64t makeFromUint) const { - AD_CONTRACT_CHECK(nofBytes > 0); - LOG(DEBUG) << "Reading frequency-encoded list from disk...\n"; - LOG(TRACE) << "NofElements: " << nofElements << ", from: " << from - << ", nofBytes: " << nofBytes << '\n'; - size_t nofCodebookBytes; - vector result; - uint64_t* encoded = new uint64_t[nofElements]; - result.resize(nofElements + 250); - off_t current = from; - size_t ret = textIndexFile_.read(&nofCodebookBytes, sizeof(off_t), current); - LOG(TRACE) << "Nof Codebook Bytes: " << nofCodebookBytes << '\n'; - AD_CONTRACT_CHECK(sizeof(off_t) == ret); - current += ret; - T* codebook = new T[nofCodebookBytes / sizeof(T)]; - ret = textIndexFile_.read(codebook, nofCodebookBytes, current); - current += ret; - AD_CONTRACT_CHECK(ret == size_t(nofCodebookBytes)); - ret = textIndexFile_.read( - encoded, static_cast(nofBytes - (current - from)), current); - current += ret; - AD_CONTRACT_CHECK(size_t(current - from) == nofBytes); - LOG(DEBUG) << "Decoding Simple8b code...\n"; - ad_utility::Simple8bCode::decode(encoded, nofElements, result.data(), - makeFromUint); - LOG(DEBUG) << "Reverting frequency encoded items to actual IDs...\n"; - result.resize(nofElements); - for (size_t i = 0; i < result.size(); ++i) { - // TODO handle the strong ID types properly. - if constexpr (requires(T t) { t.getBits(); }) { - result[i] = Id::makeFromVocabIndex( - VocabIndex::make(codebook[result[i].getBits()].getBits())); - } else { - result[i] = codebook[result[i]]; - } - } - delete[] encoded; - delete[] codebook; - LOG(DEBUG) << "Done reading frequency-encoded list. Size: " << result.size() - << "\n"; - return result; -} - // _____________________________________________________________________________ size_t IndexImpl::getIndexOfBestSuitedElTerm( const vector& terms) const { diff --git a/src/index/IndexImpl.h b/src/index/IndexImpl.h index ac0003db87..d284cdb415 100644 --- a/src/index/IndexImpl.h +++ b/src/index/IndexImpl.h @@ -25,6 +25,7 @@ #include "index/IndexMetaData.h" #include "index/PatternCreator.h" #include "index/Permutation.h" +#include "index/Postings.h" #include "index/StxxlSortFunctors.h" #include "index/TextMetaData.h" #include "index/Vocabulary.h" @@ -106,7 +107,6 @@ class IndexImpl { // Block Id, Context Id, Word Id, Score, entity using TextVec = stxxl::vector< tuple>; - using Posting = std::tuple; struct IndexMetaDataMmapDispatcher { using WriteType = IndexMetaDataMmap; @@ -588,10 +588,6 @@ class IndexImpl { void createTextIndex(const string& filename, const TextVec& vec); - ContextListMetaData writePostings(ad_utility::File& out, - const vector& postings, - bool skipWordlistIfAllTheSame); - void openTextFileHandle(); void addContextToVector(TextVec::bufwriter_type& writer, @@ -599,15 +595,6 @@ class IndexImpl { const ad_utility::HashMap& words, const ad_utility::HashMap& entities); - template - vector readGapComprList(size_t nofElements, off_t from, size_t nofBytes, - MakeFromUint64t makeFromUint64t) const; - - template - vector readFreqComprList( - size_t nofElements, off_t from, size_t nofBytes, - MakeFromUint64t makeFromUint = MakeFromUint64t{}) const; - // Get the metadata for the block from the text index that contains the // `word`. Also works for prefixes that are terminated with `PREFIX_CHAR` like // "astro*". Returns `nullopt` if no suitable block was found because no @@ -642,31 +629,6 @@ class IndexImpl { TextBlockIndex getWordBlockId(WordIndex wordIndex) const; - //! Writes a list of elements (have to be able to be cast to unit64_t) - //! to file. - //! Returns the number of bytes written. - template - size_t writeList(Numeric* data, size_t nofElements, - ad_utility::File& file) const; - - // TODO understand what the "codes" are, are they better just ints? - // After using createCodebooks on these types, the lowest codes refer to the - // most frequent WordIndex/Score. The maps are mapping those codes to their - // respective frequency. - typedef ad_utility::HashMap WordCodeMap; - typedef ad_utility::HashMap ScoreCodeMap; - typedef vector WordCodebook; - typedef vector ScoreCodebook; - - //! Creates codebooks for lists that are supposed to be entropy encoded. - void createCodebooks(const vector& postings, - WordCodeMap& wordCodemap, WordCodebook& wordCodebook, - ScoreCodeMap& scoreCodemap, - ScoreCodebook& scoreCodebook) const; - - template - size_t writeCodebook(const vector& codebook, ad_utility::File& file) const; - // FRIEND TESTS friend class IndexTest_createFromTsvTest_Test; friend class IndexTest_createFromOnDiskIndexTest_Test; diff --git a/src/index/Postings.h b/src/index/Postings.h new file mode 100644 index 0000000000..262c661f67 --- /dev/null +++ b/src/index/Postings.h @@ -0,0 +1,10 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Felix Meisen (fesemeisen@outlook.de) + +#pragma once + +#include "global/Id.h" +#include "global/IndexTypes.h" + +using Posting = std::tuple; diff --git a/src/index/TextIndexReadWrite.cpp b/src/index/TextIndexReadWrite.cpp new file mode 100644 index 0000000000..3079aa182d --- /dev/null +++ b/src/index/TextIndexReadWrite.cpp @@ -0,0 +1,151 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Felix Meisen (fesemeisen@outlook.de) + +#include "index/TextIndexReadWrite.h" + +namespace textIndexReadWrite { + +// ____________________________________________________________________________ +ContextListMetaData writePostings(ad_utility::File& out, + const vector& postings, + bool skipWordlistIfAllTheSame, + off_t& currentOffset) { + ContextListMetaData meta; + meta._nofElements = postings.size(); + if (meta._nofElements == 0) { + meta._startContextlist = currentOffset; + meta._startWordlist = currentOffset; + meta._startScorelist = currentOffset; + meta._lastByte = currentOffset - 1; + return meta; + } + + GapEncode textRecordEncoder(postings | + ql::views::transform([](const Posting& posting) { + return std::get<0>(posting).get(); + })); + FrequencyEncode wordIndexEncoder( + postings | ql::views::transform([](const Posting& posting) { + return std::get<1>(posting); + })); + FrequencyEncode scoreEncoder(postings | + ql::views::transform([](const Posting& posting) { + return std::get<2>(posting); + })); + + meta._startContextlist = currentOffset; + textRecordEncoder.writeToFile(out, currentOffset); + + meta._startWordlist = currentOffset; + if (!skipWordlistIfAllTheSame || wordIndexEncoder.getCodeBook().size() > 1) { + wordIndexEncoder.writeToFile(out, currentOffset); + } + + meta._startScorelist = currentOffset; + scoreEncoder.writeToFile(out, currentOffset); + + meta._lastByte = currentOffset - 1; + + return meta; +} + +// ____________________________________________________________________________ +template +size_t writeCodebook(const vector& codebook, ad_utility::File& file) { + size_t byteSizeOfCodebook = sizeof(T) * codebook.size(); + file.write(&byteSizeOfCodebook, sizeof(byteSizeOfCodebook)); + file.write(codebook.data(), byteSizeOfCodebook); + return byteSizeOfCodebook + sizeof(byteSizeOfCodebook); +} + +// ____________________________________________________________________________ +template +void encodeAndWriteSpanAndMoveOffset(std::span spanToWrite, + ad_utility::File& file, + off_t& currentOffset) { + size_t bytes = 0; + if (spanToWrite.size() > 0) { + std::vector encoded; + encoded.resize(spanToWrite.size()); + bytes = ad_utility::Simple8bCode::encode( + spanToWrite.data(), spanToWrite.size(), encoded.data()); + size_t ret = file.write(encoded.data(), bytes); + AD_CONTRACT_CHECK(bytes == ret); + } + currentOffset += bytes; +} + +} // namespace textIndexReadWrite + +// ____________________________________________________________________________ +template +template +void FrequencyEncode::initialize(View&& view) { + if (ql::ranges::empty(view)) { + return; + } + // Create the frequency map to count how often a certain value of type T + // appears in the vector + TypedMap frequencyMap; + for (const auto& value : view) { + ++frequencyMap[value]; + } + + // Convert the hashmap to a vector to sort it by most frequent first + std::vector> frequencyVector; + frequencyVector.reserve(frequencyMap.size()); + ql::ranges::copy(frequencyMap, std::back_inserter(frequencyVector)); + ql::ranges::sort(frequencyVector, [](const auto& a, const auto& b) { + return a.second > b.second; + }); + + // Write the codeBook and codeMap + // codeBook contains all values of type T exactly ones, sorted by frequency + // descending + // codeMap maps all values of type T that where in the vector to their + // position in the codeBook + codeBook_.reserve(frequencyVector.size()); + codeMap_.reserve(frequencyVector.size()); + size_t i = 0; + for (const auto& frequencyPair : frequencyVector) { + codeBook_.push_back(frequencyPair.first); + codeMap_[frequencyPair.first] = i; + ++i; + } + + // Finally encode the vector + encodedVector_.reserve(ql::ranges::size(view)); + for (const auto& value : view) { + encodedVector_.push_back(codeMap_[value]); + } +} + +// ____________________________________________________________________________ +template +void FrequencyEncode::writeToFile(ad_utility::File& out, + off_t& currentOffset) { + currentOffset += textIndexReadWrite::writeCodebook(codeBook_, out); + textIndexReadWrite::encodeAndWriteSpanAndMoveOffset( + encodedVector_, out, currentOffset); +} + +// ____________________________________________________________________________ +template +requires std::is_arithmetic_v template +void GapEncode::initialize(View&& view) { + if (ql::ranges::empty(view)) { + return; + } + encodedVector_.reserve(ql::ranges::size(view)); + std::adjacent_difference(ql::ranges::begin(view), ql::ranges::end(view), + std::back_inserter(encodedVector_)); +} + +// ____________________________________________________________________________ +template +requires std::is_arithmetic_v +void GapEncode::writeToFile(ad_utility::File& out, off_t& currentOffset) { + textIndexReadWrite::encodeAndWriteSpanAndMoveOffset(encodedVector_, out, + currentOffset); +} diff --git a/src/index/TextIndexReadWrite.h b/src/index/TextIndexReadWrite.h new file mode 100644 index 0000000000..9860f4de0b --- /dev/null +++ b/src/index/TextIndexReadWrite.h @@ -0,0 +1,359 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Felix Meisen (fesemeisen@outlook.de) + +#pragma once + +#include "global/Id.h" +#include "global/IndexTypes.h" +#include "index/Postings.h" +#include "index/TextMetaData.h" +#include "util/HashMap.h" +#include "util/Simple8bCode.h" +#include "util/TransparentFunctors.h" + +namespace textIndexReadWrite::detail { + +// This function contains the actual frequency compressed list reading and does +// the following steps: +// - Read codebook size +// - Read codebook +// - return the read codebook through reference +// - Read list from disk which is simple8b and frequency encoded +// - simple8b decode the list +// - return the still frequency encoded vector through reference +template +void readFreqComprListHelper(size_t nofElements, off_t from, size_t nofBytes, + const ad_utility::File& textIndexFile, + vector& frequencyEncodedVector, + std::vector& codebook) { + AD_CONTRACT_CHECK(nofBytes > 0); + LOG(DEBUG) << "Reading frequency-encoded list from disk...\n"; + LOG(TRACE) << "NofElements: " << nofElements << ", from: " << from + << ", nofBytes: " << nofBytes << '\n'; + + // Read codebook size and advance pointer (current) + size_t nofCodebookBytes; + off_t current = from; + size_t ret = textIndexFile.read(&nofCodebookBytes, sizeof(size_t), current); + LOG(TRACE) << "Nof Codebook Bytes: " << nofCodebookBytes << '\n'; + AD_CONTRACT_CHECK(sizeof(size_t) == ret); + current += ret; + + // Set correct size of codebook, read codebook, advance pointer (current) + codebook.resize(nofCodebookBytes / sizeof(From)); + ret = textIndexFile.read(codebook.data(), nofCodebookBytes, current); + AD_CONTRACT_CHECK(ret == size_t(nofCodebookBytes)); + current += ret; + + // Create vector that is simple8b and frequency encoded, read encoded vector + // from file, advance pointer (current) + std::vector simple8bEncoded; + simple8bEncoded.resize(nofElements); + ret = textIndexFile.read(simple8bEncoded.data(), nofBytes - (current - from), + current); + current += ret; + + AD_CONTRACT_CHECK(size_t(current - from) == nofBytes); + + // simple8b decode the list which is then directly passed to + // frequencyEncodedVector. The resizing with overhead is necessary for + // simple8b + LOG(DEBUG) << "Decoding Simple8b code...\n"; + frequencyEncodedVector.resize(nofElements + 250); + ad_utility::Simple8bCode::decode(simple8bEncoded.data(), nofElements, + frequencyEncodedVector.data()); + LOG(DEBUG) << "Reverting frequency encoded items to actual IDs...\n"; + frequencyEncodedVector.resize(nofElements); +} + +// This function contains the actual gap compressed list reading and does the +// following steps: +// - Read list from disk which is simple8b and gap encoded +// - simple8b decode the list +// - return the still gap encoded vector through reference +template +void readGapComprListHelper(size_t nofElements, off_t from, size_t nofBytes, + const ad_utility::File& textIndexFile, + vector& gapEncodedVector) { + LOG(DEBUG) << "Reading gap-encoded list from disk...\n"; + LOG(TRACE) << "NofElements: " << nofElements << ", from: " << from + << ", nofBytes: " << nofBytes << '\n'; + + // Create vector that is simple8b and gap encoded, read encoded vector from + // file + std::vector simple8bEncoded; + simple8bEncoded.resize(nofBytes / 8); + textIndexFile.read(simple8bEncoded.data(), nofBytes, from); + + // simple8b decode the list which is then directly passed to + // gapEncodedVector. The resizing with overhead is necessary for simple8b + LOG(DEBUG) << "Decoding Simple8b code...\n"; + gapEncodedVector.resize(nofElements + 250); + ad_utility::Simple8bCode::decode(simple8bEncoded.data(), nofElements, + gapEncodedVector.data()); + LOG(DEBUG) << "Reverting gaps to actual IDs...\n"; + gapEncodedVector.resize(nofElements); +} + +} // namespace textIndexReadWrite::detail +namespace textIndexReadWrite { + +/** + * @brief Writes posting to given file. It splits the vector of postings into + * the lists for each respetive tuple element of postings. + * The TextRecordIndex list gets gap encoded and then simple8b encoded + * before being written to file. The WordIndex and Score lists get + * frequency encoded and then simple8b encoded before being written to + * file. + * @param out The file to write to. + * @param postings The vector of postings to write. + * @param skipWordlistIfAllTheSame If true, the wordlist is not written to file. + * This can be done because the WordIndex for + * the first and last word in a block are saved + * in the TextBlockMetaData. Always should be + * false for the entity postings. + * @param currentOffset The current offset in the file which gets passed by + * reference because it gets updated. + * + */ +ContextListMetaData writePostings(ad_utility::File& out, + const vector& postings, + bool skipWordlistIfAllTheSame, + off_t& currentOffset); + +template +size_t writeCodebook(const vector& codebook, ad_utility::File& file); + +/** + * @brief Encodes a span of elements and writes the encoded list to file. + * Advances the currentOffset by number of bytes written. + * @param spanToWrite The span of elements to encode and write. + * @param file The file to write the encoded list to. + * @param currentOffset The offset that's advanced. + * @warning The elements of the list have to be able to be cast to uint64_t. + */ +template +void encodeAndWriteSpanAndMoveOffset(std::span spanToWrite, + ad_utility::File& file, + off_t& currentOffset); + +/** + * @brief Reads a frequency encoded list from the given file and casts its + * elements to the To type using the given transformer. The From type + * specifies the type that was used to create the codebook in the writing + * step. + * @return The decoded list as vector. + * @param nofElements The number of elements in the list. + * @param from The offset in the file to start reading from. + * @param nofBytes The number of bytes to read which can't be deduced from the + * number of elements since the list is simple8b compressed. + * @param textIndexFile The file to read from. + * @param transformer The transformer to cast the decoded values to the To type. + * If no transformer is given, a static cast is used. + */ +template )> +vector readFreqComprList(size_t nofElements, off_t from, size_t nofBytes, + const ad_utility::File& textIndexFile, + Transformer transformer = {}) { + vector frequencyEncodedVector; + vector codebook; + detail::readFreqComprListHelper(nofElements, from, nofBytes, textIndexFile, + frequencyEncodedVector, codebook); + vector result; + result.reserve(frequencyEncodedVector.size()); + ql::ranges::for_each(frequencyEncodedVector, [&](const auto& encoded) { + result.push_back(transformer(codebook.at(encoded))); + }); + LOG(DEBUG) << "Done reading frequency-encoded list. Size: " << result.size() + << "\n"; + return result; +} + +/** + * @brief Does the same as the other readFreqComprList but writes the decoded + * list to the given iterator instead of returning it. + * @warning The iterator has to be big enough to be increased nofElements times. + * (it is increased once more but not written to and then discarded) + */ +template )> +void readFreqComprList(OutputIterator iterator, size_t nofElements, off_t from, + size_t nofBytes, const ad_utility::File& textIndexFile, + Transformer transformer = {}) { + vector frequencyEncodedVector; + vector codebook; + detail::readFreqComprListHelper(nofElements, from, nofBytes, textIndexFile, + frequencyEncodedVector, codebook); + ql::ranges::for_each(frequencyEncodedVector, [&](const auto& encoded) { + *iterator = transformer(codebook.at(encoded)); + ++iterator; + }); + LOG(DEBUG) << "Done reading frequency-encoded list."; +} + +/** + * @brief Reads a gap encoded list from the given file and casts its elements to + * the To type using the given transformer. The From type specifies the + * type that was used to calculate the gaps in the writing step. + * @return The decoded list as vector. + * @param nofElements The number of elements in the list. + * @param from The offset in the file to start reading from. + * @param nofBytes The number of bytes to read which can't be deduced from the + * number of elements since the list is simple8b compressed. + * @param textIndexFile The file to read from. + * @param transformer The transformer to cast the decoded values to the To type. + * If no transformer is given, a static cast is used. + */ +template )> +vector readGapComprList(size_t nofElements, off_t from, size_t nofBytes, + const ad_utility::File& textIndexFile, + Transformer transformer = {}) { + vector gapEncodedVector; + detail::readGapComprListHelper(nofElements, from, nofBytes, textIndexFile, + gapEncodedVector); + + // Undo gapEncoding + vector result; + result.reserve(nofElements); + From previous = 0; + for (auto gap : gapEncodedVector) { + previous += gap; + result.push_back(transformer(previous)); + } + LOG(DEBUG) << "Done reading gap-encoded list. Size: " << result.size() + << "\n"; + return result; +} + +/** + * @brief Does the same as the other readGapComprList but writes the decoded + * list to the given iterator instead of returning it. + * @warning The iterator has to be big enough to be increased nofElements times. + * (it is increased once more but not written to and then discarded) + */ +template )> +void readGapComprList(OutputIterator iterator, size_t nofElements, off_t from, + size_t nofBytes, const ad_utility::File& textIndexFile, + Transformer transformer = {}) { + vector gapEncodedVector; + detail::readGapComprListHelper(nofElements, from, nofBytes, textIndexFile, + gapEncodedVector); + + // Undo gapEncoding + From previous = 0; + for (auto gap : gapEncodedVector) { + previous += gap; + *iterator = transformer(previous); + ++iterator; + } + LOG(DEBUG) << "Done reading gap-encoded list."; +} + +} // namespace textIndexReadWrite + +/** + * @brief A class used to encode a view of elements by frequency encoding them. + * It does this during the construction of the object and stores the + * encoded vector, the codebook and the code map. It also has a method + * to write the encoded vector to a file. + */ +template +class FrequencyEncode { + public: + using TypedMap = ad_utility::HashMap; + using TypedVector = std::vector; + using CodeMap = TypedMap; + using CodeBook = TypedVector; + + // View must be an input range with value type `T`. + // The constructor forwards the view to the initialize function. This is + // necessary because of a clang 16 bug. This bug causes the declaration + // of a method in the .h file to not be connected to its definition in the + // .cpp file when using this specific requires clause. To circumvent this the + // requires clause is kept only in the .h file and the constructor calls the + // initialize function which has no direct requires clause. + CPP_template(typename View)(requires( + !std::same_as< + FrequencyEncode, + std::remove_cvref_t>)) explicit FrequencyEncode(View&& view) { + initialize(std::forward(view)); + }; + + FrequencyEncode() = delete; + FrequencyEncode(const FrequencyEncode&) = delete; + FrequencyEncode& operator=(const FrequencyEncode&) = delete; + FrequencyEncode(FrequencyEncode&&) = delete; + FrequencyEncode& operator=(FrequencyEncode&&) = delete; + + void writeToFile(ad_utility::File& out, off_t& currentOffset); + + const std::vector& getEncodedVector() const { return encodedVector_; } + const CodeMap& getCodeMap() const { return codeMap_; } + const CodeBook& getCodeBook() const { return codeBook_; } + + private: + // This method implements the constructor. The reason is explained in the + // comment above the constructor. + template + void initialize(View&& view); + + std::vector encodedVector_; + CodeMap codeMap_; + CodeBook codeBook_; +}; + +template +FrequencyEncode(View&& view) + -> FrequencyEncode>>; + +/** + * @brief A class used to encode a view of elements by gap encoding them. + * It does this during the construction of the object and stores the + * encoded vector. It also has a method to write the encoded vector to + * a file. + */ +template +requires std::is_arithmetic_v class GapEncode { + public: + using TypedVector = std::vector; + + // View must be an input range with value type `T`. + // The constructor forwards the view to the initialize function. This is + // necessary because of a clang 16 bug. This bug causes the declaration + // of a method in the .h file to not be connected to its definition in the + // .cpp file when using this specific requires clause. To circumvent this the + // requires clause is kept only in the .h file and the constructor calls the + // initialize function which has no direct requires clause. + CPP_template(typename View)(requires( + !std::same_as>)) explicit GapEncode(View&& + view) { + initialize(std::forward(view)); + }; + + GapEncode() = delete; + GapEncode(const GapEncode&) = delete; + GapEncode& operator=(const GapEncode&) = delete; + GapEncode(GapEncode&&) = delete; + GapEncode& operator=(GapEncode&&) = delete; + + void writeToFile(ad_utility::File& out, off_t& currentOffset); + + const TypedVector& getEncodedVector() const { return encodedVector_; } + + private: + // This method implements the constructor. The reason is explained in the + // comment above the constructor. + template + void initialize(View&& view); + + TypedVector encodedVector_; +}; + +template +GapEncode(View&& view) + -> GapEncode>>; From 1f38ba3f7829c6ccc8abb0552ff0047dbe37b796 Mon Sep 17 00:00:00 2001 From: Julian <14220769+Qup42@users.noreply.github.com> Date: Fri, 31 Jan 2025 01:16:20 +0100 Subject: [PATCH 14/28] Also support `Authorization:Bearer` header to specify access token (#1720) The access token (for privileged functionality) could so far only be passed as a parameter (that is, either as a field in the data for `application/x-www-form-urlencoded` or as a query parameter). With this change, the access token is also read from the `Authorization` header, provided it exists and the value has the form `Bearer `. If the access token is specified both via the `Authorization` header and via the `access-token` parameter, they must be the same. Resolves #1691 --- .github/workflows/sparql-conformance.yml | 2 +- src/engine/Server.cpp | 65 +++++-- src/engine/Server.h | 7 + src/util/http/UrlParser.h | 2 + test/ServerTest.cpp | 213 ++++++++++++++++++++--- test/util/HttpRequestHelpers.h | 3 +- 6 files changed, 249 insertions(+), 43 deletions(-) diff --git a/.github/workflows/sparql-conformance.yml b/.github/workflows/sparql-conformance.yml index 38152b5c79..3e4bdfd63d 100644 --- a/.github/workflows/sparql-conformance.yml +++ b/.github/workflows/sparql-conformance.yml @@ -80,7 +80,7 @@ jobs: echo ${{github.event.pull_request.head.sha}} > ./conformance-report/sha mv ${{ github.workspace}}/qlever-test-suite/results/${{ github.sha }}.json.bz2 conformance-report/${{ github.event.pull_request.head.sha }}.json.bz2 - name: Upload coverage artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: conformance-report path: conformance-report/ \ No newline at end of file diff --git a/src/engine/Server.cpp b/src/engine/Server.cpp index 8c1248203c..4f7fc97a4e 100644 --- a/src/engine/Server.cpp +++ b/src/engine/Server.cpp @@ -169,6 +169,37 @@ void Server::run(const string& indexBaseName, bool useText, bool usePatterns, httpServer.run(); } +std::optional Server::extractAccessToken( + const ad_utility::httpUtils::HttpRequest auto& request, + const ad_utility::url_parser::ParamValueMap& params) { + std::optional tokenFromAuthorizationHeader; + std::optional tokenFromParameter; + if (request.find(http::field::authorization) != request.end()) { + string_view authorization = request[http::field::authorization]; + const std::string prefix = "Bearer "; + if (!authorization.starts_with(prefix)) { + throw std::runtime_error(absl::StrCat( + "Authorization header doesn't start with \"", prefix, "\".")); + } + authorization.remove_prefix(prefix.length()); + tokenFromAuthorizationHeader = std::string(authorization); + } + if (params.contains("access-token")) { + tokenFromParameter = ad_utility::url_parser::getParameterCheckAtMostOnce( + params, "access-token"); + } + // If both are specified, they must be equal. This way there is no hidden + // precedence. + if (tokenFromAuthorizationHeader && tokenFromParameter && + tokenFromAuthorizationHeader != tokenFromParameter) { + throw std::runtime_error( + "Access token is specified both in the `Authorization` header and by " + "the `access-token` parameter, but they are not the same"); + } + return tokenFromAuthorizationHeader ? std::move(tokenFromAuthorizationHeader) + : std::move(tokenFromParameter); +} + // _____________________________________________________________________________ ad_utility::url_parser::ParsedRequest Server::parseHttpRequest( const ad_utility::httpUtils::HttpRequest auto& request) { @@ -177,7 +208,8 @@ ad_utility::url_parser::ParsedRequest Server::parseHttpRequest( // This is a concatenation of the URL path and the query strings. auto parsedUrl = ad_utility::url_parser::parseRequestTarget(request.target()); ad_utility::url_parser::ParsedRequest parsedRequest{ - std::move(parsedUrl.path_), std::move(parsedUrl.parameters_), None{}}; + std::move(parsedUrl.path_), std::nullopt, + std::move(parsedUrl.parameters_), None{}}; // Some valid requests (e.g. QLever's custom commands like retrieving index // statistics) don't have a query. So an empty operation is not necessarily an @@ -208,10 +240,15 @@ ad_utility::url_parser::ParsedRequest Server::parseHttpRequest( addToDatasetClausesIfOperationIs(ti, "using-graph-uri", false); addToDatasetClausesIfOperationIs(ti, "using-named-graph-uri", true); }; + auto extractAccessTokenFromRequest = [&parsedRequest, &request]() { + parsedRequest.accessToken_ = + extractAccessToken(request, parsedRequest.parameters_); + }; if (request.method() == http::verb::get) { setOperationIfSpecifiedInParams(ti, "query"); addDatasetClauses(); + extractAccessTokenFromRequest(); if (parsedRequest.parameters_.contains("update")) { throw std::runtime_error("SPARQL Update is not allowed as GET request."); @@ -283,16 +320,22 @@ ad_utility::url_parser::ParsedRequest Server::parseHttpRequest( setOperationIfSpecifiedInParams(ti, "query"); setOperationIfSpecifiedInParams(ti, "update"); addDatasetClauses(); + // We parse the access token from the url-encoded parameters in the body. + // The URL parameters must be empty for URL-encoded POST (see above). + extractAccessTokenFromRequest(); + return parsedRequest; } if (contentType.starts_with(contentTypeSparqlQuery)) { parsedRequest.operation_ = Query{request.body(), {}}; addDatasetClauses(); + extractAccessTokenFromRequest(); return parsedRequest; } if (contentType.starts_with(contentTypeSparqlUpdate)) { parsedRequest.operation_ = Update{request.body(), {}}; addDatasetClauses(); + extractAccessTokenFromRequest(); return parsedRequest; } throw std::runtime_error(absl::StrCat( @@ -369,15 +412,13 @@ Awaitable Server::process( // Check the access token. If an access token is provided and the check fails, // throw an exception and do not process any part of the query (even if the // processing had been allowed without access token). - bool accessTokenOk = - checkAccessToken(checkParameter("access-token", std::nullopt)); + bool accessTokenOk = checkAccessToken(parsedHttpRequest.accessToken_); auto requireValidAccessToken = [&accessTokenOk]( const std::string& actionName) { if (!accessTokenOk) { throw std::runtime_error(absl::StrCat( actionName, - " requires a valid access token. No valid access token is present.", - "Processing of request aborted.")); + " requires a valid access token but no access token was provided")); } }; @@ -1181,17 +1222,15 @@ bool Server::checkAccessToken( if (!accessToken) { return false; } - auto accessTokenProvidedMsg = absl::StrCat( - "Access token \"access-token=", accessToken.value(), "\" provided"); - auto requestIgnoredMsg = ", request is ignored"; + const auto accessTokenProvidedMsg = "Access token was provided"; if (accessToken_.empty()) { - throw std::runtime_error(absl::StrCat( - accessTokenProvidedMsg, - " but server was started without --access-token", requestIgnoredMsg)); + throw std::runtime_error( + absl::StrCat(accessTokenProvidedMsg, + " but server was started without --access-token")); } else if (!ad_utility::constantTimeEquals(accessToken.value(), accessToken_)) { - throw std::runtime_error(absl::StrCat( - accessTokenProvidedMsg, " but not correct", requestIgnoredMsg)); + throw std::runtime_error( + absl::StrCat(accessTokenProvidedMsg, " but it was invalid")); } else { LOG(DEBUG) << accessTokenProvidedMsg << " and correct" << std::endl; return true; diff --git a/src/engine/Server.h b/src/engine/Server.h index 4e0889b48a..9558f8743e 100644 --- a/src/engine/Server.h +++ b/src/engine/Server.h @@ -35,6 +35,7 @@ class Server { FRIEND_TEST(ServerTest, parseHttpRequest); FRIEND_TEST(ServerTest, getQueryId); FRIEND_TEST(ServerTest, createMessageSender); + FRIEND_TEST(ServerTest, extractAccessToken); public: explicit Server(unsigned short port, size_t numThreads, @@ -115,6 +116,12 @@ class Server { static ad_utility::url_parser::ParsedRequest parseHttpRequest( const ad_utility::httpUtils::HttpRequest auto& request); + /// Extract the Access token for that request from the `Authorization` header + /// or the URL query parameters. + static std::optional extractAccessToken( + const ad_utility::httpUtils::HttpRequest auto& request, + const ad_utility::url_parser::ParamValueMap& params); + /// Handle a single HTTP request. Check whether a file request or a query was /// sent, and dispatch to functions handling these cases. This function /// requires the constraints for the `HttpHandler` in `HttpServer.h`. diff --git a/src/util/http/UrlParser.h b/src/util/http/UrlParser.h index 8c1d272aac..45b05e4334 100644 --- a/src/util/http/UrlParser.h +++ b/src/util/http/UrlParser.h @@ -71,10 +71,12 @@ struct None { // Representation of parsed HTTP request. // - `path_` is the URL path +// - `accessToken_` is the access token for that request // - `parameters_` is a hashmap of the parameters // - `operation_` the operation that should be performed struct ParsedRequest { std::string path_; + std::optional accessToken_; ParamValueMap parameters_; std::variant diff --git a/test/ServerTest.cpp b/test/ServerTest.cpp index dd7d776da7..0319b138d5 100644 --- a/test/ServerTest.cpp +++ b/test/ServerTest.cpp @@ -21,11 +21,14 @@ using namespace ad_utility::testing; namespace { auto ParsedRequestIs = [](const std::string& path, + const std::optional& accessToken, const ParamValueMap& parameters, const std::variant& operation) -> testing::Matcher { return testing::AllOf( AD_FIELD(ad_utility::url_parser::ParsedRequest, path_, testing::Eq(path)), + AD_FIELD(ad_utility::url_parser::ParsedRequest, accessToken_, + testing::Eq(accessToken)), AD_FIELD(ad_utility::url_parser::ParsedRequest, parameters_, testing::ContainerEq(parameters)), AD_FIELD(ad_utility::url_parser::ParsedRequest, operation_, @@ -41,19 +44,20 @@ TEST(ServerTest, parseHttpRequest) { "application/x-www-form-urlencoded;charset=UTF-8"; const std::string QUERY = "application/sparql-query"; const std::string UPDATE = "application/sparql-update"; - EXPECT_THAT(parse(makeGetRequest("/")), ParsedRequestIs("/", {}, None{})); + EXPECT_THAT(parse(makeGetRequest("/")), + ParsedRequestIs("/", std::nullopt, {}, None{})); EXPECT_THAT(parse(makeGetRequest("/ping")), - ParsedRequestIs("/ping", {}, None{})); + ParsedRequestIs("/ping", std::nullopt, {}, None{})); EXPECT_THAT(parse(makeGetRequest("/?cmd=stats")), - ParsedRequestIs("/", {{"cmd", {"stats"}}}, None{})); + ParsedRequestIs("/", std::nullopt, {{"cmd", {"stats"}}}, None{})); EXPECT_THAT(parse(makeGetRequest( "/?query=SELECT+%2A%20WHERE%20%7B%7D&action=csv_export")), - ParsedRequestIs("/", {{"action", {"csv_export"}}}, + ParsedRequestIs("/", std::nullopt, {{"action", {"csv_export"}}}, Query{"SELECT * WHERE {}", {}})); EXPECT_THAT( parse(makePostRequest("/", URLENCODED, "query=SELECT+%2A%20WHERE%20%7B%7D&send=100")), - ParsedRequestIs("/", {{"send", {"100"}}}, + ParsedRequestIs("/", std::nullopt, {{"send", {"100"}}}, Query{"SELECT * WHERE {}", {}})); AD_EXPECT_THROW_WITH_MESSAGE( parse(makePostRequest("/", URLENCODED, @@ -79,11 +83,12 @@ TEST(ServerTest, parseHttpRequest) { EXPECT_THAT( parse(makePostRequest("/", "application/x-www-form-urlencoded", "query=SELECT%20%2A%20WHERE%20%7B%7D&send=100")), - ParsedRequestIs("/", {{"send", {"100"}}}, + ParsedRequestIs("/", std::nullopt, {{"send", {"100"}}}, Query{"SELECT * WHERE {}", {}})); - EXPECT_THAT(parse(makePostRequest("/", URLENCODED, - "query=SELECT%20%2A%20WHERE%20%7B%7D")), - ParsedRequestIs("/", {}, Query{"SELECT * WHERE {}", {}})); + EXPECT_THAT( + parse(makePostRequest("/", URLENCODED, + "query=SELECT%20%2A%20WHERE%20%7B%7D")), + ParsedRequestIs("/", std::nullopt, {}, Query{"SELECT * WHERE {}", {}})); auto Iri = ad_utility::triple_component::Iri::fromIriref; EXPECT_THAT( parse(makePostRequest( @@ -92,7 +97,7 @@ TEST(ServerTest, parseHttpRequest) { "2Fw3.org%2Fdefault&named-graph-uri=https%3A%2F%2Fw3.org%2F1&named-" "graph-uri=https%3A%2F%2Fw3.org%2F2")), ParsedRequestIs( - "/", + "/", std::nullopt, {{"default-graph-uri", {"https://w3.org/default"}}, {"named-graph-uri", {"https://w3.org/1", "https://w3.org/2"}}}, Query{"SELECT * WHERE {}", @@ -105,12 +110,14 @@ TEST(ServerTest, parseHttpRequest) { "query=SELECT%20%2A%20WHERE%20%7B%7D")), testing::StrEq("URL-encoded POST requests must not contain query " "parameters in the URL.")); - EXPECT_THAT(parse(makePostRequest("/", URLENCODED, "cmd=clear-cache")), - ParsedRequestIs("/", {{"cmd", {"clear-cache"}}}, None{})); - EXPECT_THAT(parse(makePostRequest("/", QUERY, "SELECT * WHERE {}")), - ParsedRequestIs("/", {}, Query{"SELECT * WHERE {}", {}})); + EXPECT_THAT( + parse(makePostRequest("/", URLENCODED, "cmd=clear-cache")), + ParsedRequestIs("/", std::nullopt, {{"cmd", {"clear-cache"}}}, None{})); + EXPECT_THAT( + parse(makePostRequest("/", QUERY, "SELECT * WHERE {}")), + ParsedRequestIs("/", std::nullopt, {}, Query{"SELECT * WHERE {}", {}})); EXPECT_THAT(parse(makePostRequest("/?send=100", QUERY, "SELECT * WHERE {}")), - ParsedRequestIs("/", {{"send", {"100"}}}, + ParsedRequestIs("/", std::nullopt, {{"send", {"100"}}}, Query{"SELECT * WHERE {}", {}})); AD_EXPECT_THROW_WITH_MESSAGE( parse(makeRequest(http::verb::patch, "/")), @@ -125,20 +132,23 @@ TEST(ServerTest, parseHttpRequest) { AD_EXPECT_THROW_WITH_MESSAGE( parse(makeGetRequest("/?update=DELETE%20%2A%20WHERE%20%7B%7D")), testing::StrEq("SPARQL Update is not allowed as GET request.")); - EXPECT_THAT(parse(makePostRequest("/", UPDATE, "DELETE * WHERE {}")), - ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}", {}})); - EXPECT_THAT(parse(makePostRequest("/", URLENCODED, - "update=DELETE%20%2A%20WHERE%20%7B%7D")), - ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}", {}})); - EXPECT_THAT(parse(makePostRequest("/", URLENCODED, - "update=DELETE+%2A+WHERE%20%7B%7D")), - ParsedRequestIs("/", {}, Update{"DELETE * WHERE {}", {}})); + EXPECT_THAT( + parse(makePostRequest("/", UPDATE, "DELETE * WHERE {}")), + ParsedRequestIs("/", std::nullopt, {}, Update{"DELETE * WHERE {}", {}})); + EXPECT_THAT( + parse(makePostRequest("/", URLENCODED, + "update=DELETE%20%2A%20WHERE%20%7B%7D")), + ParsedRequestIs("/", std::nullopt, {}, Update{"DELETE * WHERE {}", {}})); + EXPECT_THAT( + parse( + makePostRequest("/", URLENCODED, "update=DELETE+%2A+WHERE%20%7B%7D")), + ParsedRequestIs("/", std::nullopt, {}, Update{"DELETE * WHERE {}", {}})); // Check that the correct datasets for the method (GET or POST) are added EXPECT_THAT( parse(makeGetRequest("/?query=SELECT%20%2A%20WHERE%20%7B%7D&default-" "graph-uri=foo&named-graph-uri=bar&using-graph-uri=" "baz&using-named-graph-uri=cat")), - ParsedRequestIs("/", + ParsedRequestIs("/", std::nullopt, {{"default-graph-uri", {"foo"}}, {"named-graph-uri", {"bar"}}, {"using-graph-uri", {"baz"}}, @@ -151,7 +161,7 @@ TEST(ServerTest, parseHttpRequest) { "graph-uri=foo&named-graph-uri=bar&using-graph-uri=" "baz&using-named-graph-uri=cat", QUERY, "SELECT * WHERE {}")), - ParsedRequestIs("/", + ParsedRequestIs("/", std::nullopt, {{"default-graph-uri", {"foo"}}, {"named-graph-uri", {"bar"}}, {"using-graph-uri", {"baz"}}, @@ -164,7 +174,7 @@ TEST(ServerTest, parseHttpRequest) { "query=SELECT%20%2A%20WHERE%20%7B%7D&default-graph-" "uri=foo&named-graph-uri=bar&using-graph-uri=baz&" "using-named-graph-uri=cat")), - ParsedRequestIs("/", + ParsedRequestIs("/", std::nullopt, {{"default-graph-uri", {"foo"}}, {"named-graph-uri", {"bar"}}, {"using-graph-uri", {"baz"}}, @@ -177,7 +187,7 @@ TEST(ServerTest, parseHttpRequest) { "update=INSERT%20DATA%20%7B%7D&default-graph-uri=" "foo&named-graph-uri=bar&using-graph-uri=baz&" "using-named-graph-uri=cat")), - ParsedRequestIs("/", + ParsedRequestIs("/", std::nullopt, { {"default-graph-uri", {"foo"}}, {"named-graph-uri", {"bar"}}, @@ -192,7 +202,7 @@ TEST(ServerTest, parseHttpRequest) { "/?default-graph-uri=foo&named-graph-uri=bar&using-graph-uri=baz&" "using-named-graph-uri=cat", UPDATE, "INSERT DATA {}")), - ParsedRequestIs("/", + ParsedRequestIs("/", std::nullopt, { {"default-graph-uri", {"foo"}}, {"named-graph-uri", {"bar"}}, @@ -202,6 +212,109 @@ TEST(ServerTest, parseHttpRequest) { Update{"INSERT DATA {}", {DatasetClause{Iri(""), false}, DatasetClause{Iri(""), true}}})); + auto testAccessTokenCombinations = + [&](const http::verb& method, std::string_view pathBase, + const std::variant& expectedOperation, + const ad_utility::HashMap& headers = {}, + const std::optional& body = std::nullopt, + ad_utility::source_location l = + ad_utility::source_location::current()) { + auto t = generateLocationTrace(l); + // Test the cases: + // 1. No access token + // 2. Access token in query + // 3. Access token in `Authorization` header + // 4. Different access tokens + // 5. Same access token + boost::urls::url pathWithAccessToken{pathBase}; + pathWithAccessToken.params().append({"access-token", "foo"}); + ad_utility::HashMap + headersWithDifferentAccessToken{headers}; + headersWithDifferentAccessToken.insert( + {http::field::authorization, "Bearer bar"}); + ad_utility::HashMap + headersWithSameAccessToken{headers}; + headersWithSameAccessToken.insert( + {http::field::authorization, "Bearer foo"}); + EXPECT_THAT(parse(makeRequest(method, pathBase, headers, body)), + ParsedRequestIs("/", std::nullopt, {}, expectedOperation)); + EXPECT_THAT(parse(makeRequest(method, pathWithAccessToken.buffer(), + headers, body)), + ParsedRequestIs("/", "foo", {{"access-token", {"foo"}}}, + expectedOperation)); + EXPECT_THAT(parse(makeRequest(method, pathBase, + headersWithDifferentAccessToken, body)), + ParsedRequestIs("/", "bar", {}, expectedOperation)); + EXPECT_THAT(parse(makeRequest(method, pathWithAccessToken.buffer(), + headersWithSameAccessToken, body)), + ParsedRequestIs("/", "foo", {{"access-token", {"foo"}}}, + expectedOperation)); + AD_EXPECT_THROW_WITH_MESSAGE( + parse(makeRequest(method, pathWithAccessToken.buffer(), + headersWithDifferentAccessToken, body)), + testing::HasSubstr( + "Access token is specified both in the " + "`Authorization` header and by the `access-token` " + "parameter, but they are not the same")); + }; + testAccessTokenCombinations(http::verb::get, "/?query=a", Query{"a", {}}); + testAccessTokenCombinations(http::verb::post, "/", Query{"a", {}}, + {{http::field::content_type, QUERY}}, "a"); + testAccessTokenCombinations(http::verb::post, "/", Update{"a", {}}, + {{http::field::content_type, UPDATE}}, "a"); + auto testAccessTokenCombinationsUrlEncoded = + [&](const std::string& bodyBase, + const std::variant& expectedOperation, + ad_utility::source_location l = + ad_utility::source_location::current()) { + auto t = generateLocationTrace(l); + // Test the cases: + // 1. No access token + // 2. Access token in query + // 3. Access token in `Authorization` header + // 4. Different access tokens + // 5. Same access token + boost::urls::url paramsWithAccessToken{absl::StrCat("/?", bodyBase)}; + paramsWithAccessToken.params().append({"access-token", "foo"}); + std::string bodyWithAccessToken{ + paramsWithAccessToken.encoded_params().buffer()}; + ad_utility::HashMap headers{ + {http::field::content_type, {URLENCODED}}}; + ad_utility::HashMap + headersWithDifferentAccessToken{ + {http::field::content_type, {URLENCODED}}, + {http::field::authorization, "Bearer bar"}}; + ad_utility::HashMap + headersWithSameAccessToken{ + {http::field::content_type, {URLENCODED}}, + {http::field::authorization, "Bearer foo"}}; + EXPECT_THAT( + parse(makeRequest(http::verb::post, "/", headers, bodyBase)), + ParsedRequestIs("/", std::nullopt, {}, expectedOperation)); + EXPECT_THAT(parse(makeRequest(http::verb::post, "/", headers, + bodyWithAccessToken)), + ParsedRequestIs("/", "foo", {{"access-token", {"foo"}}}, + expectedOperation)); + EXPECT_THAT( + parse(makeRequest(http::verb::post, "/", + headersWithDifferentAccessToken, bodyBase)), + ParsedRequestIs("/", "bar", {}, expectedOperation)); + EXPECT_THAT(parse(makeRequest(http::verb::post, "/", + headersWithSameAccessToken, bodyBase)), + ParsedRequestIs("/", "foo", {}, expectedOperation)); + AD_EXPECT_THROW_WITH_MESSAGE( + parse(makeRequest(http::verb::post, "/", + headersWithDifferentAccessToken, + bodyWithAccessToken)), + testing::HasSubstr( + "Access token is specified both in the " + "`Authorization` header and by the `access-token` " + "parameter, but they are not the same")); + }; + testAccessTokenCombinationsUrlEncoded("query=SELECT%20%2A%20WHERE%20%7B%7D", + Query{"SELECT * WHERE {}", {}}); + testAccessTokenCombinationsUrlEncoded("update=DELETE%20WHERE%20%7B%7D", + Update{"DELETE WHERE {}", {}}); } TEST(ServerTest, determineResultPinning) { @@ -370,3 +483,47 @@ TEST(ServerTest, createResponseMetadata) { EXPECT_THAT(metadata["delta-triples"], testing::Eq(deltaTriplesJson)); EXPECT_THAT(metadata["located-triples"], testing::Eq(locatedTriplesJson)); } + +TEST(ServerTest, extractAccessToken) { + auto extract = [](const ad_utility::httpUtils::HttpRequest auto& request) { + auto parsedUrl = parseRequestTarget(request.target()); + return Server::extractAccessToken(request, parsedUrl.parameters_); + }; + EXPECT_THAT(extract(makeGetRequest("/")), testing::Eq(std::nullopt)); + EXPECT_THAT(extract(makeGetRequest("/?access-token=foo")), + testing::Optional(testing::Eq("foo"))); + EXPECT_THAT( + extract(makeRequest(http::verb::get, "/", + {{http::field::authorization, "Bearer foo"}})), + testing::Optional(testing::Eq("foo"))); + EXPECT_THAT( + extract(makeRequest(http::verb::get, "/?access-token=foo", + {{http::field::authorization, "Bearer foo"}})), + testing::Optional(testing::Eq("foo"))); + AD_EXPECT_THROW_WITH_MESSAGE( + extract(makeRequest(http::verb::get, "/?access-token=bar", + {{http::field::authorization, "Bearer foo"}})), + testing::HasSubstr( + "Access token is specified both in the `Authorization` header and by " + "the `access-token` parameter, but they are not the same")); + AD_EXPECT_THROW_WITH_MESSAGE( + extract(makeRequest(http::verb::get, "/", + {{http::field::authorization, "foo"}})), + testing::HasSubstr( + "Authorization header doesn't start with \"Bearer \".")); + EXPECT_THAT(extract(makePostRequest("/", "text/turtle", "")), + testing::Eq(std::nullopt)); + EXPECT_THAT(extract(makePostRequest("/?access-token=foo", "text/turtle", "")), + testing::Optional(testing::Eq("foo"))); + AD_EXPECT_THROW_WITH_MESSAGE( + extract(makeRequest(http::verb::post, "/?access-token=bar", + {{http::field::authorization, "Bearer foo"}})), + testing::HasSubstr( + "Access token is specified both in the `Authorization` header and by " + "the `access-token` parameter, but they are not the same")); + AD_EXPECT_THROW_WITH_MESSAGE( + extract(makeRequest(http::verb::post, "/?access-token=bar", + {{http::field::authorization, "foo"}})), + testing::HasSubstr( + "Authorization header doesn't start with \"Bearer \".")); +} diff --git a/test/util/HttpRequestHelpers.h b/test/util/HttpRequestHelpers.h index 4505e828db..cb57ead971 100644 --- a/test/util/HttpRequestHelpers.h +++ b/test/util/HttpRequestHelpers.h @@ -14,7 +14,8 @@ namespace http = boost::beast::http; // Construct a boost::beast request with the HTTP method, target path, headers // and body. inline auto makeRequest( - const http::verb method = http::verb::get, const std::string& target = "/", + const http::verb method = http::verb::get, + const std::string_view target = "/", const ad_utility::HashMap& headers = {}, const std::optional& body = std::nullopt) { // version 11 stands for HTTP/1.1 From f2562fe2ca1da7e1704aa2a5b0473be08e5f296c Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Fri, 31 Jan 2025 02:27:50 +0100 Subject: [PATCH 15/28] Don't use the exact size from the cache for query planning (#1736) So far, when a part of a query was in the cache, the query planner used the exact size as cost estimate. This sounds natural because the exact size is by definition the best estimate. However, under special circumstances, this might lead to poor query plans when the other estimates are off (it's not important that the individual estimates are good, it's important that together they lead to a reasonable query plan). NOTE: This is a trivial change. A bigger issue that we might want to address in the future is that it might not be optimal to compute the average multiplicity of a column as the average multiplicity of it's distinct elements. Instead the average of the squares might be more appropriate (note that the sum of the squares of the individual multiplicities is exactly the size of the result one would get when joining that column with itself). --- src/engine/QueryExecutionTree.cpp | 14 +++++--------- test/OperationTest.cpp | 6 +++--- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index 9b58d0bb78..49ceb340aa 100644 --- a/src/engine/QueryExecutionTree.cpp +++ b/src/engine/QueryExecutionTree.cpp @@ -91,15 +91,11 @@ size_t QueryExecutionTree::getCostEstimate() { // _____________________________________________________________________________ size_t QueryExecutionTree::getSizeEstimate() { if (!sizeEstimate_.has_value()) { - if (cachedResult_) { - AD_CORRECTNESS_CHECK(cachedResult_->isFullyMaterialized()); - sizeEstimate_ = cachedResult_->idTable().size(); - } else { - // if we are in a unit test setting and there is no QueryExecutionContest - // specified it is the rootOperation_'s obligation to handle this case - // correctly - sizeEstimate_ = rootOperation_->getSizeEstimate(); - } + // Note: Previously we used the exact size instead of the estimate for + // results that were already in the cache. This however often lead to poor + // planning, because the query planner compared exact sizes with estimates, + // which lead to worse plans than just conistently choosing the estimate. + sizeEstimate_ = rootOperation_->getSizeEstimate(); } return sizeEstimate_.value(); } diff --git a/test/OperationTest.cpp b/test/OperationTest.cpp index 2afbe38a83..e39b9312e0 100644 --- a/test/OperationTest.cpp +++ b/test/OperationTest.cpp @@ -242,12 +242,12 @@ TEST(OperationTest, estimatesForCachedResults) { [[maybe_unused]] auto res = qet->getResult(); } // The result is now cached inside the static execution context, if we create - // the same operation again, the cost estimate is 0 and the size estimate is - // exact (3 rows). + // the same operation again, the cost estimate is 0. The size estimate doesn't + // change (see the `getCostEstimate` function for details on why). { auto qet = makeQet(); EXPECT_EQ(qet->getCacheKey(), qet->getRootOperation()->getCacheKey()); - EXPECT_EQ(qet->getSizeEstimate(), 3u); + EXPECT_EQ(qet->getSizeEstimate(), 24u); EXPECT_EQ(qet->getCostEstimate(), 0u); } } From 50c1f0a22d1f26ac3409de5bcd7bb25274654c53 Mon Sep 17 00:00:00 2001 From: Julian <14220769+Qup42@users.noreply.github.com> Date: Sat, 1 Feb 2025 21:25:18 +0100 Subject: [PATCH 16/28] Allow `;` after update request with a single operation (#1750) QLever does not yet support update requests with more than one operation, e.g., `DELETE DATA { 3.14 }; INSERT DATA { 3.1459 }`. Due to an oversight, the current implementation also did not support single operations followed by a semicolon, e.g., `DELETE DATA { 3.14 };`. These are now supported. Fixes #1747 NOTE: Support for multiple operations in a single update request will be added soon. --- .../sparqlParser/SparqlQleverVisitor.cpp | 6 +++-- test/SparqlAntlrParserTest.cpp | 25 ++++++++++++++++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index e22454cfd7..9df20dd5ba 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -438,7 +438,7 @@ std::optional Visitor::visit(Parser::ValuesClauseContext* ctx) { return visitOptional(ctx->dataBlock()); } -// ____________________________________________________________________________________ +// ____________________________________________________________________________ ParsedQuery Visitor::visit(Parser::UpdateContext* ctx) { // The prologue (BASE and PREFIX declarations) only affects the internal // state of the visitor. @@ -446,7 +446,9 @@ ParsedQuery Visitor::visit(Parser::UpdateContext* ctx) { auto update = visit(ctx->update1()); - if (ctx->update()) { + // More than one operation in a single update request is not yet supported, + // but a semicolon after a single update is allowed. + if (ctx->update() && !ctx->update()->getText().empty()) { parsedQuery_ = ParsedQuery{}; reportNotSupported(ctx->update(), "Multiple updates in one query are"); } diff --git a/test/SparqlAntlrParserTest.cpp b/test/SparqlAntlrParserTest.cpp index 0803f96f03..3bd820256e 100644 --- a/test/SparqlAntlrParserTest.cpp +++ b/test/SparqlAntlrParserTest.cpp @@ -2050,10 +2050,12 @@ TEST(SparqlParser, QuadData) { TEST(SparqlParser, Update) { auto expectUpdate_ = ExpectCompleteParse<&Parser::update>{defaultPrefixMap}; // Automatically test all updates for their `_originalString`. - auto expectUpdate = [&expectUpdate_](const std::string& query, - auto&& expected) { - expectUpdate_(query, - testing::AllOf(expected, m::pq::OriginalString(query))); + auto expectUpdate = [&expectUpdate_]( + const std::string& query, auto&& expected, + ad_utility::source_location l = + ad_utility::source_location::current()) { + expectUpdate_(query, testing::AllOf(expected, m::pq::OriginalString(query)), + l); }; auto expectUpdateFails = ExpectParseFails<&Parser::update>{}; auto Iri = [](std::string_view stringWithBrackets) { @@ -2185,6 +2187,21 @@ TEST(SparqlParser, Update) { expectUpdate("COPY DEFAULT TO GRAPH ", m::UpdateClause(m::Copy(false, DEFAULT{}, Iri("")), m::GraphPattern())); + const auto simpleInsertMatcher = m::UpdateClause( + m::GraphUpdate({}, {{Iri(""), Iri(""), Iri(""), noGraph}}, + std::nullopt), + m::GraphPattern()); + expectUpdate("INSERT DATA { }", simpleInsertMatcher); + expectUpdate("INSERT DATA { };", simpleInsertMatcher); + const auto multipleUpdatesError = testing::HasSubstr( + "Not supported: Multiple updates in one query are " + "currently not supported by QLever"); + expectUpdateFails("INSERT DATA { }; PREFIX foo: ", + multipleUpdatesError); + expectUpdateFails("INSERT DATA { }; BASE ", + multipleUpdatesError); + expectUpdateFails("INSERT DATA { }; INSERT DATA { }", + multipleUpdatesError); } TEST(SparqlParser, QueryOrUpdate) { From 5550d1d98d374813ca6be5680a904accaa6f86a7 Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Sat, 1 Feb 2025 22:03:18 +0100 Subject: [PATCH 17/28] Reduce the memory overhead of the `augmentedMetadata` (#1734) The current version of the code makes two copies of the complete block metadata, even when no updates are ever performed. For datasets like OpenStreetMap or UniProt this caused a base RAM consumption of 40 GB+ versus the usual 13 GB. The copies also noticeably slowed down the time needed for starting the server. This is now fixed by storing the block metadata (for each permutation) as a shared pointer and adapting all the functions and interfaces depending on this. As long as there are no updates, the base RAM consumption of the server is now as it used to be (before update was implement). Once there is an update, the block meta data is copied and stored twice (original and augmented) from then on. --- src/index/DeltaTriples.cpp | 2 +- src/index/DeltaTriples.h | 5 +++-- src/index/IndexImpl.cpp | 2 +- src/index/IndexMetaData.h | 11 +++++++---- src/index/IndexMetaDataImpl.h | 4 ++-- src/index/LocatedTriples.cpp | 11 ++++++++--- src/index/LocatedTriples.h | 22 +++++++++++++++++----- 7 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/index/DeltaTriples.cpp b/src/index/DeltaTriples.cpp index ab02e2e2ed..e8d35fa3b3 100644 --- a/src/index/DeltaTriples.cpp +++ b/src/index/DeltaTriples.cpp @@ -260,7 +260,7 @@ SharedLocatedTriplesSnapshot DeltaTriplesManager::getCurrentSnapshot() const { // _____________________________________________________________________________ void DeltaTriples::setOriginalMetadata( Permutation::Enum permutation, - std::vector metadata) { + std::shared_ptr> metadata) { locatedTriples() .at(static_cast(permutation)) .setOriginalMetadata(std::move(metadata)); diff --git a/src/index/DeltaTriples.h b/src/index/DeltaTriples.h index 372cf3ddd8..3a2037768c 100644 --- a/src/index/DeltaTriples.h +++ b/src/index/DeltaTriples.h @@ -163,8 +163,9 @@ class DeltaTriples { // Register the original `metadata` for the given `permutation`. This has to // be called before any updates are processed. - void setOriginalMetadata(Permutation::Enum permutation, - std::vector metadata); + void setOriginalMetadata( + Permutation::Enum permutation, + std::shared_ptr> metadata); private: // Find the position of the given triple in the given permutation and add it diff --git a/src/index/IndexImpl.cpp b/src/index/IndexImpl.cpp index 72efec5307..a49d4169c2 100644 --- a/src/index/IndexImpl.cpp +++ b/src/index/IndexImpl.cpp @@ -894,7 +894,7 @@ void IndexImpl::createFromOnDiskIndex(const string& onDiskBase) { auto setMetadata = [this](const Permutation& p) { deltaTriplesManager().modify([&p](DeltaTriples& deltaTriples) { deltaTriples.setOriginalMetadata(p.permutation(), - p.metaData().blockData()); + p.metaData().blockDataShared()); }); }; diff --git a/src/index/IndexMetaData.h b/src/index/IndexMetaData.h index 7b865fafa7..030cdad07c 100644 --- a/src/index/IndexMetaData.h +++ b/src/index/IndexMetaData.h @@ -89,7 +89,7 @@ class IndexMetaData { // For each relation, its meta data. MapType data_; // For each compressed block, its meta data. - BlocksType blockData_; + std::shared_ptr blockData_ = std::make_shared(); size_t totalElements_ = 0; size_t numDistinctCol0_ = 0; @@ -175,8 +175,11 @@ class IndexMetaData { const MapType& data() const { return data_; } - BlocksType& blockData() { return blockData_; } - const BlocksType& blockData() const { return blockData_; } + BlocksType& blockData() { return *blockData_; } + const BlocksType& blockData() const { return *blockData_; } + std::shared_ptr blockDataShared() const { + return blockData_; + } // Symmetric serialization function for the ad_utility::serialization module. AD_SERIALIZE_FRIEND_FUNCTION(IndexMetaData) { @@ -207,7 +210,7 @@ class IndexMetaData { // Serialize the rest of the data members serializer | arg.name_; serializer | arg.data_; - serializer | arg.blockData_; + serializer | arg.blockData(); serializer | arg.offsetAfter_; serializer | arg.totalElements_; serializer | arg.numDistinctCol0_; diff --git a/src/index/IndexMetaDataImpl.h b/src/index/IndexMetaDataImpl.h index d136f6d7e6..4931e1d183 100644 --- a/src/index/IndexMetaDataImpl.h +++ b/src/index/IndexMetaDataImpl.h @@ -96,7 +96,7 @@ string IndexMetaData::statistics() const { std::locale locWithNumberGrouping(loc, &facet); os.imbue(locWithNumberGrouping); os << "#relations = " << numDistinctCol0_ - << ", #blocks = " << blockData_.size() + << ", #blocks = " << blockData().size() << ", #triples = " << totalElements_; return std::move(os).str(); } @@ -106,7 +106,7 @@ template void IndexMetaData::calculateStatistics(size_t numDistinctCol0) { totalElements_ = 0; numDistinctCol0_ = numDistinctCol0; - for (const auto& block : blockData_) { + for (const auto& block : blockData()) { totalElements_ += block.numRows_; } } diff --git a/src/index/LocatedTriples.cpp b/src/index/LocatedTriples.cpp index c8d977f473..f689546b5a 100644 --- a/src/index/LocatedTriples.cpp +++ b/src/index/LocatedTriples.cpp @@ -245,9 +245,8 @@ void LocatedTriplesPerBlock::erase(size_t blockIndex, // ____________________________________________________________________________ void LocatedTriplesPerBlock::setOriginalMetadata( - std::vector metadata) { + std::shared_ptr> metadata) { originalMetadata_ = std::move(metadata); - updateAugmentedMetadata(); } // Update the `blockMetadata`, such that its graph info is consistent with the @@ -297,7 +296,13 @@ void LocatedTriplesPerBlock::updateAugmentedMetadata() { // TODO use view::enumerate size_t blockIndex = 0; // Copy to preserve originalMetadata_. - augmentedMetadata_ = originalMetadata_; + if (!originalMetadata_.has_value()) { + AD_LOG_WARN << "The original metadata has not been set, but updates are " + "being performed. This should only happen in unit tests\n"; + augmentedMetadata_.emplace(); + } else { + augmentedMetadata_ = *originalMetadata_.value(); + } for (auto& blockMetadata : augmentedMetadata_.value()) { if (hasUpdates(blockIndex)) { const auto& blockUpdates = map_.at(blockIndex); diff --git a/src/index/LocatedTriples.h b/src/index/LocatedTriples.h index 0280ce8cc6..2b3815a953 100644 --- a/src/index/LocatedTriples.h +++ b/src/index/LocatedTriples.h @@ -6,6 +6,8 @@ #pragma once +#include + #include "engine/idTable/IdTable.h" #include "global/IdTriple.h" #include "index/CompressedRelation.h" @@ -89,7 +91,8 @@ class LocatedTriplesPerBlock { // Stores the block metadata where the block borders have been adjusted for // the updated triples. std::optional> augmentedMetadata_; - std::vector originalMetadata_; + std::optional>> + originalMetadata_; public: void updateAugmentedMetadata(); @@ -156,21 +159,30 @@ class LocatedTriplesPerBlock { // Must be called initially before using the `LocatedTriplesPerBlock` to // initialize the original block metadata that is augmented for updated // triples. This is currently done in `Permutation::loadFromDisk`. - void setOriginalMetadata(std::vector metadata); + void setOriginalMetadata( + std::shared_ptr> metadata); + void setOriginalMetadata(std::vector metadata) { + setOriginalMetadata( + std::make_shared>( + std::move(metadata))); + } // Returns the block metadata where the block borders have been updated to // account for the update triples. All triples (both insert and delete) will // enlarge the block borders. const std::vector& getAugmentedMetadata() const { - AD_CONTRACT_CHECK(augmentedMetadata_.has_value()); - return augmentedMetadata_.value(); + if (augmentedMetadata_.has_value()) { + return augmentedMetadata_.value(); + } + AD_CONTRACT_CHECK(originalMetadata_.has_value()); + return *originalMetadata_.value(); }; // Remove all located triples. void clear() { map_.clear(); numTriples_ = 0; - augmentedMetadata_ = originalMetadata_; + augmentedMetadata_.reset(); } // Return `true` iff the given triple is one of the located triples with the From 5a2a35446fcb8a893b186e0d6d5805bf1333da9e Mon Sep 17 00:00:00 2001 From: krzysztof-tyb Date: Mon, 3 Feb 2025 20:19:36 +0100 Subject: [PATCH 18/28] Backport C++20 concepts to C++17 in the `index` module (#1748) The backport is (as usual) implemented using the `CPP_template` etc. macros from Eric Niebler's `range-v3` library. --- src/index/IndexImpl.cpp | 41 +++++++------- src/index/IndexImpl.h | 37 ++++++------- src/index/LocatedTriples.cpp | 12 ++--- src/index/TextIndexReadWrite.cpp | 3 +- src/index/TextIndexReadWrite.h | 4 +- src/index/VocabularyMerger.h | 51 ++++++++++-------- src/index/VocabularyMergerImpl.h | 40 ++++++++------ src/index/vocabulary/CompressedVocabulary.h | 33 +++++++++--- src/index/vocabulary/CompressionWrappers.h | 53 ++++++++++--------- .../vocabulary/CompressedVocabularyTest.cpp | 6 ++- 10 files changed, 160 insertions(+), 120 deletions(-) diff --git a/src/index/IndexImpl.cpp b/src/index/IndexImpl.cpp index a49d4169c2..f11cab5b87 100644 --- a/src/index/IndexImpl.cpp +++ b/src/index/IndexImpl.cpp @@ -1622,12 +1622,12 @@ constexpr auto makeNumDistinctIdsCounter = [](size_t& numDistinctIds) { } // namespace // _____________________________________________________________________________ -template -requires(sizeof...(NextSorter) <= 1) -void IndexImpl::createPSOAndPOSImpl(size_t numColumns, - BlocksOfTriples sortedTriples, - bool doWriteConfiguration, - NextSorter&&... nextSorter) +CPP_template_def(typename... NextSorter)(requires( + sizeof...(NextSorter) <= + 1)) void IndexImpl::createPSOAndPOSImpl(size_t numColumns, + BlocksOfTriples sortedTriples, + bool doWriteConfiguration, + NextSorter&&... nextSorter) { size_t numTriplesNormal = 0; @@ -1654,21 +1654,20 @@ void IndexImpl::createPSOAndPOSImpl(size_t numColumns, }; // _____________________________________________________________________________ -template -requires(sizeof...(NextSorter) <= 1) -void IndexImpl::createPSOAndPOS(size_t numColumns, - BlocksOfTriples sortedTriples, - NextSorter&&... nextSorter) { +CPP_template_def(typename... NextSorter)( + requires(sizeof...(NextSorter) <= + 1)) void IndexImpl::createPSOAndPOS(size_t numColumns, + BlocksOfTriples sortedTriples, + NextSorter&&... nextSorter) { createPSOAndPOSImpl(numColumns, std::move(sortedTriples), true, AD_FWD(nextSorter)...); } // _____________________________________________________________________________ -template -requires(sizeof...(NextSorter) <= 1) -std::optional IndexImpl::createSPOAndSOP( - size_t numColumns, BlocksOfTriples sortedTriples, - NextSorter&&... nextSorter) { +CPP_template_def(typename... NextSorter)(requires(sizeof...(NextSorter) <= 1)) + std::optional IndexImpl::createSPOAndSOP( + size_t numColumns, BlocksOfTriples sortedTriples, + NextSorter&&... nextSorter) { size_t numSubjectsNormal = 0; size_t numSubjectsTotal = 0; auto numSubjectCounter = makeNumDistinctIdsCounter<0>(numSubjectsNormal); @@ -1716,11 +1715,11 @@ std::optional IndexImpl::createSPOAndSOP( }; // _____________________________________________________________________________ -template -requires(sizeof...(NextSorter) <= 1) -void IndexImpl::createOSPAndOPS(size_t numColumns, - BlocksOfTriples sortedTriples, - NextSorter&&... nextSorter) { +CPP_template_def(typename... NextSorter)( + requires(sizeof...(NextSorter) <= + 1)) void IndexImpl::createOSPAndOPS(size_t numColumns, + BlocksOfTriples sortedTriples, + NextSorter&&... nextSorter) { // For the last pair of permutations we don't need a next sorter, so we // have no fourth argument. size_t numObjectsNormal = 0; diff --git a/src/index/IndexImpl.h b/src/index/IndexImpl.h index d284cdb415..8daef8ccd3 100644 --- a/src/index/IndexImpl.h +++ b/src/index/IndexImpl.h @@ -12,6 +12,7 @@ #include #include +#include "backports/algorithm.h" #include "engine/Result.h" #include "engine/idTable/CompressedExternalIdTable.h" #include "global/Pattern.h" @@ -678,34 +679,34 @@ class IndexImpl { // Create the SPO and SOP permutations. Additionally, count the number of // distinct actual (not internal) subjects in the input and write it to the // metadata. Also builds the patterns if specified. - template - requires(sizeof...(NextSorter) <= 1) - std::optional createSPOAndSOP( - size_t numColumns, BlocksOfTriples sortedTriples, - NextSorter&&... nextSorter); + CPP_template(typename... NextSorter)(requires(sizeof...(NextSorter) <= 1)) + std::optional createSPOAndSOP( + size_t numColumns, BlocksOfTriples sortedTriples, + NextSorter&&... nextSorter); // Create the OSP and OPS permutations. Additionally, count the number of // distinct objects and write it to the metadata. - template - requires(sizeof...(NextSorter) <= 1) - void createOSPAndOPS(size_t numColumns, BlocksOfTriples sortedTriples, - NextSorter&&... nextSorter); + CPP_template(typename... NextSorter)(requires( + sizeof...(NextSorter) <= + 1)) void createOSPAndOPS(size_t numColumns, BlocksOfTriples sortedTriples, + NextSorter&&... nextSorter); // Create the PSO and POS permutations. Additionally, count the number of // distinct predicates and the number of actual triples and write them to the // metadata. The meta-data JSON file for the index statistics will only be // written iff `doWriteConfiguration` is true. That parameter is set to // `false` when building the additional permutations for the internal triples. - template - requires(sizeof...(NextSorter) <= 1) - void createPSOAndPOSImpl(size_t numColumns, BlocksOfTriples sortedTriples, - bool doWriteConfiguration, - NextSorter&&... nextSorter); + CPP_template(typename... NextSorter)( + requires(sizeof...(NextSorter) <= + 1)) void createPSOAndPOSImpl(size_t numColumns, + BlocksOfTriples sortedTriples, + bool doWriteConfiguration, + NextSorter&&... nextSorter); // Call `createPSOAndPOSImpl` with the given arguments and with // `doWriteConfiguration` set to `true` (see above). - template - requires(sizeof...(NextSorter) <= 1) - void createPSOAndPOS(size_t numColumns, BlocksOfTriples sortedTriples, - NextSorter&&... nextSorter); + CPP_template(typename... NextSorter)(requires( + sizeof...(NextSorter) <= + 1)) void createPSOAndPOS(size_t numColumns, BlocksOfTriples sortedTriples, + NextSorter&&... nextSorter); // Create the internal PSO and POS permutations from the sorted internal // triples. Return `(numInternalTriples, numInternalPredicates)`. diff --git a/src/index/LocatedTriples.cpp b/src/index/LocatedTriples.cpp index f689546b5a..d70646db2e 100644 --- a/src/index/LocatedTriples.cpp +++ b/src/index/LocatedTriples.cpp @@ -61,9 +61,9 @@ NumAddedAndDeleted LocatedTriplesPerBlock::numTriples(size_t blockIndex) const { // `numIndexColumns` and `includeGraphColumn`. For example, if `numIndexColumns` // is `2` and `includeGraphColumn` is `true`, the function returns // `std::tie(row[0], row[1], row[2])`. -template -requires(numIndexColumns >= 1 && numIndexColumns <= 3) -auto tieIdTableRow(auto& row) { +CPP_template(size_t numIndexColumns, bool includeGraphColumn)( + requires(numIndexColumns >= 1 && + numIndexColumns <= 3)) auto tieIdTableRow(auto& row) { return [&row](std::index_sequence) { return std::tie(row[I]...); }(std::make_index_sequencetriple_`. -template -requires(numIndexColumns >= 1 && numIndexColumns <= 3) -auto tieLocatedTriple(auto& lt) { +CPP_template(size_t numIndexColumns, bool includeGraphColumn)( + requires(numIndexColumns >= 1 && + numIndexColumns <= 3)) auto tieLocatedTriple(auto& lt) { constexpr auto indices = []() { std::array(includeGraphColumn)> diff --git a/src/index/TextIndexReadWrite.cpp b/src/index/TextIndexReadWrite.cpp index 3079aa182d..d7af6320e3 100644 --- a/src/index/TextIndexReadWrite.cpp +++ b/src/index/TextIndexReadWrite.cpp @@ -132,7 +132,7 @@ void FrequencyEncode::writeToFile(ad_utility::File& out, // ____________________________________________________________________________ template -requires std::is_arithmetic_v template +template void GapEncode::initialize(View&& view) { if (ql::ranges::empty(view)) { return; @@ -144,7 +144,6 @@ void GapEncode::initialize(View&& view) { // ____________________________________________________________________________ template -requires std::is_arithmetic_v void GapEncode::writeToFile(ad_utility::File& out, off_t& currentOffset) { textIndexReadWrite::encodeAndWriteSpanAndMoveOffset(encodedVector_, out, currentOffset); diff --git a/src/index/TextIndexReadWrite.h b/src/index/TextIndexReadWrite.h index 9860f4de0b..8ce075a347 100644 --- a/src/index/TextIndexReadWrite.h +++ b/src/index/TextIndexReadWrite.h @@ -317,7 +317,9 @@ FrequencyEncode(View&& view) * a file. */ template -requires std::is_arithmetic_v class GapEncode { +class GapEncode { + static_assert(std::is_arithmetic_v); + public: using TypedVector = std::vector; diff --git a/src/index/VocabularyMerger.h b/src/index/VocabularyMerger.h index 8b9322796c..10b2121c97 100644 --- a/src/index/VocabularyMerger.h +++ b/src/index/VocabularyMerger.h @@ -6,6 +6,7 @@ #include #include +#include "backports/algorithm.h" #include "engine/idTable/CompressedExternalIdTable.h" #include "global/Constants.h" #include "global/Id.h" @@ -27,10 +28,11 @@ namespace ad_utility::vocabulary_merger { // If the `bool` is true, then the word is to be stored in the external // vocabulary else in the internal vocabulary. template -concept WordCallback = std::invocable; +CPP_concept WordCallback = std::invocable; // Concept for a callable that compares to `string_view`s. template -concept WordComparator = std::predicate; +CPP_concept WordComparator = + std::predicate; // The result of a call to `mergeVocabulary` (see below). struct VocabularyMetaData { @@ -141,10 +143,11 @@ struct VocabularyMetaData { // strings (case-sensitive or not). Argument `wordCallback` // is called for each merged word in the vocabulary in the order of their // appearance. -VocabularyMetaData mergeVocabulary(const std::string& basename, size_t numFiles, - WordComparator auto comparator, - WordCallback auto& wordCallback, - ad_utility::MemorySize memoryToUse); +template +auto mergeVocabulary(const std::string& basename, size_t numFiles, W comparator, + C& wordCallback, ad_utility::MemorySize memoryToUse) + -> CPP_ret(VocabularyMetaData)( + requires WordComparator&& WordCallback); // A helper class that implements the `mergeVocabulary` function (see // above). Everything in this class is private and only the @@ -162,21 +165,23 @@ class VocabularyMerger { const size_t bufferSize_ = BATCH_SIZE_VOCABULARY_MERGE; // Friend declaration for the publicly available function. - friend VocabularyMetaData mergeVocabulary(const std::string& basename, - size_t numFiles, - WordComparator auto comparator, - WordCallback auto& wordCallback, - ad_utility::MemorySize memoryToUse); + template + friend auto mergeVocabulary(const std::string& basename, size_t numFiles, + W comparator, C& wordCallback, + ad_utility::MemorySize memoryToUse) + -> CPP_ret(VocabularyMetaData)( + requires WordComparator&& WordCallback); VocabularyMerger() = default; // _______________________________________________________________ // The function that performs the actual merge. See the static global // `mergeVocabulary` function for details. - VocabularyMetaData mergeVocabulary(const std::string& basename, - size_t numFiles, - WordComparator auto comparator, - WordCallback auto& wordCallback, - ad_utility::MemorySize memoryToUse); + template + auto mergeVocabulary(const std::string& basename, size_t numFiles, + W comparator, C& wordCallback, + ad_utility::MemorySize memoryToUse) + -> CPP_ret(VocabularyMetaData)( + requires WordComparator&& WordCallback); // Helper `struct` for a word from a partial vocabulary. struct QueueWord { @@ -205,11 +210,15 @@ class VocabularyMerger { // Write the queue words in the buffer to their corresponding `idPairVecs`. // The `QueueWord`s must be passed in alphabetical order wrt `lessThan` (also // across multiple calls). - void writeQueueWordsToIdVec( - const std::vector& buffer, WordCallback auto& wordCallback, - std::predicate auto const& lessThan, - ad_utility::ProgressBar& progressBar); + // clang-format off + CPP_template(typename C, typename L)( + requires WordCallback CPP_and ranges::predicate< + L, TripleComponentWithIndex, + TripleComponentWithIndex>) + // clang-format on + void writeQueueWordsToIdVec(const std::vector& buffer, + C& wordCallback, const L& lessThan, + ad_utility::ProgressBar& progressBar); // Close all associated files and MmapVectors and reset all internal // variables. diff --git a/src/index/VocabularyMergerImpl.h b/src/index/VocabularyMergerImpl.h index 2b8113daf5..1abd7ac783 100644 --- a/src/index/VocabularyMergerImpl.h +++ b/src/index/VocabularyMergerImpl.h @@ -13,6 +13,7 @@ #include #include +#include "backports/algorithm.h" #include "index/ConstantsIndexBuilding.h" #include "index/Vocabulary.h" #include "index/VocabularyMerger.h" @@ -30,22 +31,25 @@ namespace ad_utility::vocabulary_merger { // _________________________________________________________________ -VocabularyMetaData mergeVocabulary(const std::string& basename, size_t numFiles, - WordComparator auto comparator, - WordCallback auto& internalWordCallback, - ad_utility::MemorySize memoryToUse) { +template +auto mergeVocabulary(const std::string& basename, size_t numFiles, W comparator, + C& internalWordCallback, + ad_utility::MemorySize memoryToUse) + -> CPP_ret(VocabularyMetaData)( + requires WordComparator&& WordCallback) { VocabularyMerger merger; return merger.mergeVocabulary(basename, numFiles, std::move(comparator), internalWordCallback, memoryToUse); } // _________________________________________________________________ +template auto VocabularyMerger::mergeVocabulary(const std::string& basename, - size_t numFiles, - WordComparator auto comparator, - WordCallback auto& wordCallback, + size_t numFiles, W comparator, + C& wordCallback, ad_utility::MemorySize memoryToUse) - -> VocabularyMetaData { + -> CPP_ret(VocabularyMetaData)( + requires WordComparator&& WordCallback) { // Return true iff p1 >= p2 according to the lexicographic order of the IRI // or literal. auto lessThan = [&comparator](const TripleComponentWithIndex& t1, @@ -84,8 +88,8 @@ auto VocabularyMerger::mergeVocabulary(const std::string& basename, std::future writeFuture; - // Some memory (that is hard to measure exactly) is used for the writing of a - // batch of merged words, so we only give 80% of the total memory to the + // Some memory (that is hard to measure exactly) is used for the writing of + // a batch of merged words, so we only give 80% of the total memory to the // merging. This is very approximate and should be investigated in more // detail. auto mergedWords = @@ -111,8 +115,8 @@ auto VocabularyMerger::mergeVocabulary(const std::string& basename, // wait for the last batch LOG(TIMING) << "A new batch of words is ready" << std::endl; - // First wait for the last batch to finish, that way there will be no race - // conditions. + // First wait for the last batch to finish, that way there will be no + // race conditions. if (writeFuture.valid()) { writeFuture.get(); } @@ -139,11 +143,13 @@ auto VocabularyMerger::mergeVocabulary(const std::string& basename, } // ________________________________________________________________________________ -void VocabularyMerger::writeQueueWordsToIdVec( - const std::vector& buffer, WordCallback auto& wordCallback, - std::predicate auto const& lessThan, - ad_utility::ProgressBar& progressBar) { +CPP_template_def(typename C, typename L)( + requires WordCallback CPP_and + ranges::predicate) void VocabularyMerger:: + writeQueueWordsToIdVec(const std::vector& buffer, + C& wordCallback, const L& lessThan, + ad_utility::ProgressBar& progressBar) { LOG(TIMING) << "Start writing a batch of merged words\n"; // Smaller grained buffer for the actual inner write. diff --git a/src/index/vocabulary/CompressedVocabulary.h b/src/index/vocabulary/CompressedVocabulary.h index dad9e84457..cc106375cc 100644 --- a/src/index/vocabulary/CompressedVocabulary.h +++ b/src/index/vocabulary/CompressedVocabulary.h @@ -4,6 +4,7 @@ #pragma once +#include "backports/algorithm.h" #include "index/ConstantsIndexBuilding.h" #include "index/PrefixHeuristic.h" #include "index/vocabulary/CompressionWrappers.h" @@ -15,14 +16,28 @@ #include "util/Serializer/SerializePair.h" #include "util/TaskQueue.h" +namespace detail { + +template +CPP_requires(IterableVocabulary_, + requires(const Vocabulary& vocabulary, + const Iterator& it)(it - vocabulary.begin())); + +template +CPP_concept IterableVocabulary = + CPP_requires_ref(IterableVocabulary_, Vocabulary, Iterator); + +} // namespace detail + // A vocabulary in which compression is performed using a customizable // compression algorithm, with one dictionary per `NumWordsPerBlock` many words // (default 1 million). -template -class CompressedVocabulary { +CPP_template(typename UnderlyingVocabulary, + typename CompressionWrapper = + ad_utility::vocabulary::FsstSquaredCompressionWrapper, + size_t NumWordsPerBlock = 1UL << 20)( + requires ad_utility::vocabulary::CompressionWrapper< + CompressionWrapper>) class CompressedVocabulary { private: UnderlyingVocabulary underlyingVocabulary_; CompressionWrapper compressionWrapper_; @@ -225,7 +240,8 @@ class CompressedVocabulary { static_cast(compressedSize > uncompressedSize); size_t i = 0; for (auto& word : views) { - if constexpr (requires() { underlyingWriter_(word, false); }) { + if constexpr (std::is_invocable_v) { underlyingWriter_(word, isExternalBuffer.at(i)); ++i; } else { @@ -277,9 +293,10 @@ class CompressedVocabulary { // Decompress the word that `it` points to. `it` is an iterator into the // underlying vocabulary. - auto decompressFromIterator(auto it) const { + template + auto decompressFromIterator(It it) const { auto idx = [&]() { - if constexpr (requires() { it - underlyingVocabulary_.begin(); }) { + if constexpr (detail::IterableVocabulary) { return it - underlyingVocabulary_.begin(); } else { return underlyingVocabulary_.iteratorToIndex(it); diff --git a/src/index/vocabulary/CompressionWrappers.h b/src/index/vocabulary/CompressionWrappers.h index 6005f731e7..c299aa6119 100644 --- a/src/index/vocabulary/CompressionWrappers.h +++ b/src/index/vocabulary/CompressionWrappers.h @@ -4,8 +4,7 @@ #pragma once -#include - +#include "backports/algorithm.h" #include "index/PrefixHeuristic.h" #include "index/vocabulary/PrefixCompressor.h" #include "util/FsstCompressor.h" @@ -19,32 +18,38 @@ namespace ad_utility::vocabulary { // `vector` that stores compressed strings and the third of which is a // `Decoder`. template -concept BulkResultForDecoder = requires(T t) { - requires(std::tuple_size_v == 3); - { - std::get<1>(t) - } -> ad_utility::SimilarToAny, - std::vector>; - { std::get<2>(t) } -> ad_utility::SimilarTo; -}; +CPP_requires( + BulkResultForDecoder_, + requires(T t)(std::tuple_size_v == 3, + ad_utility::SimilarToAny(t)), + std::vector, + std::vector>, + ad_utility::SimilarTo(t)), Decoder>)); +template +CPP_concept BulkResultForDecoder = + CPP_requires_ref(BulkResultForDecoder_, T, Decoder); + +template +CPP_requires( + CompressionWrapper_, + requires(const T& t)( + // Return the number of decoders that are stored. + concepts::same_as, + // Decompress the given string, use the Decoder specified by the second + // argument. + concepts::same_as, + // Compress all the strings and return the strings together with a + // `Decoder` that can be used to decompress the strings again. + BulkResultForDecoder< + decltype(T::compressAll(std::vector{})), + typename T::Decoder>, + concepts::constructible_from>)); // A concept for the `CompressionWrappers` that can be passed as template // arguments to the compressed vocabulary to specify the compression algorithm. template -concept CompressionWrapper = requires(const T& t) { - typename T::Decoder; - // Return the number of decoders that are stored. - { t.numDecoders() } -> std::same_as; - // Decompress the given string, use the Decoder specified by the second - // argument. - { t.decompress(std::string_view{}, size_t{0}) } -> std::same_as; - // Compress all the strings and return the strings together with a `Decoder` - // that can be used to decompress the strings again. - { - T::compressAll(std::vector{}) - } -> BulkResultForDecoder; - requires(std::constructible_from>); -}; +CPP_concept CompressionWrapper = CPP_requires_ref(CompressionWrapper_, T); namespace detail { // A class that holds a `vector` and implements the diff --git a/test/index/vocabulary/CompressedVocabularyTest.cpp b/test/index/vocabulary/CompressedVocabularyTest.cpp index d074c62e5f..a1a445e213 100644 --- a/test/index/vocabulary/CompressedVocabularyTest.cpp +++ b/test/index/vocabulary/CompressedVocabularyTest.cpp @@ -5,6 +5,7 @@ #include #include "VocabularyTestHelpers.h" +#include "backports/algorithm.h" #include "index/VocabularyOnDisk.h" #include "index/vocabulary/CompressedVocabulary.h" #include "index/vocabulary/PrefixCompressor.h" @@ -84,8 +85,9 @@ using Compressors = PrefixCompressionWrapper, DummyCompressionWrapper>; // _________________________________________________________________________ -template -struct CompressedVocabularyF : public testing::Test { +CPP_template(typename Compressor)( + requires ad_utility::vocabulary::CompressionWrapper< + Compressor>) struct CompressedVocabularyF : public testing::Test { // Tests for the FSST-compressed vocabulary. These use the generic testing // framework that was set up for all the other vocabularies. static constexpr auto createCompressedVocabulary( From aa550575c990869a44bc469490c0371e77243b48 Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Tue, 4 Feb 2025 03:09:53 +0100 Subject: [PATCH 19/28] Account for `FILTER`s when considering greedy query planning (#1705) Since #1442, QLever switches to greedy query planning for large connected components. A connected component is considered large when the number of connected subgraphs is above the threshold determined by the runtime parameter `query-planning-budget`. So far, `FILTER`s were simply ignored when counting the number of subgraphs. However, `FILTER`s can add significant complexity to the standard query planning because for each subplan, our query planner considers either adding all applicable `FILTER`s to it or none of them. As a result, for certain queries with a medium-sized component but a significant number of `FILTER`s, the query planning complexity was underestimated and the query was not planned greedily and the standard query planning took very long. This is now fixed by replacing, for the purpose of query planning, each `FILTER` by a dummy `VALUES` clause which uses the set of distinct variables from the `FILTER`. A `FILTER` that has many variables in common with other triples will then increase the subgraph count substantially. If multiple `FILTER`s have the same set of distinct variables, the dummy `VALUES` clause is added only once (because our query planner either adds all applicable `FILTER`s at a certain point or none of them). Note that this trick overestimates the true query planning complexity. That is, the worst that can happen now is that with many `FILTER`s, we switch to greedy planning even though standard query planning would have still been feasible, --- src/engine/QueryPlanner.cpp | 39 +++++++++++++++++++++++++++++++++++-- src/engine/QueryPlanner.h | 7 +++++-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/engine/QueryPlanner.cpp b/src/engine/QueryPlanner.cpp index 43caa71f02..a8bf59d1c6 100644 --- a/src/engine/QueryPlanner.cpp +++ b/src/engine/QueryPlanner.cpp @@ -1343,7 +1343,8 @@ QueryPlanner::runDynamicProgrammingOnConnectedComponent( // _____________________________________________________________________________ size_t QueryPlanner::countSubgraphs( - std::vector graph, size_t budget) { + std::vector graph, + const std::vector& filters, size_t budget) { // Remove duplicate plans from `graph`. auto getId = [](const SubtreePlan* v) { return v->_idsOfIncludedNodes; }; ql::ranges::sort(graph, ql::ranges::less{}, getId); @@ -1354,6 +1355,36 @@ size_t QueryPlanner::countSubgraphs( graph.erase(uniqueIter.begin(), graph.end()); #endif + // We also have to consider the `filters`. To make life easy, we temporarily + // create simple `SubtreePlans` for them which just have the correct + // variables. We only create one subtree plan for each set of variables that + // is contained in the `filters`, because this will bring the estimate of this + // function closer to the actual behavior of the DP query planner (it always + // applies either all possible filters at once, or none of them). + std::vector dummyPlansForFilter; + ad_utility::HashSet> + deduplicatedFilterVariables; + for (const auto& filter : filters) { + const auto& vars = filter.expression_.containedVariables(); + ad_utility::HashSet varSet; + // We use a `VALUES` clause as the dummy because this operation is the + // easiest to setup for a number of given variables. + parsedQuery::SparqlValues values; + for (auto* var : vars) { + values._variables.push_back(*var); + varSet.insert(*var); + } + if (deduplicatedFilterVariables.insert(std::move(varSet)).second) { + dummyPlansForFilter.push_back( + makeSubtreePlan(_qec, std::move(values))); + } + } + + const size_t numPlansWithoutFilters = graph.size(); + for (const auto& filterPlan : dummyPlansForFilter) { + graph.push_back(&filterPlan); + } + // Qlever currently limits the number of triples etc. per group to be <= 64 // anyway, so we can simply assert here. AD_CORRECTNESS_CHECK(graph.size() <= 64, @@ -1367,7 +1398,11 @@ size_t QueryPlanner::countSubgraphs( for (size_t i = 0; i < graph.size(); ++i) { countConnectedSubgraphs::Node v{0}; for (size_t k = 0; k < graph.size(); ++k) { + // Don't connect nodes to themselves, don't connect filters with other + // filters, otherwise connect `i` and `k` if they have at least one + // variable in common. if ((k != i) && + (k < numPlansWithoutFilters || i < numPlansWithoutFilters) && !QueryPlanner::getJoinColumns(*graph.at(k), *graph.at(i)).empty()) { v.neighbors_ |= (1ULL << k); } @@ -1477,7 +1512,7 @@ vector> QueryPlanner::fillDpTab( g.push_back(&plan); } const size_t budget = RuntimeParameters().get<"query-planning-budget">(); - bool useGreedyPlanning = countSubgraphs(g, budget) > budget; + bool useGreedyPlanning = countSubgraphs(g, filters, budget) > budget; if (useGreedyPlanning) { LOG(INFO) << "Using the greedy query planner for a large connected component" diff --git a/src/engine/QueryPlanner.h b/src/engine/QueryPlanner.h index b51523baed..5b4a1c67f3 100644 --- a/src/engine/QueryPlanner.h +++ b/src/engine/QueryPlanner.h @@ -488,8 +488,11 @@ class QueryPlanner { // if the number of subgraphs is `> budget`. This is used to analyze the // complexity of the query graph and to choose between the DP and the greedy // query planner see above. - static size_t countSubgraphs(std::vector graph, - size_t budget); + // Note: We also need the added filters, because they behave like additional + // graph nodes wrt the performance of the DP based query planner. + size_t countSubgraphs(std::vector graph, + const std::vector& filters, + size_t budget); // Creates a SubtreePlan for the given text leaf node in the triple graph. // While doing this the TextLimitMetaObjects are created and updated according From db6825c3c3769836056106b7dd2ac23f7bdc19d0 Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Tue, 4 Feb 2025 04:43:40 +0100 Subject: [PATCH 20/28] Parse `POINT(...)` as `GeoPoint` in a SPARQL query (#1731) Specifying an explicit `POINT(...)` in a SPARQL query could lead to a segmentation fault because it wasn't actually parsed as a `GeoPoint`. This is now fixed. --- src/parser/TripleComponent.cpp | 4 ++++ test/TripleComponentTest.cpp | 27 ++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/parser/TripleComponent.cpp b/src/parser/TripleComponent.cpp index 96730aa0b4..e6913c17a1 100644 --- a/src/parser/TripleComponent.cpp +++ b/src/parser/TripleComponent.cpp @@ -28,7 +28,11 @@ std::ostream& operator<<(std::ostream& stream, const TripleComponent& obj) { stream << "DATE: " << value.toStringAndType().first; } else if constexpr (std::is_same_v) { stream << (value ? "true" : "false"); + } else if constexpr (std::is_same_v) { + stream << Id::makeFromGeoPoint(value); } else { + static_assert( + ad_utility::SameAsAny); stream << value; } }, diff --git a/test/TripleComponentTest.cpp b/test/TripleComponentTest.cpp index 2c67b16823..4e6de05205 100644 --- a/test/TripleComponentTest.cpp +++ b/test/TripleComponentTest.cpp @@ -3,7 +3,7 @@ // Author: Johannes Kalmbach(joka921) // -#include +#include #include "./util/IdTestHelpers.h" #include "./util/TripleComponentTestHelpers.h" @@ -225,3 +225,28 @@ TEST(TripleComponent, invalidDatatypeForLiteral) { // Something that is invalid because it is none of the above ASSERT_ANY_THROW(lit("\"alpha\"", "fr-ca")); } + +// _____________________________________________________________________________1 +TEST(TripleComponent, toString) { + using namespace ::testing; + auto match = [](const auto& matcher) { + auto makeTcAndToString = [](auto&& val) { + return TripleComponent{AD_FWD(val)}.toString(); + }; + return ResultOf(makeTcAndToString, matcher); + }; + + EXPECT_THAT(GeoPoint(13, 14), match(StartsWith("G:POINT(14."))); + EXPECT_THAT("hello", match("hello")); + EXPECT_THAT(12, match("12")); + EXPECT_THAT(12.3, match("12.3")); + using Tc = TripleComponent; + EXPECT_THAT(Tc::UNDEF{}, match("UNDEF")); + EXPECT_THAT(Variable("?x"), match("?x")); + EXPECT_THAT(Tc::Literal::literalWithoutQuotes("hallo"), match("\"hallo\"")); + EXPECT_THAT(Tc::Iri::fromIrirefWithoutBrackets("blim"), match("")); + EXPECT_THAT(DateYearOrDuration(Date(2000, 1, 1)), match("DATE: 2000-01-01")); + EXPECT_THAT(true, match("true")); + EXPECT_THAT(false, match("false")); + EXPECT_THAT(Id::makeFromInt(42), match("I:42")); +} From be240abc3bc9bfa731353d612c3db4c2187bba41 Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Tue, 4 Feb 2025 16:46:03 +0100 Subject: [PATCH 21/28] Implement cast expressions correctly (#1751) Add the cast expressions `xsd:boolean(?something)` and `xsd:string(?something)` . Also add `xsd:float` which just does the same as `xsd:double` as QLever currently doesn't distinguish between these operations. Also support exponential notation like "1E04" for the `xsd:double` cast. --- .../ConvertToNumericExpression.cpp | 49 +++++++++++++++- src/engine/sparqlExpressions/NaryExpression.h | 7 +++ .../sparqlExpressions/StringExpressions.cpp | 4 ++ .../sparqlParser/SparqlQleverVisitor.cpp | 19 +++++- test/SparqlAntlrParserTest.cpp | 9 ++- test/SparqlExpressionTest.cpp | 58 +++++++++++++++++-- 6 files changed, 136 insertions(+), 10 deletions(-) diff --git a/src/engine/sparqlExpressions/ConvertToNumericExpression.cpp b/src/engine/sparqlExpressions/ConvertToNumericExpression.cpp index 50ac26d7af..a1fa97b6c7 100644 --- a/src/engine/sparqlExpressions/ConvertToNumericExpression.cpp +++ b/src/engine/sparqlExpressions/ConvertToNumericExpression.cpp @@ -9,12 +9,17 @@ namespace detail::to_numeric { // class that converts an input `int64_t`, `double` or `std::string` // to a numeric value `int64_t` or `double` -template +template requires std::same_as || std::same_as class ToNumericImpl { private: Id getFromString(const std::string& input) const { auto str = absl::StripAsciiWhitespace(input); + // Abseil and the standard library don't match leading + signs, so we skip + // them. + if (str.starts_with('+')) { + str.remove_prefix(1); + } auto strEnd = str.data() + str.size(); auto strStart = str.data(); T resT{}; @@ -24,7 +29,10 @@ class ToNumericImpl { return Id::makeFromInt(resT); } } else { - auto conv = absl::from_chars(strStart, strEnd, resT); + auto conv = absl::from_chars(strStart, strEnd, resT, + AllowExponentialNotation + ? absl::chars_format::general + : absl::chars_format::fixed); if (conv.ec == std::error_code{} && conv.ptr == strEnd) { return Id::makeFromDouble(resT); } @@ -61,9 +69,38 @@ class ToNumericImpl { using ToInteger = NARY<1, FV, ToNumericValueGetter>>; using ToDouble = NARY<1, FV, ToNumericValueGetter>>; +using ToDecimal = + NARY<1, FV, ToNumericValueGetter>>; } // namespace detail::to_numeric +namespace detail::to_boolean { +class ToBooleanImpl { + public: + Id operator()(IntDoubleStr value) const { + if (std::holds_alternative(value)) { + const std::string& str = std::get(value); + if (str == "true" || str == "1") { + return Id::makeFromBool(true); + } + if (str == "false" || str == "0") { + return Id::makeFromBool(false); + } + return Id::makeUndefined(); + } else if (std::holds_alternative(value)) { + return Id::makeFromBool(std::get(value) != 0); + } else if (std::holds_alternative(value)) { + return Id::makeFromBool(std::get(value) != 0); + } else { + AD_CORRECTNESS_CHECK(std::holds_alternative(value)); + return Id::makeUndefined(); + } + } +}; +using ToBoolean = NARY<1, FV>; +} // namespace detail::to_boolean + using namespace detail::to_numeric; +using namespace detail::to_boolean; using Expr = SparqlExpression::Ptr; Expr makeConvertToIntExpression(Expr child) { @@ -73,4 +110,12 @@ Expr makeConvertToIntExpression(Expr child) { Expr makeConvertToDoubleExpression(Expr child) { return std::make_unique(std::move(child)); } + +Expr makeConvertToDecimalExpression(Expr child) { + return std::make_unique(std::move(child)); +} + +Expr makeConvertToBooleanExpression(Expr child) { + return std::make_unique(std::move(child)); +} } // namespace sparqlExpression diff --git a/src/engine/sparqlExpressions/NaryExpression.h b/src/engine/sparqlExpressions/NaryExpression.h index 8fb8d95043..9f3b2c9f18 100644 --- a/src/engine/sparqlExpressions/NaryExpression.h +++ b/src/engine/sparqlExpressions/NaryExpression.h @@ -96,6 +96,9 @@ SparqlExpression::Ptr makeSHA256Expression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeSHA384Expression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeSHA512Expression(SparqlExpression::Ptr child); +SparqlExpression::Ptr makeConvertToStringExpression( + SparqlExpression::Ptr child); + SparqlExpression::Ptr makeIfExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child2, SparqlExpression::Ptr child3); @@ -104,6 +107,10 @@ SparqlExpression::Ptr makeIfExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr makeConvertToIntExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeConvertToDoubleExpression( SparqlExpression::Ptr child); +SparqlExpression::Ptr makeConvertToDecimalExpression( + SparqlExpression::Ptr child); +SparqlExpression::Ptr makeConvertToBooleanExpression( + SparqlExpression::Ptr child); // Implemented in RdfTermExpressions.cpp SparqlExpression::Ptr makeDatatypeExpression(SparqlExpression::Ptr child); diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 3378ae7fdb..ba06cac535 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -619,4 +619,8 @@ Expr makeSHA384Expression(Expr child) { return make(child); } Expr makeSHA512Expression(Expr child) { return make(child); } +Expr makeConvertToStringExpression(Expr child) { + return std::make_unique(std::move(child)); +} + } // namespace sparqlExpression diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 9df20dd5ba..088f463ec5 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -158,11 +158,28 @@ ExpressionPtr Visitor::processIriFunctionCall( checkNumArgs(1); return sparqlExpression::makeConvertToIntExpression( std::move(argList[0])); - } else if (functionName == "double" || functionName == "decimal") { + } + if (functionName == "decimal") { + checkNumArgs(1); + return sparqlExpression::makeConvertToDecimalExpression( + std::move(argList[0])); + } + // We currently don't have a float type, so we just convert to double. + if (functionName == "double" || functionName == "float") { checkNumArgs(1); return sparqlExpression::makeConvertToDoubleExpression( std::move(argList[0])); } + if (functionName == "boolean") { + checkNumArgs(1); + return sparqlExpression::makeConvertToBooleanExpression( + std::move(argList[0])); + } + if (functionName == "string") { + checkNumArgs(1); + return sparqlExpression::makeConvertToStringExpression( + std::move(argList[0])); + } } // QLever-internal functions. diff --git a/test/SparqlAntlrParserTest.cpp b/test/SparqlAntlrParserTest.cpp index 3bd820256e..a0e10d9b6f 100644 --- a/test/SparqlAntlrParserTest.cpp +++ b/test/SparqlAntlrParserTest.cpp @@ -1798,8 +1798,15 @@ TEST(SparqlParser, FunctionCall) { matchUnary(&makeConvertToIntExpression)); expectFunctionCall(absl::StrCat(xsd, "double>(?x)"), matchUnary(&makeConvertToDoubleExpression)); - expectFunctionCall(absl::StrCat(xsd, "decimal>(?x)"), + expectFunctionCall(absl::StrCat(xsd, "float>(?x)"), matchUnary(&makeConvertToDoubleExpression)); + expectFunctionCall(absl::StrCat(xsd, "decimal>(?x)"), + matchUnary(&makeConvertToDecimalExpression)); + expectFunctionCall(absl::StrCat(xsd, "boolean>(?x)"), + matchUnary(&makeConvertToBooleanExpression)); + + expectFunctionCall(absl::StrCat(xsd, "string>(?x)"), + matchUnary(&makeConvertToStringExpression)); // Wrong number of arguments. expectFunctionCallFails(absl::StrCat(geof, "distance>(?a)")); diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 71a24908a2..45bfbd83eb 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -1090,16 +1090,25 @@ TEST(SparqlExpression, testToNumericExpression) { Id G = Id::makeFromGeoPoint(GeoPoint(50.0, 50.0)); auto checkGetInt = testUnaryExpression<&makeConvertToIntExpression>; auto checkGetDouble = testUnaryExpression<&makeConvertToDoubleExpression>; + auto chekGetDecimal = testUnaryExpression<&makeConvertToDecimalExpression>; - checkGetInt(idOrLitOrStringVec({U, " -1275", "5.97", "-78.97", "-5BoB6", - "FreBurg1", "", " .", " 42\n", " 0.01 ", "", - "@", "@?+1", "1", G}), - Ids{U, I(-1275), U, U, U, U, U, U, I(42), U, U, U, U, I(1), U}); + checkGetInt( + idOrLitOrStringVec({U, " -1275", "5.97", "-78.97", "-5BoB6", "FreBurg1", + "", " .", " 42\n", " 0.01 ", "", "@", "@?+1", "1", G, + "+42"}), + Ids{U, I(-1275), U, U, U, U, U, U, I(42), U, U, U, U, I(1), U, I(42)}); checkGetDouble( idOrLitOrStringVec({U, "-122.2", "19,96", " 128789334.345 ", "-0.f", - " 0.007 ", " -14.75 ", "Q", "@!+?", "1", G}), + " 0.007 ", " -14.75 ", "Q", "@!+?", "1", G, "+42.0", + " +1E-2", "1e3 ", "1.3E1"}), Ids{U, D(-122.2), U, D(128789334.345), U, D(0.007), D(-14.75), U, U, - D(1.00), U}); + D(1.00), U, D(42), D(0.01), D(1000), D(13)}); + chekGetDecimal( + idOrLitOrStringVec({U, "-122.2", "19,96", " 128789334.345 ", "-0.f", + " 0.007 ", " -14.75 ", "Q", "@!+?", "1", G, "+42.0", + " +1E-2", "1e3 ", "1.3E1"}), + Ids{U, D(-122.2), U, D(128789334.345), U, D(0.007), D(-14.75), U, U, + D(1.00), U, D(42), U, U, U}); checkGetInt(idOrLitOrStringVec( {U, I(-12475), I(42), I(0), D(-14.57), D(33.0), D(0.00001)}), Ids{U, I(-12475), I(42), I(0), I(-14), I(33), I(0)}); @@ -1107,14 +1116,51 @@ TEST(SparqlExpression, testToNumericExpression) { idOrLitOrStringVec( {U, I(-12475), I(42), I(0), D(-14.57), D(33.0), D(0.00001)}), Ids{U, D(-12475.00), D(42.00), D(0.00), D(-14.57), D(33.00), D(0.00001)}); + chekGetDecimal( + idOrLitOrStringVec( + {U, I(-12475), I(42), I(0), D(-14.57), D(33.0), D(0.00001)}), + Ids{U, D(-12475.00), D(42.00), D(0.00), D(-14.57), D(33.00), D(0.00001)}); checkGetDouble(IdOrLiteralOrIriVec{lit("."), lit("-12.745"), T, F, lit("0.003"), lit("1")}, Ids{U, D(-12.745), D(1.00), D(0.00), D(0.003), D(1.00)}); + chekGetDecimal(IdOrLiteralOrIriVec{lit("."), lit("-12.745"), T, F, + lit("0.003"), lit("1")}, + Ids{U, D(-12.745), D(1.00), D(0.00), D(0.003), D(1.00)}); checkGetInt(IdOrLiteralOrIriVec{lit("."), lit("-12.745"), T, F, lit(".03"), lit("1"), lit("-33")}, Ids{U, U, I(1), I(0), U, I(1), I(-33)}); } +// ____________________________________________________________________________ +TEST(SparqlExpression, testToBooleanExpression) { + Id T = Id::makeFromBool(true); + Id F = Id::makeFromBool(false); + auto checkGetBoolean = testUnaryExpression<&makeConvertToBooleanExpression>; + + checkGetBoolean( + IdOrLiteralOrIriVec( + {sparqlExpression::detail::LiteralOrIri{ + iri("")}, + lit("string"), lit("-10.2E3"), lit("+33.3300"), lit("0.0"), lit("0"), + lit("0E1"), lit("1.5"), lit("1"), lit("1E0"), lit("13"), + lit("2002-10-10T17:00:00Z"), lit("false"), lit("true"), T, F, + lit("0", absl::StrCat("^^<", XSD_PREFIX.second, "boolean>")), I(0), + I(1), I(-1), D(0.0), D(1.0), D(-1.0), + // The SPARQL compliance tests for the boolean conversion functions + // mandate that xds:boolean("0E1") is undefined, but + // xsd:boolean("0E1"^^xsd:float) is false. The code currently returns + // undefined in both cases, which is fine, because the SPARQL parser + // converts the latter into an actual float literal. + lit("0E1", absl::StrCat("^^<", XSD_PREFIX.second, "float>")), + lit("1E0", absl::StrCat("^^<", XSD_PREFIX.second, "float>")), + lit("1.25", absl::StrCat("^^<", XSD_PREFIX.second, "float>")), + lit("-7.875", absl::StrCat("^^<", XSD_PREFIX.second, "float>"))}), + Ids{U, U, U, U, U, F, U, U, T, U, U, U, F, T, + T, F, F, F, T, T, F, T, T, U, U, U, U}); + + checkGetBoolean(IdOrLiteralOrIriVec({Id::makeUndefined()}), Ids{U}); +} + // ____________________________________________________________________________ TEST(SparqlExpression, geoSparqlExpressions) { auto checkLat = testUnaryExpression<&makeLatitudeExpression>; From 32e2611aedc2dc309491e6f65a50c245ec25fd5d Mon Sep 17 00:00:00 2001 From: krzysztof-tyb Date: Tue, 4 Feb 2025 18:55:27 +0100 Subject: [PATCH 22/28] Backport the rest of the C++20 concepts to C++17 in the `index` module (#1758) This PR mainly replaces concepts such as `std::same_as` by their counterparts from `range-v3`. --- src/index/LocalVocabEntry.h | 6 ++++-- src/index/StringSortComparator.h | 10 +++++++--- src/index/TextIndexReadWrite.h | 7 +++---- src/index/VocabularyMerger.h | 4 ++-- src/index/vocabulary/CompressedVocabulary.h | 2 +- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/index/LocalVocabEntry.h b/src/index/LocalVocabEntry.h index e591ad64ff..8cdc0dc593 100644 --- a/src/index/LocalVocabEntry.h +++ b/src/index/LocalVocabEntry.h @@ -6,6 +6,7 @@ #include +#include "backports/algorithm.h" #include "global/VocabIndex.h" #include "parser/LiteralOrIri.h" #include "util/CopyableSynchronization.h" @@ -68,8 +69,9 @@ class alignas(16) LocalVocabEntry // It suffices to hash the base class `LiteralOrIri` as the position in the // vocab is redundant for those purposes. - template - friend H AbslHashValue(H h, const std::same_as auto& entry) { + template + friend auto AbslHashValue(H h, const V& entry) + -> CPP_ret(H)(requires ranges::same_as) { return AbslHashValue(std::move(h), static_cast(entry)); } diff --git a/src/index/StringSortComparator.h b/src/index/StringSortComparator.h index 81829f226e..651b857e6b 100644 --- a/src/index/StringSortComparator.h +++ b/src/index/StringSortComparator.h @@ -17,6 +17,7 @@ #include #include +#include "backports/algorithm.h" #include "global/Constants.h" #include "util/Exception.h" #include "util/StringUtils.h" @@ -177,9 +178,12 @@ class LocaleManager { * @return A weight string s.t. compare(s, t, level) == * std::strcmp(getSortKey(s, level), getSortKey(t, level) */ - void getSortKey( - std::string_view s, const Level level, - std::invocable auto resultFunction) const { + // clang-format off + CPP_template(typename F)( + requires ranges::invocable) + // clang-format on + void getSortKey(std::string_view s, const Level level, + F resultFunction) const { // TODO This function is one of the bottlenecks of the first pass // of the IndexBuilder One possible improvement is to reuse the memory // allocations for the `sortKeyBuffer`. diff --git a/src/index/TextIndexReadWrite.h b/src/index/TextIndexReadWrite.h index 8ce075a347..26d0327769 100644 --- a/src/index/TextIndexReadWrite.h +++ b/src/index/TextIndexReadWrite.h @@ -277,7 +277,7 @@ class FrequencyEncode { // requires clause is kept only in the .h file and the constructor calls the // initialize function which has no direct requires clause. CPP_template(typename View)(requires( - !std::same_as< + !ranges::same_as< FrequencyEncode, std::remove_cvref_t>)) explicit FrequencyEncode(View&& view) { initialize(std::forward(view)); @@ -331,9 +331,8 @@ class GapEncode { // requires clause is kept only in the .h file and the constructor calls the // initialize function which has no direct requires clause. CPP_template(typename View)(requires( - !std::same_as>)) explicit GapEncode(View&& - view) { + !ranges::same_as>)) explicit GapEncode(View&& view) { initialize(std::forward(view)); }; diff --git a/src/index/VocabularyMerger.h b/src/index/VocabularyMerger.h index 10b2121c97..ae72da46c6 100644 --- a/src/index/VocabularyMerger.h +++ b/src/index/VocabularyMerger.h @@ -28,11 +28,11 @@ namespace ad_utility::vocabulary_merger { // If the `bool` is true, then the word is to be stored in the external // vocabulary else in the internal vocabulary. template -CPP_concept WordCallback = std::invocable; +CPP_concept WordCallback = ranges::invocable; // Concept for a callable that compares to `string_view`s. template CPP_concept WordComparator = - std::predicate; + ranges::predicate; // The result of a call to `mergeVocabulary` (see below). struct VocabularyMetaData { diff --git a/src/index/vocabulary/CompressedVocabulary.h b/src/index/vocabulary/CompressedVocabulary.h index cc106375cc..6620248363 100644 --- a/src/index/vocabulary/CompressedVocabulary.h +++ b/src/index/vocabulary/CompressedVocabulary.h @@ -314,7 +314,7 @@ CPP_template(typename UnderlyingVocabulary, // _________________________________________________________________ template static std::string_view toStringView(const T& el) { - if constexpr (std::convertible_to) { + if constexpr (ranges::convertible_to) { return el; } else if constexpr (ad_utility::isInstantiation) { return toStringView(el.value()); From 6c0e792e50f82179429b5f43f07f6b04951bbfd2 Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Tue, 4 Feb 2025 18:55:48 +0100 Subject: [PATCH 23/28] Don't use `cache-max-size-lazy-result` setting for the root operation, even when it's lazy (#1709) Since https://github.com/ad-freiburg/qlever/pull/1638, QLever has the runtime parameter `lazy-result-max-cache-size`, with a deliberately small default value of 5 MB. However, the final result (of the root operation) should be cached not depending on that size, but depending on the regular `cache-max-size`, `cache-max-size-single-entry`, and `cache-max-num-entries`, even if the last operation is (output-)lazy. This is now indeed so. Fixes #1701 (which provides a good example of a query, where the old behavior is weird and the new behavior makes much more sense). Use the occasion to give the runtime parameter a more consistent name, namely `cache-max-size-lazy-result`. --- src/ServerMain.cpp | 4 +-- src/engine/Operation.cpp | 12 ++++---- src/engine/Operation.h | 3 +- src/global/RuntimeParameters.h | 2 +- test/OperationTest.cpp | 54 +++++++++++++++++++++++++++++++--- test/engine/ValuesForTesting.h | 11 ++++++- 6 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/ServerMain.cpp b/src/ServerMain.cpp index f50d12cba2..e0808817b1 100644 --- a/src/ServerMain.cpp +++ b/src/ServerMain.cpp @@ -82,8 +82,8 @@ int main(int argc, char** argv) { optionFactory.getProgramOption<"cache-max-size-single-entry">(), "Maximum size for a single cache entry. That is, " "results larger than this will not be cached unless pinned."); - add("lazy-result-max-cache-size,E", - optionFactory.getProgramOption<"lazy-result-max-cache-size">(), + add("cache-max-size-lazy-result,E", + optionFactory.getProgramOption<"cache-max-size-lazy-result">(), "Maximum size up to which lazy results will be cached by aggregating " "partial results. Caching does cause significant overhead for this " "case."); diff --git a/src/engine/Operation.cpp b/src/engine/Operation.cpp index ed5d9d3cc6..0f94ff7886 100644 --- a/src/engine/Operation.cpp +++ b/src/engine/Operation.cpp @@ -226,12 +226,13 @@ ProtoResult Operation::runComputation(const ad_utility::Timer& timer, // _____________________________________________________________________________ CacheValue Operation::runComputationAndPrepareForCache( const ad_utility::Timer& timer, ComputationMode computationMode, - const QueryCacheKey& cacheKey, bool pinned) { + const QueryCacheKey& cacheKey, bool pinned, bool isRoot) { auto& cache = _executionContext->getQueryTreeCache(); auto result = runComputation(timer, computationMode); auto maxSize = - std::min(RuntimeParameters().get<"lazy-result-max-cache-size">(), - cache.getMaxSizeSingleEntry()); + isRoot ? cache.getMaxSizeSingleEntry() + : std::min(RuntimeParameters().get<"cache-max-size-lazy-result">(), + cache.getMaxSizeSingleEntry()); if (canResultBeCached() && !result.isFullyMaterialized() && !unlikelyToFitInCache(maxSize)) { AD_CONTRACT_CHECK(!pinned); @@ -306,9 +307,10 @@ std::shared_ptr Operation::getResult( updateRuntimeInformationOnFailure(timer.msecs()); } }); - auto cacheSetup = [this, &timer, computationMode, &cacheKey, pinResult]() { + auto cacheSetup = [this, &timer, computationMode, &cacheKey, pinResult, + isRoot]() { return runComputationAndPrepareForCache(timer, computationMode, cacheKey, - pinResult); + pinResult, isRoot); }; auto suitedForCache = [](const CacheValue& cacheValue) { diff --git a/src/engine/Operation.h b/src/engine/Operation.h index 893e85ca22..1a3f68b83d 100644 --- a/src/engine/Operation.h +++ b/src/engine/Operation.h @@ -384,7 +384,7 @@ class Operation { CacheValue runComputationAndPrepareForCache(const ad_utility::Timer& timer, ComputationMode computationMode, const QueryCacheKey& cacheKey, - bool pinned); + bool pinned, bool isRoot); // Create and store the complete runtime information for this operation after // it has either been successfully computed or read from the cache. @@ -454,4 +454,5 @@ class Operation { FRIEND_TEST(Operation, ensureLazyOperationIsCachedIfSmallEnough); FRIEND_TEST(Operation, checkLazyOperationIsNotCachedIfTooLarge); FRIEND_TEST(Operation, checkLazyOperationIsNotCachedIfUnlikelyToFitInCache); + FRIEND_TEST(Operation, checkMaxCacheSizeIsComputedCorrectly); }; diff --git a/src/global/RuntimeParameters.h b/src/global/RuntimeParameters.h index c082b14ca4..f61705e7fc 100644 --- a/src/global/RuntimeParameters.h +++ b/src/global/RuntimeParameters.h @@ -54,7 +54,7 @@ inline auto& RuntimeParameters() { Bool<"throw-on-unbound-variables">{false}, // Control up until which size lazy results should be cached. Caching // does cause significant overhead for this case. - MemorySizeParameter<"lazy-result-max-cache-size">{5_MB}, + MemorySizeParameter<"cache-max-size-lazy-result">{5_MB}, Bool<"websocket-updates-enabled">{true}, // When the result of an index scan is smaller than a single block, then // its size estimate will be the size of the block divided by this diff --git a/test/OperationTest.cpp b/test/OperationTest.cpp index e39b9312e0..96c6525221 100644 --- a/test/OperationTest.cpp +++ b/test/OperationTest.cpp @@ -9,6 +9,7 @@ #include "engine/NeutralElementOperation.h" #include "engine/ValuesForTesting.h" #include "global/RuntimeParameters.h" +#include "util/GTestHelpers.h" #include "util/IdTableHelpers.h" #include "util/IndexTestHelpers.h" #include "util/OperationTestHelpers.h" @@ -556,7 +557,7 @@ TEST(Operation, ensureLazyOperationIsCachedIfSmallEnough) { auto cacheValue = valuesForTesting.runComputationAndPrepareForCache( timer, ComputationMode::LAZY_IF_SUPPORTED, makeQueryCacheKey("test"), - false); + false, false); EXPECT_FALSE( qec->getQueryTreeCache().cacheContains(makeQueryCacheKey("test"))); @@ -610,11 +611,11 @@ TEST(Operation, checkLazyOperationIsNotCachedIfTooLarge) { // generator to additionally assert sure it is not re-read on every // iteration. auto cleanup = - setRuntimeParameterForTest<"lazy-result-max-cache-size">(1_B); + setRuntimeParameterForTest<"cache-max-size-lazy-result">(1_B); cacheValue = valuesForTesting.runComputationAndPrepareForCache( timer, ComputationMode::LAZY_IF_SUPPORTED, makeQueryCacheKey("test"), - false); + false, false); EXPECT_FALSE( qec->getQueryTreeCache().cacheContains(makeQueryCacheKey("test"))); } @@ -641,7 +642,7 @@ TEST(Operation, checkLazyOperationIsNotCachedIfUnlikelyToFitInCache) { auto cacheValue = valuesForTesting.runComputationAndPrepareForCache( timer, ComputationMode::LAZY_IF_SUPPORTED, makeQueryCacheKey("test"), - false); + false, false); EXPECT_FALSE( qec->getQueryTreeCache().cacheContains(makeQueryCacheKey("test"))); @@ -653,6 +654,51 @@ TEST(Operation, checkLazyOperationIsNotCachedIfUnlikelyToFitInCache) { qec->getQueryTreeCache().cacheContains(makeQueryCacheKey("test"))); } +// _____________________________________________________________________________ +TEST(Operation, checkMaxCacheSizeIsComputedCorrectly) { + auto runTest = [](ad_utility::MemorySize cacheLimit, + ad_utility::MemorySize runtimeParameterLimit, bool isRoot, + ad_utility::MemorySize expectedSize, + ad_utility::source_location sourceLocation = + ad_utility::source_location::current()) { + auto loc = generateLocationTrace(sourceLocation); + auto qec = getQec(); + qec->getQueryTreeCache().clearAll(); + std::vector idTablesVector{}; + idTablesVector.push_back(makeIdTableFromVector({{3, 4}})); + ValuesForTesting valuesForTesting{ + qec, std::move(idTablesVector), {Variable{"?x"}, Variable{"?y"}}, true}; + + ad_utility::MemorySize actualCacheSize; + valuesForTesting.setCacheSizeStorage(&actualCacheSize); + + absl::Cleanup restoreOriginalSize{ + [qec, original = qec->getQueryTreeCache().getMaxSizeSingleEntry()]() { + qec->getQueryTreeCache().setMaxSizeSingleEntry(original); + }}; + qec->getQueryTreeCache().setMaxSizeSingleEntry(cacheLimit); + + auto cleanup = setRuntimeParameterForTest<"cache-max-size-lazy-result">( + runtimeParameterLimit); + + ad_utility::Timer timer{ad_utility::Timer::InitialStatus::Started}; + + auto cacheValue = valuesForTesting.runComputationAndPrepareForCache( + timer, ComputationMode::LAZY_IF_SUPPORTED, makeQueryCacheKey("test"), + false, isRoot); + + EXPECT_EQ(actualCacheSize, expectedSize); + }; + + runTest(10_B, 10_B, true, 10_B); + runTest(10_B, 10_B, false, 10_B); + runTest(10_B, 1_B, false, 1_B); + runTest(1_B, 10_B, false, 1_B); + runTest(10_B, 1_B, true, 10_B); + runTest(1_B, 10_B, true, 1_B); +} + +// _____________________________________________________________________________ TEST(OperationTest, disableCaching) { auto qec = getQec(); qec->getQueryTreeCache().clearAll(); diff --git a/test/engine/ValuesForTesting.h b/test/engine/ValuesForTesting.h index 097ccd9c78..ec9b55cd30 100644 --- a/test/engine/ValuesForTesting.h +++ b/test/engine/ValuesForTesting.h @@ -22,6 +22,7 @@ class ValuesForTesting : public Operation { size_t sizeEstimate_; size_t costEstimate_; bool unlikelyToFitInCache_ = false; + ad_utility::MemorySize* cacheSizeStorage_ = nullptr; public: // Create an operation that has as its result the given `table` and the given @@ -115,9 +116,17 @@ class ValuesForTesting : public Operation { } return {std::move(table), resultSortedOn(), localVocab_.clone()}; } - bool unlikelyToFitInCache(ad_utility::MemorySize) const override { + bool unlikelyToFitInCache(ad_utility::MemorySize cacheSize) const override { + if (cacheSizeStorage_ != nullptr) { + *cacheSizeStorage_ = cacheSize; + } return unlikelyToFitInCache_; } + + void setCacheSizeStorage(ad_utility::MemorySize* cacheSizeStorage) { + cacheSizeStorage_ = cacheSizeStorage; + } + bool supportsLimit() const override { return supportsLimit_; } bool& forceFullyMaterialized() { return forceFullyMaterialized_; } From 1dbe2afbcba5807de127c5f97e500c33a45d8364 Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Wed, 5 Feb 2025 08:30:03 +0100 Subject: [PATCH 24/28] Fix `REPLACE` function with replace patterns (#1755) In the replace string of the `REPLACE` function, QLever now uses a dollar sign `$` to dynamically fetch the replacement from the input. For example `REPLACE("aabc", "(a+)b", "$1def$1")` will now correctly result in "aadefaac". This fix is implemented by manually converting the replacement string to the format the Google RE2 expects where the replacement format is not `$n` but `\n`. Fixes #1664 --- .../SparqlExpressionValueGetters.cpp | 49 +++++++++++++++++++ .../SparqlExpressionValueGetters.h | 16 ++++++ .../sparqlExpressions/StringExpressions.cpp | 2 +- test/SparqlExpressionTest.cpp | 10 ++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/SparqlExpressionValueGetters.cpp b/src/engine/sparqlExpressions/SparqlExpressionValueGetters.cpp index 3ed18e7b99..8eda73d065 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionValueGetters.cpp +++ b/src/engine/sparqlExpressions/SparqlExpressionValueGetters.cpp @@ -90,6 +90,55 @@ std::optional StringValueGetter::operator()( } } +// ____________________________________________________________________________ +std::optional ReplacementStringGetter::operator()( + Id id, const EvaluationContext* context) const { + std::optional originalString = + StringValueGetter::operator()(id, context); + if (!originalString.has_value()) { + return originalString; + } + return convertToReplacementString(originalString.value()); +} + +// ____________________________________________________________________________ +std::optional ReplacementStringGetter::operator()( + const LiteralOrIri& s, const EvaluationContext*) const { + return convertToReplacementString(asStringViewUnsafe(s.getContent())); +} + +// ____________________________________________________________________________ +std::string ReplacementStringGetter::convertToReplacementString( + std::string_view view) { + std::string result; + // Rough estimate of the size of the result string. + result.reserve(view.size()); + for (size_t i = 0; i < view.size(); i++) { + char c = view.at(i); + switch (c) { + case '$': + // Re2 used \1, \2, ... for backreferences, so we change $ to \. + result.push_back('\\'); + break; + case '\\': + // "\$" is unescaped to "$" + if (i + 1 < view.size() && view.at(i + 1) == '$') { + result.push_back('$'); + i++; + } else { + // Escape existing backslashes. + result.push_back(c); + result.push_back(c); + } + break; + default: + result.push_back(c); + break; + } + } + return result; +} + // ____________________________________________________________________________ template Id IsSomethingValueGetter::operator()( diff --git a/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h b/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h index 88dbc2d825..bea3a60e3a 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h +++ b/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h @@ -140,6 +140,22 @@ struct StringValueGetter : Mixin { return std::string(asStringViewUnsafe(s.getContent())); } }; +// Similar to `StringValueGetter`, but correctly preprocesses strings so that +// they can be used by re2 as replacement strings. So '$1 \abc \$' becomes +// '\1 \\abc $', where the former variant is valid in the SPARQL standard and +// the latter represents the format that re2 expects. +struct ReplacementStringGetter : StringValueGetter, + Mixin { + using Mixin::operator(); + std::optional operator()(ValueId, + const EvaluationContext*) const; + + std::optional operator()(const LiteralOrIri& s, + const EvaluationContext*) const; + + private: + static std::string convertToReplacementString(std::string_view view); +}; // Boolean value getter that checks whether the given `Id` is a `ValueId` of the // given `datatype`. diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index ba06cac535..cc39fbca9f 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -357,7 +357,7 @@ using StrBeforeExpression = using ReplaceExpression = StringExpressionImpl<3, decltype(replaceImpl), RegexValueGetter, - StringValueGetter>; + ReplacementStringGetter>; // CONCAT class ConcatExpression : public detail::VariadicExpression { diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 45bfbd83eb..891b990e92 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -1288,6 +1288,16 @@ TEST(SparqlExpression, ReplaceExpression) { idOrLitOrStringVec({"null", "Xs", "zwei", "drei", U, U}), std::tuple{idOrLitOrStringVec({"null", "eins", "zwei", "drei", U, U}), IdOrLiteralOrIri{lit("e.[a-z]")}, IdOrLiteralOrIri{lit("X")}}); + // A regex with replacement with substitutions + checkReplace(idOrLitOrStringVec({R"("$1 \\2 A \\bc")", R"("$1 \\2 DE \\f")"}), + std::tuple{idOrLitOrStringVec({"Abc", "DEf"}), + IdOrLiteralOrIri{lit("([A-Z]+)")}, + IdOrLiteralOrIri{lit(R"("\\$1 \\2 $1 \\")")}}); + + checkReplace(idOrLitOrStringVec({"truebc", "truef"}), + std::tuple{idOrLitOrStringVec({"Abc", "DEf"}), + IdOrLiteralOrIri{lit("([A-Z]+)")}, + IdOrLiteralOrIri{Id::makeFromBool(true)}}); // Case-insensitive matching using the hack for google regex: checkReplace(idOrLitOrStringVec({"null", "xxns", "zwxx", "drxx"}), From 098b79c715f9992972a8d3452f5bd10e303c6ce4 Mon Sep 17 00:00:00 2001 From: Julian <14220769+Qup42@users.noreply.github.com> Date: Wed, 5 Feb 2025 08:34:46 +0100 Subject: [PATCH 25/28] Fix `graphOrDefault` update grammar rule (#1760) There was a small mistake in the grammar of the `graphOrDefault` rule(Rule 45 in the [grammar](https://www.w3.org/TR/2013/REC-sparql11-query-20130321/#sparqlGrammar)). Some updates were incorrectly rejected as invalid due to this. E.g. `COPY DEFAULT TO GRAPH ` was accepted while the also valid `COPY DEFAULT TO ` was rejected. PR only affects the `SparqlAutomatic.g4` grammar files, as well as the auto-generated ANTLR files. --- .../sparqlParser/generated/SparqlAutomatic.g4 | 2 +- .../generated/SparqlAutomatic.interp | 2 +- .../generated/SparqlAutomaticParser.cpp | 3613 +++++++++-------- .../generated/SparqlAutomaticParser.h | 2 +- 4 files changed, 1816 insertions(+), 1803 deletions(-) diff --git a/src/parser/sparqlParser/generated/SparqlAutomatic.g4 b/src/parser/sparqlParser/generated/SparqlAutomatic.g4 index f8b8c9206d..1eceb783ce 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomatic.g4 +++ b/src/parser/sparqlParser/generated/SparqlAutomatic.g4 @@ -181,7 +181,7 @@ insertClause: INSERT quadPattern ; usingClause: USING (iri | NAMED iri) ; -graphOrDefault: DEFAULT | GRAPH iri ; +graphOrDefault: DEFAULT | GRAPH? iri ; graphRef: GRAPH iri ; diff --git a/src/parser/sparqlParser/generated/SparqlAutomatic.interp b/src/parser/sparqlParser/generated/SparqlAutomatic.interp index d9e3d98064..9a41cd2b02 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomatic.interp +++ b/src/parser/sparqlParser/generated/SparqlAutomatic.interp @@ -517,4 +517,4 @@ pnameNs atn: -[4, 1, 176, 1655, 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, 1, 142, 3, 142, 1598, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1605, 8, 142, 1, 142, 1, 142, 3, 142, 1609, 8, 142, 1, 143, 1, 143, 3, 143, 1613, 8, 143, 1, 144, 1, 144, 1, 144, 1, 144, 3, 144, 1619, 8, 144, 1, 145, 1, 145, 1, 145, 3, 145, 1624, 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, 1637, 8, 151, 1, 151, 1, 151, 3, 151, 1641, 8, 151, 1, 152, 1, 152, 3, 152, 1645, 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, 146, 147, 1, 0, 150, 152, 1, 0, 153, 155, 1, 0, 156, 158, 1, 0, 28, 29, 1, 0, 160, 163, 2, 0, 145, 145, 166, 166, 1771, 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, 1608, 1, 0, 0, 0, 286, 1610, 1, 0, 0, 0, 288, 1614, 1, 0, 0, 0, 290, 1623, 1, 0, 0, 0, 292, 1625, 1, 0, 0, 0, 294, 1627, 1, 0, 0, 0, 296, 1629, 1, 0, 0, 0, 298, 1631, 1, 0, 0, 0, 300, 1633, 1, 0, 0, 0, 302, 1636, 1, 0, 0, 0, 304, 1644, 1, 0, 0, 0, 306, 1646, 1, 0, 0, 0, 308, 1648, 1, 0, 0, 0, 310, 1650, 1, 0, 0, 0, 312, 1652, 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, 143, 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, 165, 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, 165, 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, 165, 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, 165, 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, 150, 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, 165, 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, 165, 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, 165, 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, 165, 0, 0, 1398, 1399, 5, 113, 0, 0, 1399, 1488, 5, 165, 0, 0, 1400, 1401, 5, 114, 0, 0, 1401, 1488, 5, 165, 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, 1609, 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, 1609, 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, 1609, 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, 1609, 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, 1609, 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, 1609, 1, 0, 0, 0, 1586, 1587, 5, 140, 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, 1592, 3, 234, 117, 0, 1592, 1593, 5, 3, 0, 0, 1593, 1609, 1, 0, 0, 0, 1594, 1595, 5, 43, 0, 0, 1595, 1597, 5, 2, 0, 0, 1596, 1598, 5, 33, 0, 0, 1597, 1596, 1, 0, 0, 0, 1597, 1598, 1, 0, 0, 0, 1598, 1599, 1, 0, 0, 0, 1599, 1604, 3, 234, 117, 0, 1600, 1601, 5, 6, 0, 0, 1601, 1602, 5, 141, 0, 0, 1602, 1603, 5, 20, 0, 0, 1603, 1605, 3, 300, 150, 0, 1604, 1600, 1, 0, 0, 0, 1604, 1605, 1, 0, 0, 0, 1605, 1606, 1, 0, 0, 0, 1606, 1607, 5, 3, 0, 0, 1607, 1609, 1, 0, 0, 0, 1608, 1536, 1, 0, 0, 0, 1608, 1546, 1, 0, 0, 0, 1608, 1554, 1, 0, 0, 0, 1608, 1562, 1, 0, 0, 0, 1608, 1570, 1, 0, 0, 0, 1608, 1578, 1, 0, 0, 0, 1608, 1586, 1, 0, 0, 0, 1608, 1594, 1, 0, 0, 0, 1609, 285, 1, 0, 0, 0, 1610, 1612, 3, 302, 151, 0, 1611, 1613, 3, 150, 75, 0, 1612, 1611, 1, 0, 0, 0, 1612, 1613, 1, 0, 0, 0, 1613, 287, 1, 0, 0, 0, 1614, 1618, 3, 300, 150, 0, 1615, 1619, 5, 148, 0, 0, 1616, 1617, 5, 27, 0, 0, 1617, 1619, 3, 302, 151, 0, 1618, 1615, 1, 0, 0, 0, 1618, 1616, 1, 0, 0, 0, 1618, 1619, 1, 0, 0, 0, 1619, 289, 1, 0, 0, 0, 1620, 1624, 3, 292, 146, 0, 1621, 1624, 3, 294, 147, 0, 1622, 1624, 3, 296, 148, 0, 1623, 1620, 1, 0, 0, 0, 1623, 1621, 1, 0, 0, 0, 1623, 1622, 1, 0, 0, 0, 1624, 291, 1, 0, 0, 0, 1625, 1626, 7, 4, 0, 0, 1626, 293, 1, 0, 0, 0, 1627, 1628, 7, 5, 0, 0, 1628, 295, 1, 0, 0, 0, 1629, 1630, 7, 6, 0, 0, 1630, 297, 1, 0, 0, 0, 1631, 1632, 7, 7, 0, 0, 1632, 299, 1, 0, 0, 0, 1633, 1634, 7, 8, 0, 0, 1634, 301, 1, 0, 0, 0, 1635, 1637, 5, 149, 0, 0, 1636, 1635, 1, 0, 0, 0, 1636, 1637, 1, 0, 0, 0, 1637, 1640, 1, 0, 0, 0, 1638, 1641, 3, 308, 154, 0, 1639, 1641, 3, 304, 152, 0, 1640, 1638, 1, 0, 0, 0, 1640, 1639, 1, 0, 0, 0, 1641, 303, 1, 0, 0, 0, 1642, 1645, 3, 310, 155, 0, 1643, 1645, 3, 312, 156, 0, 1644, 1642, 1, 0, 0, 0, 1644, 1643, 1, 0, 0, 0, 1645, 305, 1, 0, 0, 0, 1646, 1647, 7, 9, 0, 0, 1647, 307, 1, 0, 0, 0, 1648, 1649, 5, 142, 0, 0, 1649, 309, 1, 0, 0, 0, 1650, 1651, 5, 144, 0, 0, 1651, 311, 1, 0, 0, 0, 1652, 1653, 5, 143, 0, 0, 1653, 313, 1, 0, 0, 0, 162, 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, 1597, 1604, 1608, 1612, 1618, 1623, 1636, 1640, 1644] \ No newline at end of file +[4, 1, 176, 1657, 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, 3, 47, 687, 8, 47, 1, 47, 3, 47, 690, 8, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 3, 49, 699, 8, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 3, 52, 710, 8, 52, 1, 52, 1, 52, 3, 52, 714, 8, 52, 1, 52, 3, 52, 717, 8, 52, 5, 52, 719, 8, 52, 10, 52, 12, 52, 722, 9, 52, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 728, 8, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 3, 54, 735, 8, 54, 3, 54, 737, 8, 54, 1, 55, 1, 55, 1, 55, 3, 55, 742, 8, 55, 1, 55, 1, 55, 1, 56, 3, 56, 747, 8, 56, 1, 56, 5, 56, 750, 8, 56, 10, 56, 12, 56, 753, 9, 56, 1, 57, 1, 57, 3, 57, 757, 8, 57, 1, 57, 3, 57, 760, 8, 57, 1, 58, 1, 58, 1, 58, 3, 58, 765, 8, 58, 3, 58, 767, 8, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 777, 8, 59, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 3, 62, 788, 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, 805, 8, 65, 1, 66, 1, 66, 1, 66, 5, 66, 810, 8, 66, 10, 66, 12, 66, 813, 9, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 5, 67, 820, 8, 67, 10, 67, 12, 67, 823, 9, 67, 1, 67, 3, 67, 826, 8, 67, 1, 67, 1, 67, 5, 67, 830, 8, 67, 10, 67, 12, 67, 833, 9, 67, 1, 67, 1, 67, 1, 68, 1, 68, 5, 68, 839, 8, 68, 10, 68, 12, 68, 842, 9, 68, 1, 68, 1, 68, 3, 68, 846, 8, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 3, 69, 853, 8, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 5, 71, 861, 8, 71, 10, 71, 12, 71, 864, 9, 71, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 3, 73, 872, 8, 73, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 3, 75, 880, 8, 75, 1, 75, 1, 75, 1, 75, 5, 75, 885, 8, 75, 10, 75, 12, 75, 888, 9, 75, 1, 75, 1, 75, 3, 75, 892, 8, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 5, 76, 899, 8, 76, 10, 76, 12, 76, 902, 9, 76, 1, 76, 1, 76, 3, 76, 906, 8, 76, 1, 77, 1, 77, 3, 77, 910, 8, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 3, 78, 917, 8, 78, 3, 78, 919, 8, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 3, 79, 927, 8, 79, 1, 80, 3, 80, 930, 8, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 3, 81, 938, 8, 81, 5, 81, 940, 8, 81, 10, 81, 12, 81, 943, 9, 81, 1, 82, 1, 82, 3, 82, 947, 8, 82, 1, 83, 1, 83, 1, 83, 5, 83, 952, 8, 83, 10, 83, 12, 83, 955, 9, 83, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 3, 85, 965, 8, 85, 1, 86, 3, 86, 968, 8, 86, 1, 87, 1, 87, 1, 87, 3, 87, 973, 8, 87, 5, 87, 975, 8, 87, 10, 87, 12, 87, 978, 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, 992, 8, 92, 1, 93, 1, 93, 1, 93, 5, 93, 997, 8, 93, 10, 93, 12, 93, 1000, 9, 93, 1, 94, 1, 94, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 5, 96, 1009, 8, 96, 10, 96, 12, 96, 1012, 9, 96, 1, 97, 1, 97, 1, 97, 5, 97, 1017, 8, 97, 10, 97, 12, 97, 1020, 9, 97, 1, 98, 1, 98, 3, 98, 1024, 8, 98, 1, 99, 1, 99, 1, 99, 3, 99, 1029, 8, 99, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 3, 101, 1041, 8, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 5, 102, 1048, 8, 102, 10, 102, 12, 102, 1051, 9, 102, 3, 102, 1053, 8, 102, 1, 102, 3, 102, 1056, 8, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 103, 3, 103, 1063, 8, 103, 3, 103, 1065, 8, 103, 1, 104, 1, 104, 1, 105, 1, 105, 3, 105, 1071, 8, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 3, 107, 1079, 8, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 4, 109, 1087, 8, 109, 11, 109, 12, 109, 1088, 1, 109, 1, 109, 1, 110, 1, 110, 4, 110, 1095, 8, 110, 11, 110, 12, 110, 1096, 1, 110, 1, 110, 1, 111, 1, 111, 3, 111, 1103, 8, 111, 1, 112, 1, 112, 3, 112, 1107, 8, 112, 1, 113, 1, 113, 3, 113, 1111, 8, 113, 1, 114, 1, 114, 3, 114, 1115, 8, 114, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 3, 116, 1125, 8, 116, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 5, 118, 1132, 8, 118, 10, 118, 12, 118, 1135, 9, 118, 1, 119, 1, 119, 1, 119, 5, 119, 1140, 8, 119, 10, 119, 12, 119, 1143, 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, 1165, 8, 121, 1, 122, 1, 122, 1, 123, 1, 123, 5, 123, 1171, 8, 123, 10, 123, 12, 123, 1174, 9, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 3, 124, 1181, 8, 124, 1, 125, 1, 125, 1, 126, 1, 126, 1, 127, 1, 127, 3, 127, 1189, 8, 127, 1, 127, 5, 127, 1192, 8, 127, 10, 127, 12, 127, 1195, 9, 127, 1, 128, 1, 128, 5, 128, 1199, 8, 128, 10, 128, 12, 128, 1202, 9, 128, 1, 129, 1, 129, 3, 129, 1206, 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, 1221, 8, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 3, 133, 1230, 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, 1276, 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, 1490, 8, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 3, 136, 1499, 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, 1515, 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, 1528, 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, 1542, 8, 142, 1, 142, 1, 142, 3, 142, 1546, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1552, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1560, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1568, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1576, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1584, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1592, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1600, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1607, 8, 142, 1, 142, 1, 142, 3, 142, 1611, 8, 142, 1, 143, 1, 143, 3, 143, 1615, 8, 143, 1, 144, 1, 144, 1, 144, 1, 144, 3, 144, 1621, 8, 144, 1, 145, 1, 145, 1, 145, 3, 145, 1626, 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, 1639, 8, 151, 1, 151, 1, 151, 3, 151, 1643, 8, 151, 1, 152, 1, 152, 3, 152, 1647, 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, 146, 147, 1, 0, 150, 152, 1, 0, 153, 155, 1, 0, 156, 158, 1, 0, 28, 29, 1, 0, 160, 163, 2, 0, 145, 145, 166, 166, 1774, 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, 689, 1, 0, 0, 0, 96, 691, 1, 0, 0, 0, 98, 698, 1, 0, 0, 0, 100, 700, 1, 0, 0, 0, 102, 704, 1, 0, 0, 0, 104, 709, 1, 0, 0, 0, 106, 723, 1, 0, 0, 0, 108, 731, 1, 0, 0, 0, 110, 738, 1, 0, 0, 0, 112, 746, 1, 0, 0, 0, 114, 754, 1, 0, 0, 0, 116, 761, 1, 0, 0, 0, 118, 776, 1, 0, 0, 0, 120, 778, 1, 0, 0, 0, 122, 781, 1, 0, 0, 0, 124, 785, 1, 0, 0, 0, 126, 792, 1, 0, 0, 0, 128, 799, 1, 0, 0, 0, 130, 804, 1, 0, 0, 0, 132, 806, 1, 0, 0, 0, 134, 825, 1, 0, 0, 0, 136, 845, 1, 0, 0, 0, 138, 852, 1, 0, 0, 0, 140, 854, 1, 0, 0, 0, 142, 857, 1, 0, 0, 0, 144, 865, 1, 0, 0, 0, 146, 871, 1, 0, 0, 0, 148, 873, 1, 0, 0, 0, 150, 891, 1, 0, 0, 0, 152, 905, 1, 0, 0, 0, 154, 907, 1, 0, 0, 0, 156, 913, 1, 0, 0, 0, 158, 926, 1, 0, 0, 0, 160, 929, 1, 0, 0, 0, 162, 931, 1, 0, 0, 0, 164, 946, 1, 0, 0, 0, 166, 948, 1, 0, 0, 0, 168, 956, 1, 0, 0, 0, 170, 964, 1, 0, 0, 0, 172, 967, 1, 0, 0, 0, 174, 969, 1, 0, 0, 0, 176, 979, 1, 0, 0, 0, 178, 981, 1, 0, 0, 0, 180, 983, 1, 0, 0, 0, 182, 986, 1, 0, 0, 0, 184, 991, 1, 0, 0, 0, 186, 993, 1, 0, 0, 0, 188, 1001, 1, 0, 0, 0, 190, 1003, 1, 0, 0, 0, 192, 1005, 1, 0, 0, 0, 194, 1013, 1, 0, 0, 0, 196, 1021, 1, 0, 0, 0, 198, 1028, 1, 0, 0, 0, 200, 1030, 1, 0, 0, 0, 202, 1040, 1, 0, 0, 0, 204, 1055, 1, 0, 0, 0, 206, 1064, 1, 0, 0, 0, 208, 1066, 1, 0, 0, 0, 210, 1070, 1, 0, 0, 0, 212, 1072, 1, 0, 0, 0, 214, 1078, 1, 0, 0, 0, 216, 1080, 1, 0, 0, 0, 218, 1084, 1, 0, 0, 0, 220, 1092, 1, 0, 0, 0, 222, 1102, 1, 0, 0, 0, 224, 1106, 1, 0, 0, 0, 226, 1110, 1, 0, 0, 0, 228, 1114, 1, 0, 0, 0, 230, 1116, 1, 0, 0, 0, 232, 1124, 1, 0, 0, 0, 234, 1126, 1, 0, 0, 0, 236, 1128, 1, 0, 0, 0, 238, 1136, 1, 0, 0, 0, 240, 1144, 1, 0, 0, 0, 242, 1146, 1, 0, 0, 0, 244, 1166, 1, 0, 0, 0, 246, 1168, 1, 0, 0, 0, 248, 1180, 1, 0, 0, 0, 250, 1182, 1, 0, 0, 0, 252, 1184, 1, 0, 0, 0, 254, 1188, 1, 0, 0, 0, 256, 1196, 1, 0, 0, 0, 258, 1205, 1, 0, 0, 0, 260, 1207, 1, 0, 0, 0, 262, 1210, 1, 0, 0, 0, 264, 1220, 1, 0, 0, 0, 266, 1229, 1, 0, 0, 0, 268, 1231, 1, 0, 0, 0, 270, 1489, 1, 0, 0, 0, 272, 1491, 1, 0, 0, 0, 274, 1502, 1, 0, 0, 0, 276, 1507, 1, 0, 0, 0, 278, 1518, 1, 0, 0, 0, 280, 1531, 1, 0, 0, 0, 282, 1534, 1, 0, 0, 0, 284, 1610, 1, 0, 0, 0, 286, 1612, 1, 0, 0, 0, 288, 1616, 1, 0, 0, 0, 290, 1625, 1, 0, 0, 0, 292, 1627, 1, 0, 0, 0, 294, 1629, 1, 0, 0, 0, 296, 1631, 1, 0, 0, 0, 298, 1633, 1, 0, 0, 0, 300, 1635, 1, 0, 0, 0, 302, 1638, 1, 0, 0, 0, 304, 1646, 1, 0, 0, 0, 306, 1648, 1, 0, 0, 0, 308, 1650, 1, 0, 0, 0, 310, 1652, 1, 0, 0, 0, 312, 1654, 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, 143, 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, 690, 5, 68, 0, 0, 685, 687, 5, 69, 0, 0, 686, 685, 1, 0, 0, 0, 686, 687, 1, 0, 0, 0, 687, 688, 1, 0, 0, 0, 688, 690, 3, 302, 151, 0, 689, 684, 1, 0, 0, 0, 689, 686, 1, 0, 0, 0, 690, 95, 1, 0, 0, 0, 691, 692, 5, 69, 0, 0, 692, 693, 3, 302, 151, 0, 693, 97, 1, 0, 0, 0, 694, 699, 3, 96, 48, 0, 695, 699, 5, 68, 0, 0, 696, 699, 5, 41, 0, 0, 697, 699, 5, 70, 0, 0, 698, 694, 1, 0, 0, 0, 698, 695, 1, 0, 0, 0, 698, 696, 1, 0, 0, 0, 698, 697, 1, 0, 0, 0, 699, 99, 1, 0, 0, 0, 700, 701, 5, 4, 0, 0, 701, 702, 3, 104, 52, 0, 702, 703, 5, 5, 0, 0, 703, 101, 1, 0, 0, 0, 704, 705, 5, 4, 0, 0, 705, 706, 3, 104, 52, 0, 706, 707, 5, 5, 0, 0, 707, 103, 1, 0, 0, 0, 708, 710, 3, 108, 54, 0, 709, 708, 1, 0, 0, 0, 709, 710, 1, 0, 0, 0, 710, 720, 1, 0, 0, 0, 711, 713, 3, 106, 53, 0, 712, 714, 5, 7, 0, 0, 713, 712, 1, 0, 0, 0, 713, 714, 1, 0, 0, 0, 714, 716, 1, 0, 0, 0, 715, 717, 3, 108, 54, 0, 716, 715, 1, 0, 0, 0, 716, 717, 1, 0, 0, 0, 717, 719, 1, 0, 0, 0, 718, 711, 1, 0, 0, 0, 719, 722, 1, 0, 0, 0, 720, 718, 1, 0, 0, 0, 720, 721, 1, 0, 0, 0, 721, 105, 1, 0, 0, 0, 722, 720, 1, 0, 0, 0, 723, 724, 5, 69, 0, 0, 724, 725, 3, 228, 114, 0, 725, 727, 5, 4, 0, 0, 726, 728, 3, 108, 54, 0, 727, 726, 1, 0, 0, 0, 727, 728, 1, 0, 0, 0, 728, 729, 1, 0, 0, 0, 729, 730, 5, 5, 0, 0, 730, 107, 1, 0, 0, 0, 731, 736, 3, 158, 79, 0, 732, 734, 5, 7, 0, 0, 733, 735, 3, 108, 54, 0, 734, 733, 1, 0, 0, 0, 734, 735, 1, 0, 0, 0, 735, 737, 1, 0, 0, 0, 736, 732, 1, 0, 0, 0, 736, 737, 1, 0, 0, 0, 737, 109, 1, 0, 0, 0, 738, 741, 5, 4, 0, 0, 739, 742, 3, 12, 6, 0, 740, 742, 3, 112, 56, 0, 741, 739, 1, 0, 0, 0, 741, 740, 1, 0, 0, 0, 742, 743, 1, 0, 0, 0, 743, 744, 5, 5, 0, 0, 744, 111, 1, 0, 0, 0, 745, 747, 3, 116, 58, 0, 746, 745, 1, 0, 0, 0, 746, 747, 1, 0, 0, 0, 747, 751, 1, 0, 0, 0, 748, 750, 3, 114, 57, 0, 749, 748, 1, 0, 0, 0, 750, 753, 1, 0, 0, 0, 751, 749, 1, 0, 0, 0, 751, 752, 1, 0, 0, 0, 752, 113, 1, 0, 0, 0, 753, 751, 1, 0, 0, 0, 754, 756, 3, 118, 59, 0, 755, 757, 5, 7, 0, 0, 756, 755, 1, 0, 0, 0, 756, 757, 1, 0, 0, 0, 757, 759, 1, 0, 0, 0, 758, 760, 3, 116, 58, 0, 759, 758, 1, 0, 0, 0, 759, 760, 1, 0, 0, 0, 760, 115, 1, 0, 0, 0, 761, 766, 3, 170, 85, 0, 762, 764, 5, 7, 0, 0, 763, 765, 3, 116, 58, 0, 764, 763, 1, 0, 0, 0, 764, 765, 1, 0, 0, 0, 765, 767, 1, 0, 0, 0, 766, 762, 1, 0, 0, 0, 766, 767, 1, 0, 0, 0, 767, 117, 1, 0, 0, 0, 768, 777, 3, 142, 71, 0, 769, 777, 3, 120, 60, 0, 770, 777, 3, 140, 70, 0, 771, 777, 3, 122, 61, 0, 772, 777, 3, 124, 62, 0, 773, 777, 3, 144, 72, 0, 774, 777, 3, 126, 63, 0, 775, 777, 3, 128, 64, 0, 776, 768, 1, 0, 0, 0, 776, 769, 1, 0, 0, 0, 776, 770, 1, 0, 0, 0, 776, 771, 1, 0, 0, 0, 776, 772, 1, 0, 0, 0, 776, 773, 1, 0, 0, 0, 776, 774, 1, 0, 0, 0, 776, 775, 1, 0, 0, 0, 777, 119, 1, 0, 0, 0, 778, 779, 5, 71, 0, 0, 779, 780, 3, 110, 55, 0, 780, 121, 1, 0, 0, 0, 781, 782, 5, 69, 0, 0, 782, 783, 3, 228, 114, 0, 783, 784, 3, 110, 55, 0, 784, 123, 1, 0, 0, 0, 785, 787, 5, 72, 0, 0, 786, 788, 5, 54, 0, 0, 787, 786, 1, 0, 0, 0, 787, 788, 1, 0, 0, 0, 788, 789, 1, 0, 0, 0, 789, 790, 3, 228, 114, 0, 790, 791, 3, 110, 55, 0, 791, 125, 1, 0, 0, 0, 792, 793, 5, 73, 0, 0, 793, 794, 5, 2, 0, 0, 794, 795, 3, 234, 117, 0, 795, 796, 5, 35, 0, 0, 796, 797, 3, 230, 115, 0, 797, 798, 5, 3, 0, 0, 798, 127, 1, 0, 0, 0, 799, 800, 5, 52, 0, 0, 800, 801, 3, 130, 65, 0, 801, 129, 1, 0, 0, 0, 802, 805, 3, 132, 66, 0, 803, 805, 3, 134, 67, 0, 804, 802, 1, 0, 0, 0, 804, 803, 1, 0, 0, 0, 805, 131, 1, 0, 0, 0, 806, 807, 3, 230, 115, 0, 807, 811, 5, 4, 0, 0, 808, 810, 3, 138, 69, 0, 809, 808, 1, 0, 0, 0, 810, 813, 1, 0, 0, 0, 811, 809, 1, 0, 0, 0, 811, 812, 1, 0, 0, 0, 812, 814, 1, 0, 0, 0, 813, 811, 1, 0, 0, 0, 814, 815, 5, 5, 0, 0, 815, 133, 1, 0, 0, 0, 816, 826, 5, 165, 0, 0, 817, 821, 5, 2, 0, 0, 818, 820, 3, 230, 115, 0, 819, 818, 1, 0, 0, 0, 820, 823, 1, 0, 0, 0, 821, 819, 1, 0, 0, 0, 821, 822, 1, 0, 0, 0, 822, 824, 1, 0, 0, 0, 823, 821, 1, 0, 0, 0, 824, 826, 5, 3, 0, 0, 825, 816, 1, 0, 0, 0, 825, 817, 1, 0, 0, 0, 826, 827, 1, 0, 0, 0, 827, 831, 5, 4, 0, 0, 828, 830, 3, 136, 68, 0, 829, 828, 1, 0, 0, 0, 830, 833, 1, 0, 0, 0, 831, 829, 1, 0, 0, 0, 831, 832, 1, 0, 0, 0, 832, 834, 1, 0, 0, 0, 833, 831, 1, 0, 0, 0, 834, 835, 5, 5, 0, 0, 835, 135, 1, 0, 0, 0, 836, 840, 5, 2, 0, 0, 837, 839, 3, 138, 69, 0, 838, 837, 1, 0, 0, 0, 839, 842, 1, 0, 0, 0, 840, 838, 1, 0, 0, 0, 840, 841, 1, 0, 0, 0, 841, 843, 1, 0, 0, 0, 842, 840, 1, 0, 0, 0, 843, 846, 5, 3, 0, 0, 844, 846, 5, 165, 0, 0, 845, 836, 1, 0, 0, 0, 845, 844, 1, 0, 0, 0, 846, 137, 1, 0, 0, 0, 847, 853, 3, 302, 151, 0, 848, 853, 3, 288, 144, 0, 849, 853, 3, 290, 145, 0, 850, 853, 3, 298, 149, 0, 851, 853, 5, 74, 0, 0, 852, 847, 1, 0, 0, 0, 852, 848, 1, 0, 0, 0, 852, 849, 1, 0, 0, 0, 852, 850, 1, 0, 0, 0, 852, 851, 1, 0, 0, 0, 853, 139, 1, 0, 0, 0, 854, 855, 5, 75, 0, 0, 855, 856, 3, 110, 55, 0, 856, 141, 1, 0, 0, 0, 857, 862, 3, 110, 55, 0, 858, 859, 5, 76, 0, 0, 859, 861, 3, 110, 55, 0, 860, 858, 1, 0, 0, 0, 861, 864, 1, 0, 0, 0, 862, 860, 1, 0, 0, 0, 862, 863, 1, 0, 0, 0, 863, 143, 1, 0, 0, 0, 864, 862, 1, 0, 0, 0, 865, 866, 5, 77, 0, 0, 866, 867, 3, 146, 73, 0, 867, 145, 1, 0, 0, 0, 868, 872, 3, 268, 134, 0, 869, 872, 3, 270, 135, 0, 870, 872, 3, 148, 74, 0, 871, 868, 1, 0, 0, 0, 871, 869, 1, 0, 0, 0, 871, 870, 1, 0, 0, 0, 872, 147, 1, 0, 0, 0, 873, 874, 3, 302, 151, 0, 874, 875, 3, 150, 75, 0, 875, 149, 1, 0, 0, 0, 876, 892, 5, 165, 0, 0, 877, 879, 5, 2, 0, 0, 878, 880, 5, 33, 0, 0, 879, 878, 1, 0, 0, 0, 879, 880, 1, 0, 0, 0, 880, 881, 1, 0, 0, 0, 881, 886, 3, 234, 117, 0, 882, 883, 5, 8, 0, 0, 883, 885, 3, 234, 117, 0, 884, 882, 1, 0, 0, 0, 885, 888, 1, 0, 0, 0, 886, 884, 1, 0, 0, 0, 886, 887, 1, 0, 0, 0, 887, 889, 1, 0, 0, 0, 888, 886, 1, 0, 0, 0, 889, 890, 5, 3, 0, 0, 890, 892, 1, 0, 0, 0, 891, 876, 1, 0, 0, 0, 891, 877, 1, 0, 0, 0, 892, 151, 1, 0, 0, 0, 893, 906, 5, 165, 0, 0, 894, 895, 5, 2, 0, 0, 895, 900, 3, 234, 117, 0, 896, 897, 5, 8, 0, 0, 897, 899, 3, 234, 117, 0, 898, 896, 1, 0, 0, 0, 899, 902, 1, 0, 0, 0, 900, 898, 1, 0, 0, 0, 900, 901, 1, 0, 0, 0, 901, 903, 1, 0, 0, 0, 902, 900, 1, 0, 0, 0, 903, 904, 5, 3, 0, 0, 904, 906, 1, 0, 0, 0, 905, 893, 1, 0, 0, 0, 905, 894, 1, 0, 0, 0, 906, 153, 1, 0, 0, 0, 907, 909, 5, 4, 0, 0, 908, 910, 3, 156, 78, 0, 909, 908, 1, 0, 0, 0, 909, 910, 1, 0, 0, 0, 910, 911, 1, 0, 0, 0, 911, 912, 5, 5, 0, 0, 912, 155, 1, 0, 0, 0, 913, 918, 3, 158, 79, 0, 914, 916, 5, 7, 0, 0, 915, 917, 3, 156, 78, 0, 916, 915, 1, 0, 0, 0, 916, 917, 1, 0, 0, 0, 917, 919, 1, 0, 0, 0, 918, 914, 1, 0, 0, 0, 918, 919, 1, 0, 0, 0, 919, 157, 1, 0, 0, 0, 920, 921, 3, 226, 113, 0, 921, 922, 3, 162, 81, 0, 922, 927, 1, 0, 0, 0, 923, 924, 3, 210, 105, 0, 924, 925, 3, 160, 80, 0, 925, 927, 1, 0, 0, 0, 926, 920, 1, 0, 0, 0, 926, 923, 1, 0, 0, 0, 927, 159, 1, 0, 0, 0, 928, 930, 3, 162, 81, 0, 929, 928, 1, 0, 0, 0, 929, 930, 1, 0, 0, 0, 930, 161, 1, 0, 0, 0, 931, 932, 3, 164, 82, 0, 932, 941, 3, 166, 83, 0, 933, 937, 5, 6, 0, 0, 934, 935, 3, 164, 82, 0, 935, 936, 3, 166, 83, 0, 936, 938, 1, 0, 0, 0, 937, 934, 1, 0, 0, 0, 937, 938, 1, 0, 0, 0, 938, 940, 1, 0, 0, 0, 939, 933, 1, 0, 0, 0, 940, 943, 1, 0, 0, 0, 941, 939, 1, 0, 0, 0, 941, 942, 1, 0, 0, 0, 942, 163, 1, 0, 0, 0, 943, 941, 1, 0, 0, 0, 944, 947, 3, 228, 114, 0, 945, 947, 5, 9, 0, 0, 946, 944, 1, 0, 0, 0, 946, 945, 1, 0, 0, 0, 947, 165, 1, 0, 0, 0, 948, 953, 3, 168, 84, 0, 949, 950, 5, 8, 0, 0, 950, 952, 3, 168, 84, 0, 951, 949, 1, 0, 0, 0, 952, 955, 1, 0, 0, 0, 953, 951, 1, 0, 0, 0, 953, 954, 1, 0, 0, 0, 954, 167, 1, 0, 0, 0, 955, 953, 1, 0, 0, 0, 956, 957, 3, 222, 111, 0, 957, 169, 1, 0, 0, 0, 958, 959, 3, 226, 113, 0, 959, 960, 3, 174, 87, 0, 960, 965, 1, 0, 0, 0, 961, 962, 3, 214, 107, 0, 962, 963, 3, 172, 86, 0, 963, 965, 1, 0, 0, 0, 964, 958, 1, 0, 0, 0, 964, 961, 1, 0, 0, 0, 965, 171, 1, 0, 0, 0, 966, 968, 3, 174, 87, 0, 967, 966, 1, 0, 0, 0, 967, 968, 1, 0, 0, 0, 968, 173, 1, 0, 0, 0, 969, 976, 3, 182, 91, 0, 970, 972, 5, 6, 0, 0, 971, 973, 3, 180, 90, 0, 972, 971, 1, 0, 0, 0, 972, 973, 1, 0, 0, 0, 973, 975, 1, 0, 0, 0, 974, 970, 1, 0, 0, 0, 975, 978, 1, 0, 0, 0, 976, 974, 1, 0, 0, 0, 976, 977, 1, 0, 0, 0, 977, 175, 1, 0, 0, 0, 978, 976, 1, 0, 0, 0, 979, 980, 3, 190, 95, 0, 980, 177, 1, 0, 0, 0, 981, 982, 3, 230, 115, 0, 982, 179, 1, 0, 0, 0, 983, 984, 3, 184, 92, 0, 984, 985, 3, 166, 83, 0, 985, 181, 1, 0, 0, 0, 986, 987, 3, 184, 92, 0, 987, 988, 3, 186, 93, 0, 988, 183, 1, 0, 0, 0, 989, 992, 3, 176, 88, 0, 990, 992, 3, 178, 89, 0, 991, 989, 1, 0, 0, 0, 991, 990, 1, 0, 0, 0, 992, 185, 1, 0, 0, 0, 993, 998, 3, 188, 94, 0, 994, 995, 5, 8, 0, 0, 995, 997, 3, 188, 94, 0, 996, 994, 1, 0, 0, 0, 997, 1000, 1, 0, 0, 0, 998, 996, 1, 0, 0, 0, 998, 999, 1, 0, 0, 0, 999, 187, 1, 0, 0, 0, 1000, 998, 1, 0, 0, 0, 1001, 1002, 3, 224, 112, 0, 1002, 189, 1, 0, 0, 0, 1003, 1004, 3, 192, 96, 0, 1004, 191, 1, 0, 0, 0, 1005, 1010, 3, 194, 97, 0, 1006, 1007, 5, 10, 0, 0, 1007, 1009, 3, 194, 97, 0, 1008, 1006, 1, 0, 0, 0, 1009, 1012, 1, 0, 0, 0, 1010, 1008, 1, 0, 0, 0, 1010, 1011, 1, 0, 0, 0, 1011, 193, 1, 0, 0, 0, 1012, 1010, 1, 0, 0, 0, 1013, 1018, 3, 198, 99, 0, 1014, 1015, 5, 11, 0, 0, 1015, 1017, 3, 198, 99, 0, 1016, 1014, 1, 0, 0, 0, 1017, 1020, 1, 0, 0, 0, 1018, 1016, 1, 0, 0, 0, 1018, 1019, 1, 0, 0, 0, 1019, 195, 1, 0, 0, 0, 1020, 1018, 1, 0, 0, 0, 1021, 1023, 3, 202, 101, 0, 1022, 1024, 3, 200, 100, 0, 1023, 1022, 1, 0, 0, 0, 1023, 1024, 1, 0, 0, 0, 1024, 197, 1, 0, 0, 0, 1025, 1029, 3, 196, 98, 0, 1026, 1027, 5, 12, 0, 0, 1027, 1029, 3, 196, 98, 0, 1028, 1025, 1, 0, 0, 0, 1028, 1026, 1, 0, 0, 0, 1029, 199, 1, 0, 0, 0, 1030, 1031, 7, 2, 0, 0, 1031, 201, 1, 0, 0, 0, 1032, 1041, 3, 302, 151, 0, 1033, 1041, 5, 9, 0, 0, 1034, 1035, 5, 15, 0, 0, 1035, 1041, 3, 204, 102, 0, 1036, 1037, 5, 2, 0, 0, 1037, 1038, 3, 190, 95, 0, 1038, 1039, 5, 3, 0, 0, 1039, 1041, 1, 0, 0, 0, 1040, 1032, 1, 0, 0, 0, 1040, 1033, 1, 0, 0, 0, 1040, 1034, 1, 0, 0, 0, 1040, 1036, 1, 0, 0, 0, 1041, 203, 1, 0, 0, 0, 1042, 1056, 3, 206, 103, 0, 1043, 1052, 5, 2, 0, 0, 1044, 1049, 3, 206, 103, 0, 1045, 1046, 5, 10, 0, 0, 1046, 1048, 3, 206, 103, 0, 1047, 1045, 1, 0, 0, 0, 1048, 1051, 1, 0, 0, 0, 1049, 1047, 1, 0, 0, 0, 1049, 1050, 1, 0, 0, 0, 1050, 1053, 1, 0, 0, 0, 1051, 1049, 1, 0, 0, 0, 1052, 1044, 1, 0, 0, 0, 1052, 1053, 1, 0, 0, 0, 1053, 1054, 1, 0, 0, 0, 1054, 1056, 5, 3, 0, 0, 1055, 1042, 1, 0, 0, 0, 1055, 1043, 1, 0, 0, 0, 1056, 205, 1, 0, 0, 0, 1057, 1065, 3, 302, 151, 0, 1058, 1065, 5, 9, 0, 0, 1059, 1062, 5, 12, 0, 0, 1060, 1063, 3, 302, 151, 0, 1061, 1063, 5, 9, 0, 0, 1062, 1060, 1, 0, 0, 0, 1062, 1061, 1, 0, 0, 0, 1063, 1065, 1, 0, 0, 0, 1064, 1057, 1, 0, 0, 0, 1064, 1058, 1, 0, 0, 0, 1064, 1059, 1, 0, 0, 0, 1065, 207, 1, 0, 0, 0, 1066, 1067, 5, 150, 0, 0, 1067, 209, 1, 0, 0, 0, 1068, 1071, 3, 218, 109, 0, 1069, 1071, 3, 212, 106, 0, 1070, 1068, 1, 0, 0, 0, 1070, 1069, 1, 0, 0, 0, 1071, 211, 1, 0, 0, 0, 1072, 1073, 5, 16, 0, 0, 1073, 1074, 3, 162, 81, 0, 1074, 1075, 5, 17, 0, 0, 1075, 213, 1, 0, 0, 0, 1076, 1079, 3, 220, 110, 0, 1077, 1079, 3, 216, 108, 0, 1078, 1076, 1, 0, 0, 0, 1078, 1077, 1, 0, 0, 0, 1079, 215, 1, 0, 0, 0, 1080, 1081, 5, 16, 0, 0, 1081, 1082, 3, 174, 87, 0, 1082, 1083, 5, 17, 0, 0, 1083, 217, 1, 0, 0, 0, 1084, 1086, 5, 2, 0, 0, 1085, 1087, 3, 222, 111, 0, 1086, 1085, 1, 0, 0, 0, 1087, 1088, 1, 0, 0, 0, 1088, 1086, 1, 0, 0, 0, 1088, 1089, 1, 0, 0, 0, 1089, 1090, 1, 0, 0, 0, 1090, 1091, 5, 3, 0, 0, 1091, 219, 1, 0, 0, 0, 1092, 1094, 5, 2, 0, 0, 1093, 1095, 3, 224, 112, 0, 1094, 1093, 1, 0, 0, 0, 1095, 1096, 1, 0, 0, 0, 1096, 1094, 1, 0, 0, 0, 1096, 1097, 1, 0, 0, 0, 1097, 1098, 1, 0, 0, 0, 1098, 1099, 5, 3, 0, 0, 1099, 221, 1, 0, 0, 0, 1100, 1103, 3, 226, 113, 0, 1101, 1103, 3, 210, 105, 0, 1102, 1100, 1, 0, 0, 0, 1102, 1101, 1, 0, 0, 0, 1103, 223, 1, 0, 0, 0, 1104, 1107, 3, 226, 113, 0, 1105, 1107, 3, 214, 107, 0, 1106, 1104, 1, 0, 0, 0, 1106, 1105, 1, 0, 0, 0, 1107, 225, 1, 0, 0, 0, 1108, 1111, 3, 230, 115, 0, 1109, 1111, 3, 232, 116, 0, 1110, 1108, 1, 0, 0, 0, 1110, 1109, 1, 0, 0, 0, 1111, 227, 1, 0, 0, 0, 1112, 1115, 3, 230, 115, 0, 1113, 1115, 3, 302, 151, 0, 1114, 1112, 1, 0, 0, 0, 1114, 1113, 1, 0, 0, 0, 1115, 229, 1, 0, 0, 0, 1116, 1117, 7, 3, 0, 0, 1117, 231, 1, 0, 0, 0, 1118, 1125, 3, 302, 151, 0, 1119, 1125, 3, 288, 144, 0, 1120, 1125, 3, 290, 145, 0, 1121, 1125, 3, 298, 149, 0, 1122, 1125, 3, 306, 153, 0, 1123, 1125, 5, 165, 0, 0, 1124, 1118, 1, 0, 0, 0, 1124, 1119, 1, 0, 0, 0, 1124, 1120, 1, 0, 0, 0, 1124, 1121, 1, 0, 0, 0, 1124, 1122, 1, 0, 0, 0, 1124, 1123, 1, 0, 0, 0, 1125, 233, 1, 0, 0, 0, 1126, 1127, 3, 236, 118, 0, 1127, 235, 1, 0, 0, 0, 1128, 1133, 3, 238, 119, 0, 1129, 1130, 5, 18, 0, 0, 1130, 1132, 3, 238, 119, 0, 1131, 1129, 1, 0, 0, 0, 1132, 1135, 1, 0, 0, 0, 1133, 1131, 1, 0, 0, 0, 1133, 1134, 1, 0, 0, 0, 1134, 237, 1, 0, 0, 0, 1135, 1133, 1, 0, 0, 0, 1136, 1141, 3, 240, 120, 0, 1137, 1138, 5, 19, 0, 0, 1138, 1140, 3, 240, 120, 0, 1139, 1137, 1, 0, 0, 0, 1140, 1143, 1, 0, 0, 0, 1141, 1139, 1, 0, 0, 0, 1141, 1142, 1, 0, 0, 0, 1142, 239, 1, 0, 0, 0, 1143, 1141, 1, 0, 0, 0, 1144, 1145, 3, 242, 121, 0, 1145, 241, 1, 0, 0, 0, 1146, 1164, 3, 244, 122, 0, 1147, 1148, 5, 20, 0, 0, 1148, 1165, 3, 244, 122, 0, 1149, 1150, 5, 21, 0, 0, 1150, 1165, 3, 244, 122, 0, 1151, 1152, 5, 22, 0, 0, 1152, 1165, 3, 244, 122, 0, 1153, 1154, 5, 23, 0, 0, 1154, 1165, 3, 244, 122, 0, 1155, 1156, 5, 24, 0, 0, 1156, 1165, 3, 244, 122, 0, 1157, 1158, 5, 25, 0, 0, 1158, 1165, 3, 244, 122, 0, 1159, 1160, 5, 79, 0, 0, 1160, 1165, 3, 152, 76, 0, 1161, 1162, 5, 78, 0, 0, 1162, 1163, 5, 79, 0, 0, 1163, 1165, 3, 152, 76, 0, 1164, 1147, 1, 0, 0, 0, 1164, 1149, 1, 0, 0, 0, 1164, 1151, 1, 0, 0, 0, 1164, 1153, 1, 0, 0, 0, 1164, 1155, 1, 0, 0, 0, 1164, 1157, 1, 0, 0, 0, 1164, 1159, 1, 0, 0, 0, 1164, 1161, 1, 0, 0, 0, 1164, 1165, 1, 0, 0, 0, 1165, 243, 1, 0, 0, 0, 1166, 1167, 3, 246, 123, 0, 1167, 245, 1, 0, 0, 0, 1168, 1172, 3, 256, 128, 0, 1169, 1171, 3, 248, 124, 0, 1170, 1169, 1, 0, 0, 0, 1171, 1174, 1, 0, 0, 0, 1172, 1170, 1, 0, 0, 0, 1172, 1173, 1, 0, 0, 0, 1173, 247, 1, 0, 0, 0, 1174, 1172, 1, 0, 0, 0, 1175, 1176, 5, 13, 0, 0, 1176, 1181, 3, 250, 125, 0, 1177, 1178, 5, 26, 0, 0, 1178, 1181, 3, 252, 126, 0, 1179, 1181, 3, 254, 127, 0, 1180, 1175, 1, 0, 0, 0, 1180, 1177, 1, 0, 0, 0, 1180, 1179, 1, 0, 0, 0, 1181, 249, 1, 0, 0, 0, 1182, 1183, 3, 256, 128, 0, 1183, 251, 1, 0, 0, 0, 1184, 1185, 3, 256, 128, 0, 1185, 253, 1, 0, 0, 0, 1186, 1189, 3, 294, 147, 0, 1187, 1189, 3, 296, 148, 0, 1188, 1186, 1, 0, 0, 0, 1188, 1187, 1, 0, 0, 0, 1189, 1193, 1, 0, 0, 0, 1190, 1192, 3, 258, 129, 0, 1191, 1190, 1, 0, 0, 0, 1192, 1195, 1, 0, 0, 0, 1193, 1191, 1, 0, 0, 0, 1193, 1194, 1, 0, 0, 0, 1194, 255, 1, 0, 0, 0, 1195, 1193, 1, 0, 0, 0, 1196, 1200, 3, 264, 132, 0, 1197, 1199, 3, 258, 129, 0, 1198, 1197, 1, 0, 0, 0, 1199, 1202, 1, 0, 0, 0, 1200, 1198, 1, 0, 0, 0, 1200, 1201, 1, 0, 0, 0, 1201, 257, 1, 0, 0, 0, 1202, 1200, 1, 0, 0, 0, 1203, 1206, 3, 260, 130, 0, 1204, 1206, 3, 262, 131, 0, 1205, 1203, 1, 0, 0, 0, 1205, 1204, 1, 0, 0, 0, 1206, 259, 1, 0, 0, 0, 1207, 1208, 5, 1, 0, 0, 1208, 1209, 3, 264, 132, 0, 1209, 261, 1, 0, 0, 0, 1210, 1211, 5, 11, 0, 0, 1211, 1212, 3, 264, 132, 0, 1212, 263, 1, 0, 0, 0, 1213, 1214, 5, 15, 0, 0, 1214, 1221, 3, 266, 133, 0, 1215, 1216, 5, 13, 0, 0, 1216, 1221, 3, 266, 133, 0, 1217, 1218, 5, 26, 0, 0, 1218, 1221, 3, 266, 133, 0, 1219, 1221, 3, 266, 133, 0, 1220, 1213, 1, 0, 0, 0, 1220, 1215, 1, 0, 0, 0, 1220, 1217, 1, 0, 0, 0, 1220, 1219, 1, 0, 0, 0, 1221, 265, 1, 0, 0, 0, 1222, 1230, 3, 268, 134, 0, 1223, 1230, 3, 270, 135, 0, 1224, 1230, 3, 286, 143, 0, 1225, 1230, 3, 288, 144, 0, 1226, 1230, 3, 290, 145, 0, 1227, 1230, 3, 298, 149, 0, 1228, 1230, 3, 230, 115, 0, 1229, 1222, 1, 0, 0, 0, 1229, 1223, 1, 0, 0, 0, 1229, 1224, 1, 0, 0, 0, 1229, 1225, 1, 0, 0, 0, 1229, 1226, 1, 0, 0, 0, 1229, 1227, 1, 0, 0, 0, 1229, 1228, 1, 0, 0, 0, 1230, 267, 1, 0, 0, 0, 1231, 1232, 5, 2, 0, 0, 1232, 1233, 3, 234, 117, 0, 1233, 1234, 5, 3, 0, 0, 1234, 269, 1, 0, 0, 0, 1235, 1490, 3, 284, 142, 0, 1236, 1237, 5, 80, 0, 0, 1237, 1238, 5, 2, 0, 0, 1238, 1239, 3, 234, 117, 0, 1239, 1240, 5, 3, 0, 0, 1240, 1490, 1, 0, 0, 0, 1241, 1490, 3, 274, 137, 0, 1242, 1243, 5, 82, 0, 0, 1243, 1244, 5, 2, 0, 0, 1244, 1245, 3, 234, 117, 0, 1245, 1246, 5, 8, 0, 0, 1246, 1247, 3, 234, 117, 0, 1247, 1248, 5, 3, 0, 0, 1248, 1490, 1, 0, 0, 0, 1249, 1250, 5, 83, 0, 0, 1250, 1251, 5, 2, 0, 0, 1251, 1252, 3, 234, 117, 0, 1252, 1253, 5, 3, 0, 0, 1253, 1490, 1, 0, 0, 0, 1254, 1255, 5, 84, 0, 0, 1255, 1256, 5, 2, 0, 0, 1256, 1257, 3, 230, 115, 0, 1257, 1258, 5, 3, 0, 0, 1258, 1490, 1, 0, 0, 0, 1259, 1260, 5, 85, 0, 0, 1260, 1261, 5, 2, 0, 0, 1261, 1262, 3, 234, 117, 0, 1262, 1263, 5, 3, 0, 0, 1263, 1490, 1, 0, 0, 0, 1264, 1265, 5, 86, 0, 0, 1265, 1266, 5, 2, 0, 0, 1266, 1267, 3, 234, 117, 0, 1267, 1268, 5, 3, 0, 0, 1268, 1490, 1, 0, 0, 0, 1269, 1275, 5, 87, 0, 0, 1270, 1271, 5, 2, 0, 0, 1271, 1272, 3, 234, 117, 0, 1272, 1273, 5, 3, 0, 0, 1273, 1276, 1, 0, 0, 0, 1274, 1276, 5, 165, 0, 0, 1275, 1270, 1, 0, 0, 0, 1275, 1274, 1, 0, 0, 0, 1276, 1490, 1, 0, 0, 0, 1277, 1278, 5, 88, 0, 0, 1278, 1490, 5, 165, 0, 0, 1279, 1280, 5, 89, 0, 0, 1280, 1281, 5, 2, 0, 0, 1281, 1282, 3, 234, 117, 0, 1282, 1283, 5, 3, 0, 0, 1283, 1490, 1, 0, 0, 0, 1284, 1285, 5, 90, 0, 0, 1285, 1286, 5, 2, 0, 0, 1286, 1287, 3, 234, 117, 0, 1287, 1288, 5, 3, 0, 0, 1288, 1490, 1, 0, 0, 0, 1289, 1290, 5, 91, 0, 0, 1290, 1291, 5, 2, 0, 0, 1291, 1292, 3, 234, 117, 0, 1292, 1293, 5, 3, 0, 0, 1293, 1490, 1, 0, 0, 0, 1294, 1295, 5, 92, 0, 0, 1295, 1296, 5, 2, 0, 0, 1296, 1297, 3, 234, 117, 0, 1297, 1298, 5, 3, 0, 0, 1298, 1490, 1, 0, 0, 0, 1299, 1300, 5, 93, 0, 0, 1300, 1490, 3, 152, 76, 0, 1301, 1490, 3, 276, 138, 0, 1302, 1303, 5, 94, 0, 0, 1303, 1304, 5, 2, 0, 0, 1304, 1305, 3, 234, 117, 0, 1305, 1306, 5, 3, 0, 0, 1306, 1490, 1, 0, 0, 0, 1307, 1490, 3, 278, 139, 0, 1308, 1309, 5, 95, 0, 0, 1309, 1310, 5, 2, 0, 0, 1310, 1311, 3, 234, 117, 0, 1311, 1312, 5, 3, 0, 0, 1312, 1490, 1, 0, 0, 0, 1313, 1314, 5, 96, 0, 0, 1314, 1315, 5, 2, 0, 0, 1315, 1316, 3, 234, 117, 0, 1316, 1317, 5, 3, 0, 0, 1317, 1490, 1, 0, 0, 0, 1318, 1319, 5, 97, 0, 0, 1319, 1320, 5, 2, 0, 0, 1320, 1321, 3, 234, 117, 0, 1321, 1322, 5, 3, 0, 0, 1322, 1490, 1, 0, 0, 0, 1323, 1324, 5, 99, 0, 0, 1324, 1325, 5, 2, 0, 0, 1325, 1326, 3, 234, 117, 0, 1326, 1327, 5, 8, 0, 0, 1327, 1328, 3, 234, 117, 0, 1328, 1329, 5, 3, 0, 0, 1329, 1490, 1, 0, 0, 0, 1330, 1331, 5, 100, 0, 0, 1331, 1332, 5, 2, 0, 0, 1332, 1333, 3, 234, 117, 0, 1333, 1334, 5, 8, 0, 0, 1334, 1335, 3, 234, 117, 0, 1335, 1336, 5, 3, 0, 0, 1336, 1490, 1, 0, 0, 0, 1337, 1338, 5, 101, 0, 0, 1338, 1339, 5, 2, 0, 0, 1339, 1340, 3, 234, 117, 0, 1340, 1341, 5, 8, 0, 0, 1341, 1342, 3, 234, 117, 0, 1342, 1343, 5, 3, 0, 0, 1343, 1490, 1, 0, 0, 0, 1344, 1345, 5, 102, 0, 0, 1345, 1346, 5, 2, 0, 0, 1346, 1347, 3, 234, 117, 0, 1347, 1348, 5, 8, 0, 0, 1348, 1349, 3, 234, 117, 0, 1349, 1350, 5, 3, 0, 0, 1350, 1490, 1, 0, 0, 0, 1351, 1352, 5, 103, 0, 0, 1352, 1353, 5, 2, 0, 0, 1353, 1354, 3, 234, 117, 0, 1354, 1355, 5, 8, 0, 0, 1355, 1356, 3, 234, 117, 0, 1356, 1357, 5, 3, 0, 0, 1357, 1490, 1, 0, 0, 0, 1358, 1359, 5, 104, 0, 0, 1359, 1360, 5, 2, 0, 0, 1360, 1361, 3, 234, 117, 0, 1361, 1362, 5, 3, 0, 0, 1362, 1490, 1, 0, 0, 0, 1363, 1364, 5, 105, 0, 0, 1364, 1365, 5, 2, 0, 0, 1365, 1366, 3, 234, 117, 0, 1366, 1367, 5, 3, 0, 0, 1367, 1490, 1, 0, 0, 0, 1368, 1369, 5, 106, 0, 0, 1369, 1370, 5, 2, 0, 0, 1370, 1371, 3, 234, 117, 0, 1371, 1372, 5, 3, 0, 0, 1372, 1490, 1, 0, 0, 0, 1373, 1374, 5, 107, 0, 0, 1374, 1375, 5, 2, 0, 0, 1375, 1376, 3, 234, 117, 0, 1376, 1377, 5, 3, 0, 0, 1377, 1490, 1, 0, 0, 0, 1378, 1379, 5, 108, 0, 0, 1379, 1380, 5, 2, 0, 0, 1380, 1381, 3, 234, 117, 0, 1381, 1382, 5, 3, 0, 0, 1382, 1490, 1, 0, 0, 0, 1383, 1384, 5, 109, 0, 0, 1384, 1385, 5, 2, 0, 0, 1385, 1386, 3, 234, 117, 0, 1386, 1387, 5, 3, 0, 0, 1387, 1490, 1, 0, 0, 0, 1388, 1389, 5, 110, 0, 0, 1389, 1390, 5, 2, 0, 0, 1390, 1391, 3, 234, 117, 0, 1391, 1392, 5, 3, 0, 0, 1392, 1490, 1, 0, 0, 0, 1393, 1394, 5, 111, 0, 0, 1394, 1395, 5, 2, 0, 0, 1395, 1396, 3, 234, 117, 0, 1396, 1397, 5, 3, 0, 0, 1397, 1490, 1, 0, 0, 0, 1398, 1399, 5, 112, 0, 0, 1399, 1490, 5, 165, 0, 0, 1400, 1401, 5, 113, 0, 0, 1401, 1490, 5, 165, 0, 0, 1402, 1403, 5, 114, 0, 0, 1403, 1490, 5, 165, 0, 0, 1404, 1405, 5, 119, 0, 0, 1405, 1406, 5, 2, 0, 0, 1406, 1407, 3, 234, 117, 0, 1407, 1408, 5, 3, 0, 0, 1408, 1490, 1, 0, 0, 0, 1409, 1410, 5, 115, 0, 0, 1410, 1411, 5, 2, 0, 0, 1411, 1412, 3, 234, 117, 0, 1412, 1413, 5, 3, 0, 0, 1413, 1490, 1, 0, 0, 0, 1414, 1415, 5, 116, 0, 0, 1415, 1416, 5, 2, 0, 0, 1416, 1417, 3, 234, 117, 0, 1417, 1418, 5, 3, 0, 0, 1418, 1490, 1, 0, 0, 0, 1419, 1420, 5, 117, 0, 0, 1420, 1421, 5, 2, 0, 0, 1421, 1422, 3, 234, 117, 0, 1422, 1423, 5, 3, 0, 0, 1423, 1490, 1, 0, 0, 0, 1424, 1425, 5, 118, 0, 0, 1425, 1426, 5, 2, 0, 0, 1426, 1427, 3, 234, 117, 0, 1427, 1428, 5, 3, 0, 0, 1428, 1490, 1, 0, 0, 0, 1429, 1430, 5, 120, 0, 0, 1430, 1490, 3, 152, 76, 0, 1431, 1432, 5, 121, 0, 0, 1432, 1433, 5, 2, 0, 0, 1433, 1434, 3, 234, 117, 0, 1434, 1435, 5, 8, 0, 0, 1435, 1436, 3, 234, 117, 0, 1436, 1437, 5, 8, 0, 0, 1437, 1438, 3, 234, 117, 0, 1438, 1439, 5, 3, 0, 0, 1439, 1490, 1, 0, 0, 0, 1440, 1441, 5, 122, 0, 0, 1441, 1442, 5, 2, 0, 0, 1442, 1443, 3, 234, 117, 0, 1443, 1444, 5, 8, 0, 0, 1444, 1445, 3, 234, 117, 0, 1445, 1446, 5, 3, 0, 0, 1446, 1490, 1, 0, 0, 0, 1447, 1448, 5, 123, 0, 0, 1448, 1449, 5, 2, 0, 0, 1449, 1450, 3, 234, 117, 0, 1450, 1451, 5, 8, 0, 0, 1451, 1452, 3, 234, 117, 0, 1452, 1453, 5, 3, 0, 0, 1453, 1490, 1, 0, 0, 0, 1454, 1455, 5, 124, 0, 0, 1455, 1456, 5, 2, 0, 0, 1456, 1457, 3, 234, 117, 0, 1457, 1458, 5, 8, 0, 0, 1458, 1459, 3, 234, 117, 0, 1459, 1460, 5, 3, 0, 0, 1460, 1490, 1, 0, 0, 0, 1461, 1462, 5, 125, 0, 0, 1462, 1463, 5, 2, 0, 0, 1463, 1464, 3, 234, 117, 0, 1464, 1465, 5, 3, 0, 0, 1465, 1490, 1, 0, 0, 0, 1466, 1467, 5, 126, 0, 0, 1467, 1468, 5, 2, 0, 0, 1468, 1469, 3, 234, 117, 0, 1469, 1470, 5, 3, 0, 0, 1470, 1490, 1, 0, 0, 0, 1471, 1472, 5, 127, 0, 0, 1472, 1473, 5, 2, 0, 0, 1473, 1474, 3, 234, 117, 0, 1474, 1475, 5, 3, 0, 0, 1475, 1490, 1, 0, 0, 0, 1476, 1477, 5, 128, 0, 0, 1477, 1478, 5, 2, 0, 0, 1478, 1479, 3, 234, 117, 0, 1479, 1480, 5, 3, 0, 0, 1480, 1490, 1, 0, 0, 0, 1481, 1482, 5, 129, 0, 0, 1482, 1483, 5, 2, 0, 0, 1483, 1484, 3, 234, 117, 0, 1484, 1485, 5, 3, 0, 0, 1485, 1490, 1, 0, 0, 0, 1486, 1490, 3, 272, 136, 0, 1487, 1490, 3, 280, 140, 0, 1488, 1490, 3, 282, 141, 0, 1489, 1235, 1, 0, 0, 0, 1489, 1236, 1, 0, 0, 0, 1489, 1241, 1, 0, 0, 0, 1489, 1242, 1, 0, 0, 0, 1489, 1249, 1, 0, 0, 0, 1489, 1254, 1, 0, 0, 0, 1489, 1259, 1, 0, 0, 0, 1489, 1264, 1, 0, 0, 0, 1489, 1269, 1, 0, 0, 0, 1489, 1277, 1, 0, 0, 0, 1489, 1279, 1, 0, 0, 0, 1489, 1284, 1, 0, 0, 0, 1489, 1289, 1, 0, 0, 0, 1489, 1294, 1, 0, 0, 0, 1489, 1299, 1, 0, 0, 0, 1489, 1301, 1, 0, 0, 0, 1489, 1302, 1, 0, 0, 0, 1489, 1307, 1, 0, 0, 0, 1489, 1308, 1, 0, 0, 0, 1489, 1313, 1, 0, 0, 0, 1489, 1318, 1, 0, 0, 0, 1489, 1323, 1, 0, 0, 0, 1489, 1330, 1, 0, 0, 0, 1489, 1337, 1, 0, 0, 0, 1489, 1344, 1, 0, 0, 0, 1489, 1351, 1, 0, 0, 0, 1489, 1358, 1, 0, 0, 0, 1489, 1363, 1, 0, 0, 0, 1489, 1368, 1, 0, 0, 0, 1489, 1373, 1, 0, 0, 0, 1489, 1378, 1, 0, 0, 0, 1489, 1383, 1, 0, 0, 0, 1489, 1388, 1, 0, 0, 0, 1489, 1393, 1, 0, 0, 0, 1489, 1398, 1, 0, 0, 0, 1489, 1400, 1, 0, 0, 0, 1489, 1402, 1, 0, 0, 0, 1489, 1404, 1, 0, 0, 0, 1489, 1409, 1, 0, 0, 0, 1489, 1414, 1, 0, 0, 0, 1489, 1419, 1, 0, 0, 0, 1489, 1424, 1, 0, 0, 0, 1489, 1429, 1, 0, 0, 0, 1489, 1431, 1, 0, 0, 0, 1489, 1440, 1, 0, 0, 0, 1489, 1447, 1, 0, 0, 0, 1489, 1454, 1, 0, 0, 0, 1489, 1461, 1, 0, 0, 0, 1489, 1466, 1, 0, 0, 0, 1489, 1471, 1, 0, 0, 0, 1489, 1476, 1, 0, 0, 0, 1489, 1481, 1, 0, 0, 0, 1489, 1486, 1, 0, 0, 0, 1489, 1487, 1, 0, 0, 0, 1489, 1488, 1, 0, 0, 0, 1490, 271, 1, 0, 0, 0, 1491, 1492, 5, 130, 0, 0, 1492, 1493, 5, 2, 0, 0, 1493, 1494, 3, 234, 117, 0, 1494, 1495, 5, 8, 0, 0, 1495, 1498, 3, 234, 117, 0, 1496, 1497, 5, 8, 0, 0, 1497, 1499, 3, 234, 117, 0, 1498, 1496, 1, 0, 0, 0, 1498, 1499, 1, 0, 0, 0, 1499, 1500, 1, 0, 0, 0, 1500, 1501, 5, 3, 0, 0, 1501, 273, 1, 0, 0, 0, 1502, 1503, 5, 81, 0, 0, 1503, 1504, 5, 2, 0, 0, 1504, 1505, 3, 234, 117, 0, 1505, 1506, 5, 3, 0, 0, 1506, 275, 1, 0, 0, 0, 1507, 1508, 5, 131, 0, 0, 1508, 1509, 5, 2, 0, 0, 1509, 1510, 3, 234, 117, 0, 1510, 1511, 5, 8, 0, 0, 1511, 1514, 3, 234, 117, 0, 1512, 1513, 5, 8, 0, 0, 1513, 1515, 3, 234, 117, 0, 1514, 1512, 1, 0, 0, 0, 1514, 1515, 1, 0, 0, 0, 1515, 1516, 1, 0, 0, 0, 1516, 1517, 5, 3, 0, 0, 1517, 277, 1, 0, 0, 0, 1518, 1519, 5, 132, 0, 0, 1519, 1520, 5, 2, 0, 0, 1520, 1521, 3, 234, 117, 0, 1521, 1522, 5, 8, 0, 0, 1522, 1523, 3, 234, 117, 0, 1523, 1524, 5, 8, 0, 0, 1524, 1527, 3, 234, 117, 0, 1525, 1526, 5, 8, 0, 0, 1526, 1528, 3, 234, 117, 0, 1527, 1525, 1, 0, 0, 0, 1527, 1528, 1, 0, 0, 0, 1528, 1529, 1, 0, 0, 0, 1529, 1530, 5, 3, 0, 0, 1530, 279, 1, 0, 0, 0, 1531, 1532, 5, 133, 0, 0, 1532, 1533, 3, 110, 55, 0, 1533, 281, 1, 0, 0, 0, 1534, 1535, 5, 78, 0, 0, 1535, 1536, 5, 133, 0, 0, 1536, 1537, 3, 110, 55, 0, 1537, 283, 1, 0, 0, 0, 1538, 1539, 5, 134, 0, 0, 1539, 1541, 5, 2, 0, 0, 1540, 1542, 5, 33, 0, 0, 1541, 1540, 1, 0, 0, 0, 1541, 1542, 1, 0, 0, 0, 1542, 1545, 1, 0, 0, 0, 1543, 1546, 5, 1, 0, 0, 1544, 1546, 3, 234, 117, 0, 1545, 1543, 1, 0, 0, 0, 1545, 1544, 1, 0, 0, 0, 1546, 1547, 1, 0, 0, 0, 1547, 1611, 5, 3, 0, 0, 1548, 1549, 5, 135, 0, 0, 1549, 1551, 5, 2, 0, 0, 1550, 1552, 5, 33, 0, 0, 1551, 1550, 1, 0, 0, 0, 1551, 1552, 1, 0, 0, 0, 1552, 1553, 1, 0, 0, 0, 1553, 1554, 3, 234, 117, 0, 1554, 1555, 5, 3, 0, 0, 1555, 1611, 1, 0, 0, 0, 1556, 1557, 5, 136, 0, 0, 1557, 1559, 5, 2, 0, 0, 1558, 1560, 5, 33, 0, 0, 1559, 1558, 1, 0, 0, 0, 1559, 1560, 1, 0, 0, 0, 1560, 1561, 1, 0, 0, 0, 1561, 1562, 3, 234, 117, 0, 1562, 1563, 5, 3, 0, 0, 1563, 1611, 1, 0, 0, 0, 1564, 1565, 5, 137, 0, 0, 1565, 1567, 5, 2, 0, 0, 1566, 1568, 5, 33, 0, 0, 1567, 1566, 1, 0, 0, 0, 1567, 1568, 1, 0, 0, 0, 1568, 1569, 1, 0, 0, 0, 1569, 1570, 3, 234, 117, 0, 1570, 1571, 5, 3, 0, 0, 1571, 1611, 1, 0, 0, 0, 1572, 1573, 5, 138, 0, 0, 1573, 1575, 5, 2, 0, 0, 1574, 1576, 5, 33, 0, 0, 1575, 1574, 1, 0, 0, 0, 1575, 1576, 1, 0, 0, 0, 1576, 1577, 1, 0, 0, 0, 1577, 1578, 3, 234, 117, 0, 1578, 1579, 5, 3, 0, 0, 1579, 1611, 1, 0, 0, 0, 1580, 1581, 5, 139, 0, 0, 1581, 1583, 5, 2, 0, 0, 1582, 1584, 5, 33, 0, 0, 1583, 1582, 1, 0, 0, 0, 1583, 1584, 1, 0, 0, 0, 1584, 1585, 1, 0, 0, 0, 1585, 1586, 3, 234, 117, 0, 1586, 1587, 5, 3, 0, 0, 1587, 1611, 1, 0, 0, 0, 1588, 1589, 5, 140, 0, 0, 1589, 1591, 5, 2, 0, 0, 1590, 1592, 5, 33, 0, 0, 1591, 1590, 1, 0, 0, 0, 1591, 1592, 1, 0, 0, 0, 1592, 1593, 1, 0, 0, 0, 1593, 1594, 3, 234, 117, 0, 1594, 1595, 5, 3, 0, 0, 1595, 1611, 1, 0, 0, 0, 1596, 1597, 5, 43, 0, 0, 1597, 1599, 5, 2, 0, 0, 1598, 1600, 5, 33, 0, 0, 1599, 1598, 1, 0, 0, 0, 1599, 1600, 1, 0, 0, 0, 1600, 1601, 1, 0, 0, 0, 1601, 1606, 3, 234, 117, 0, 1602, 1603, 5, 6, 0, 0, 1603, 1604, 5, 141, 0, 0, 1604, 1605, 5, 20, 0, 0, 1605, 1607, 3, 300, 150, 0, 1606, 1602, 1, 0, 0, 0, 1606, 1607, 1, 0, 0, 0, 1607, 1608, 1, 0, 0, 0, 1608, 1609, 5, 3, 0, 0, 1609, 1611, 1, 0, 0, 0, 1610, 1538, 1, 0, 0, 0, 1610, 1548, 1, 0, 0, 0, 1610, 1556, 1, 0, 0, 0, 1610, 1564, 1, 0, 0, 0, 1610, 1572, 1, 0, 0, 0, 1610, 1580, 1, 0, 0, 0, 1610, 1588, 1, 0, 0, 0, 1610, 1596, 1, 0, 0, 0, 1611, 285, 1, 0, 0, 0, 1612, 1614, 3, 302, 151, 0, 1613, 1615, 3, 150, 75, 0, 1614, 1613, 1, 0, 0, 0, 1614, 1615, 1, 0, 0, 0, 1615, 287, 1, 0, 0, 0, 1616, 1620, 3, 300, 150, 0, 1617, 1621, 5, 148, 0, 0, 1618, 1619, 5, 27, 0, 0, 1619, 1621, 3, 302, 151, 0, 1620, 1617, 1, 0, 0, 0, 1620, 1618, 1, 0, 0, 0, 1620, 1621, 1, 0, 0, 0, 1621, 289, 1, 0, 0, 0, 1622, 1626, 3, 292, 146, 0, 1623, 1626, 3, 294, 147, 0, 1624, 1626, 3, 296, 148, 0, 1625, 1622, 1, 0, 0, 0, 1625, 1623, 1, 0, 0, 0, 1625, 1624, 1, 0, 0, 0, 1626, 291, 1, 0, 0, 0, 1627, 1628, 7, 4, 0, 0, 1628, 293, 1, 0, 0, 0, 1629, 1630, 7, 5, 0, 0, 1630, 295, 1, 0, 0, 0, 1631, 1632, 7, 6, 0, 0, 1632, 297, 1, 0, 0, 0, 1633, 1634, 7, 7, 0, 0, 1634, 299, 1, 0, 0, 0, 1635, 1636, 7, 8, 0, 0, 1636, 301, 1, 0, 0, 0, 1637, 1639, 5, 149, 0, 0, 1638, 1637, 1, 0, 0, 0, 1638, 1639, 1, 0, 0, 0, 1639, 1642, 1, 0, 0, 0, 1640, 1643, 3, 308, 154, 0, 1641, 1643, 3, 304, 152, 0, 1642, 1640, 1, 0, 0, 0, 1642, 1641, 1, 0, 0, 0, 1643, 303, 1, 0, 0, 0, 1644, 1647, 3, 310, 155, 0, 1645, 1647, 3, 312, 156, 0, 1646, 1644, 1, 0, 0, 0, 1646, 1645, 1, 0, 0, 0, 1647, 305, 1, 0, 0, 0, 1648, 1649, 7, 9, 0, 0, 1649, 307, 1, 0, 0, 0, 1650, 1651, 5, 142, 0, 0, 1651, 309, 1, 0, 0, 0, 1652, 1653, 5, 144, 0, 0, 1653, 311, 1, 0, 0, 0, 1654, 1655, 5, 143, 0, 0, 1655, 313, 1, 0, 0, 0, 163, 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, 686, 689, 698, 709, 713, 716, 720, 727, 734, 736, 741, 746, 751, 756, 759, 764, 766, 776, 787, 804, 811, 821, 825, 831, 840, 845, 852, 862, 871, 879, 886, 891, 900, 905, 909, 916, 918, 926, 929, 937, 941, 946, 953, 964, 967, 972, 976, 991, 998, 1010, 1018, 1023, 1028, 1040, 1049, 1052, 1055, 1062, 1064, 1070, 1078, 1088, 1096, 1102, 1106, 1110, 1114, 1124, 1133, 1141, 1164, 1172, 1180, 1188, 1193, 1200, 1205, 1220, 1229, 1275, 1489, 1498, 1514, 1527, 1541, 1545, 1551, 1559, 1567, 1575, 1583, 1591, 1599, 1606, 1610, 1614, 1620, 1625, 1638, 1642, 1646] \ No newline at end of file diff --git a/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp b/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp index ab6318befc..bafc1c4a06 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp +++ b/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp @@ -387,7 +387,7 @@ void sparqlautomaticParserInitialize() { "WS", "COMMENTS"}); static const int32_t serializedATNSegment[] = { - 4, 1, 176, 1655, 2, 0, 7, 0, 2, 1, 7, 1, + 4, 1, 176, 1657, 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, @@ -507,1095 +507,1097 @@ void sparqlautomaticParserInitialize() { 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, + 3, 46, 683, 8, 46, 1, 47, 1, 47, 3, 47, 687, + 8, 47, 1, 47, 3, 47, 690, 8, 47, 1, 48, 1, + 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 3, + 49, 699, 8, 49, 1, 50, 1, 50, 1, 50, 1, 50, + 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 3, 52, + 710, 8, 52, 1, 52, 1, 52, 3, 52, 714, 8, 52, + 1, 52, 3, 52, 717, 8, 52, 5, 52, 719, 8, 52, + 10, 52, 12, 52, 722, 9, 52, 1, 53, 1, 53, 1, + 53, 1, 53, 3, 53, 728, 8, 53, 1, 53, 1, 53, + 1, 54, 1, 54, 1, 54, 3, 54, 735, 8, 54, 3, + 54, 737, 8, 54, 1, 55, 1, 55, 1, 55, 3, 55, + 742, 8, 55, 1, 55, 1, 55, 1, 56, 3, 56, 747, + 8, 56, 1, 56, 5, 56, 750, 8, 56, 10, 56, 12, + 56, 753, 9, 56, 1, 57, 1, 57, 3, 57, 757, 8, + 57, 1, 57, 3, 57, 760, 8, 57, 1, 58, 1, 58, + 1, 58, 3, 58, 765, 8, 58, 3, 58, 767, 8, 58, + 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, + 1, 59, 1, 59, 3, 59, 777, 8, 59, 1, 60, 1, + 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, + 62, 1, 62, 3, 62, 788, 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, 805, 8, 65, 1, 66, 1, 66, 1, + 66, 5, 66, 810, 8, 66, 10, 66, 12, 66, 813, 9, + 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 5, + 67, 820, 8, 67, 10, 67, 12, 67, 823, 9, 67, 1, + 67, 3, 67, 826, 8, 67, 1, 67, 1, 67, 5, 67, + 830, 8, 67, 10, 67, 12, 67, 833, 9, 67, 1, 67, + 1, 67, 1, 68, 1, 68, 5, 68, 839, 8, 68, 10, + 68, 12, 68, 842, 9, 68, 1, 68, 1, 68, 3, 68, + 846, 8, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, + 69, 3, 69, 853, 8, 69, 1, 70, 1, 70, 1, 70, + 1, 71, 1, 71, 1, 71, 5, 71, 861, 8, 71, 10, + 71, 12, 71, 864, 9, 71, 1, 72, 1, 72, 1, 72, + 1, 73, 1, 73, 1, 73, 3, 73, 872, 8, 73, 1, + 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 3, + 75, 880, 8, 75, 1, 75, 1, 75, 1, 75, 5, 75, + 885, 8, 75, 10, 75, 12, 75, 888, 9, 75, 1, 75, + 1, 75, 3, 75, 892, 8, 75, 1, 76, 1, 76, 1, + 76, 1, 76, 1, 76, 5, 76, 899, 8, 76, 10, 76, + 12, 76, 902, 9, 76, 1, 76, 1, 76, 3, 76, 906, + 8, 76, 1, 77, 1, 77, 3, 77, 910, 8, 77, 1, + 77, 1, 77, 1, 78, 1, 78, 1, 78, 3, 78, 917, + 8, 78, 3, 78, 919, 8, 78, 1, 79, 1, 79, 1, + 79, 1, 79, 1, 79, 1, 79, 3, 79, 927, 8, 79, + 1, 80, 3, 80, 930, 8, 80, 1, 81, 1, 81, 1, + 81, 1, 81, 1, 81, 1, 81, 3, 81, 938, 8, 81, + 5, 81, 940, 8, 81, 10, 81, 12, 81, 943, 9, 81, + 1, 82, 1, 82, 3, 82, 947, 8, 82, 1, 83, 1, + 83, 1, 83, 5, 83, 952, 8, 83, 10, 83, 12, 83, + 955, 9, 83, 1, 84, 1, 84, 1, 85, 1, 85, 1, + 85, 1, 85, 1, 85, 1, 85, 3, 85, 965, 8, 85, + 1, 86, 3, 86, 968, 8, 86, 1, 87, 1, 87, 1, + 87, 3, 87, 973, 8, 87, 5, 87, 975, 8, 87, 10, + 87, 12, 87, 978, 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, 992, 8, 92, 1, + 93, 1, 93, 1, 93, 5, 93, 997, 8, 93, 10, 93, + 12, 93, 1000, 9, 93, 1, 94, 1, 94, 1, 95, 1, + 95, 1, 96, 1, 96, 1, 96, 5, 96, 1009, 8, 96, + 10, 96, 12, 96, 1012, 9, 96, 1, 97, 1, 97, 1, + 97, 5, 97, 1017, 8, 97, 10, 97, 12, 97, 1020, 9, + 97, 1, 98, 1, 98, 3, 98, 1024, 8, 98, 1, 99, + 1, 99, 1, 99, 3, 99, 1029, 8, 99, 1, 100, 1, + 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, + 101, 1, 101, 1, 101, 3, 101, 1041, 8, 101, 1, 102, + 1, 102, 1, 102, 1, 102, 1, 102, 5, 102, 1048, 8, + 102, 10, 102, 12, 102, 1051, 9, 102, 3, 102, 1053, 8, + 102, 1, 102, 3, 102, 1056, 8, 102, 1, 103, 1, 103, + 1, 103, 1, 103, 1, 103, 3, 103, 1063, 8, 103, 3, + 103, 1065, 8, 103, 1, 104, 1, 104, 1, 105, 1, 105, + 3, 105, 1071, 8, 105, 1, 106, 1, 106, 1, 106, 1, + 106, 1, 107, 1, 107, 3, 107, 1079, 8, 107, 1, 108, + 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 4, 109, + 1087, 8, 109, 11, 109, 12, 109, 1088, 1, 109, 1, 109, + 1, 110, 1, 110, 4, 110, 1095, 8, 110, 11, 110, 12, + 110, 1096, 1, 110, 1, 110, 1, 111, 1, 111, 3, 111, + 1103, 8, 111, 1, 112, 1, 112, 3, 112, 1107, 8, 112, + 1, 113, 1, 113, 3, 113, 1111, 8, 113, 1, 114, 1, + 114, 3, 114, 1115, 8, 114, 1, 115, 1, 115, 1, 116, + 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 3, 116, + 1125, 8, 116, 1, 117, 1, 117, 1, 118, 1, 118, 1, + 118, 5, 118, 1132, 8, 118, 10, 118, 12, 118, 1135, 9, + 118, 1, 119, 1, 119, 1, 119, 5, 119, 1140, 8, 119, + 10, 119, 12, 119, 1143, 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, 1165, 8, 121, 1, 122, 1, 122, 1, 123, 1, 123, + 5, 123, 1171, 8, 123, 10, 123, 12, 123, 1174, 9, 123, + 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 3, 124, + 1181, 8, 124, 1, 125, 1, 125, 1, 126, 1, 126, 1, + 127, 1, 127, 3, 127, 1189, 8, 127, 1, 127, 5, 127, + 1192, 8, 127, 10, 127, 12, 127, 1195, 9, 127, 1, 128, + 1, 128, 5, 128, 1199, 8, 128, 10, 128, 12, 128, 1202, + 9, 128, 1, 129, 1, 129, 3, 129, 1206, 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, 1221, 8, 132, 1, 133, 1, 133, 1, 133, + 1, 133, 1, 133, 1, 133, 1, 133, 3, 133, 1230, 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, 1, 135, 1, 135, 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, + 135, 1, 135, 1, 135, 3, 135, 1276, 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, 1490, 8, 135, 1, 136, 1, 136, 1, + 136, 1, 136, 1, 136, 1, 136, 1, 136, 3, 136, 1499, + 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, 1515, 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, 1528, + 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, 1542, 8, 142, 1, 142, 1, 142, 3, + 142, 1546, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, + 3, 142, 1552, 8, 142, 1, 142, 1, 142, 1, 142, 1, + 142, 1, 142, 1, 142, 3, 142, 1560, 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, + 1568, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, + 142, 1, 142, 3, 142, 1576, 8, 142, 1, 142, 1, 142, + 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1584, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, - 142, 3, 142, 1598, 8, 142, 1, 142, 1, 142, 1, 142, - 1, 142, 1, 142, 3, 142, 1605, 8, 142, 1, 142, 1, - 142, 3, 142, 1609, 8, 142, 1, 143, 1, 143, 3, 143, - 1613, 8, 143, 1, 144, 1, 144, 1, 144, 1, 144, 3, - 144, 1619, 8, 144, 1, 145, 1, 145, 1, 145, 3, 145, - 1624, 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, 1637, 8, 151, 1, 151, 1, 151, 3, 151, - 1641, 8, 151, 1, 152, 1, 152, 3, 152, 1645, 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, 146, 147, 1, 0, 150, 152, 1, 0, - 153, 155, 1, 0, 156, 158, 1, 0, 28, 29, 1, 0, - 160, 163, 2, 0, 145, 145, 166, 166, 1771, 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, 1608, 1, - 0, 0, 0, 286, 1610, 1, 0, 0, 0, 288, 1614, 1, - 0, 0, 0, 290, 1623, 1, 0, 0, 0, 292, 1625, 1, - 0, 0, 0, 294, 1627, 1, 0, 0, 0, 296, 1629, 1, - 0, 0, 0, 298, 1631, 1, 0, 0, 0, 300, 1633, 1, - 0, 0, 0, 302, 1636, 1, 0, 0, 0, 304, 1644, 1, - 0, 0, 0, 306, 1646, 1, 0, 0, 0, 308, 1648, 1, - 0, 0, 0, 310, 1650, 1, 0, 0, 0, 312, 1652, 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, 143, 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, - 165, 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, - 165, 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, - 165, 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, - 165, 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, 150, 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, - 165, 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, - 165, 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, 165, 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, - 165, 0, 0, 1398, 1399, 5, 113, 0, 0, 1399, 1488, 5, - 165, 0, 0, 1400, 1401, 5, 114, 0, 0, 1401, 1488, 5, - 165, 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, 1609, 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, 1609, 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, 1609, 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, 1609, 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, 1609, 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, 1609, 1, - 0, 0, 0, 1586, 1587, 5, 140, 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, 1592, 3, 234, 117, 0, 1592, 1593, 5, - 3, 0, 0, 1593, 1609, 1, 0, 0, 0, 1594, 1595, 5, - 43, 0, 0, 1595, 1597, 5, 2, 0, 0, 1596, 1598, 5, - 33, 0, 0, 1597, 1596, 1, 0, 0, 0, 1597, 1598, 1, - 0, 0, 0, 1598, 1599, 1, 0, 0, 0, 1599, 1604, 3, - 234, 117, 0, 1600, 1601, 5, 6, 0, 0, 1601, 1602, 5, - 141, 0, 0, 1602, 1603, 5, 20, 0, 0, 1603, 1605, 3, - 300, 150, 0, 1604, 1600, 1, 0, 0, 0, 1604, 1605, 1, - 0, 0, 0, 1605, 1606, 1, 0, 0, 0, 1606, 1607, 5, - 3, 0, 0, 1607, 1609, 1, 0, 0, 0, 1608, 1536, 1, - 0, 0, 0, 1608, 1546, 1, 0, 0, 0, 1608, 1554, 1, - 0, 0, 0, 1608, 1562, 1, 0, 0, 0, 1608, 1570, 1, - 0, 0, 0, 1608, 1578, 1, 0, 0, 0, 1608, 1586, 1, - 0, 0, 0, 1608, 1594, 1, 0, 0, 0, 1609, 285, 1, - 0, 0, 0, 1610, 1612, 3, 302, 151, 0, 1611, 1613, 3, - 150, 75, 0, 1612, 1611, 1, 0, 0, 0, 1612, 1613, 1, - 0, 0, 0, 1613, 287, 1, 0, 0, 0, 1614, 1618, 3, - 300, 150, 0, 1615, 1619, 5, 148, 0, 0, 1616, 1617, 5, - 27, 0, 0, 1617, 1619, 3, 302, 151, 0, 1618, 1615, 1, - 0, 0, 0, 1618, 1616, 1, 0, 0, 0, 1618, 1619, 1, - 0, 0, 0, 1619, 289, 1, 0, 0, 0, 1620, 1624, 3, - 292, 146, 0, 1621, 1624, 3, 294, 147, 0, 1622, 1624, 3, - 296, 148, 0, 1623, 1620, 1, 0, 0, 0, 1623, 1621, 1, - 0, 0, 0, 1623, 1622, 1, 0, 0, 0, 1624, 291, 1, - 0, 0, 0, 1625, 1626, 7, 4, 0, 0, 1626, 293, 1, - 0, 0, 0, 1627, 1628, 7, 5, 0, 0, 1628, 295, 1, - 0, 0, 0, 1629, 1630, 7, 6, 0, 0, 1630, 297, 1, - 0, 0, 0, 1631, 1632, 7, 7, 0, 0, 1632, 299, 1, - 0, 0, 0, 1633, 1634, 7, 8, 0, 0, 1634, 301, 1, - 0, 0, 0, 1635, 1637, 5, 149, 0, 0, 1636, 1635, 1, - 0, 0, 0, 1636, 1637, 1, 0, 0, 0, 1637, 1640, 1, - 0, 0, 0, 1638, 1641, 3, 308, 154, 0, 1639, 1641, 3, - 304, 152, 0, 1640, 1638, 1, 0, 0, 0, 1640, 1639, 1, - 0, 0, 0, 1641, 303, 1, 0, 0, 0, 1642, 1645, 3, - 310, 155, 0, 1643, 1645, 3, 312, 156, 0, 1644, 1642, 1, - 0, 0, 0, 1644, 1643, 1, 0, 0, 0, 1645, 305, 1, - 0, 0, 0, 1646, 1647, 7, 9, 0, 0, 1647, 307, 1, - 0, 0, 0, 1648, 1649, 5, 142, 0, 0, 1649, 309, 1, - 0, 0, 0, 1650, 1651, 5, 144, 0, 0, 1651, 311, 1, - 0, 0, 0, 1652, 1653, 5, 143, 0, 0, 1653, 313, 1, - 0, 0, 0, 162, 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, 1597, 1604, 1608, 1612, 1618, 1623, 1636, 1640, 1644}; + 142, 3, 142, 1592, 8, 142, 1, 142, 1, 142, 1, 142, + 1, 142, 1, 142, 1, 142, 3, 142, 1600, 8, 142, 1, + 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1607, + 8, 142, 1, 142, 1, 142, 3, 142, 1611, 8, 142, 1, + 143, 1, 143, 3, 143, 1615, 8, 143, 1, 144, 1, 144, + 1, 144, 1, 144, 3, 144, 1621, 8, 144, 1, 145, 1, + 145, 1, 145, 3, 145, 1626, 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, 1639, 8, 151, 1, + 151, 1, 151, 3, 151, 1643, 8, 151, 1, 152, 1, 152, + 3, 152, 1647, 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, 146, 147, 1, + 0, 150, 152, 1, 0, 153, 155, 1, 0, 156, 158, 1, + 0, 28, 29, 1, 0, 160, 163, 2, 0, 145, 145, 166, + 166, 1774, 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, 689, 1, 0, + 0, 0, 96, 691, 1, 0, 0, 0, 98, 698, 1, 0, + 0, 0, 100, 700, 1, 0, 0, 0, 102, 704, 1, 0, + 0, 0, 104, 709, 1, 0, 0, 0, 106, 723, 1, 0, + 0, 0, 108, 731, 1, 0, 0, 0, 110, 738, 1, 0, + 0, 0, 112, 746, 1, 0, 0, 0, 114, 754, 1, 0, + 0, 0, 116, 761, 1, 0, 0, 0, 118, 776, 1, 0, + 0, 0, 120, 778, 1, 0, 0, 0, 122, 781, 1, 0, + 0, 0, 124, 785, 1, 0, 0, 0, 126, 792, 1, 0, + 0, 0, 128, 799, 1, 0, 0, 0, 130, 804, 1, 0, + 0, 0, 132, 806, 1, 0, 0, 0, 134, 825, 1, 0, + 0, 0, 136, 845, 1, 0, 0, 0, 138, 852, 1, 0, + 0, 0, 140, 854, 1, 0, 0, 0, 142, 857, 1, 0, + 0, 0, 144, 865, 1, 0, 0, 0, 146, 871, 1, 0, + 0, 0, 148, 873, 1, 0, 0, 0, 150, 891, 1, 0, + 0, 0, 152, 905, 1, 0, 0, 0, 154, 907, 1, 0, + 0, 0, 156, 913, 1, 0, 0, 0, 158, 926, 1, 0, + 0, 0, 160, 929, 1, 0, 0, 0, 162, 931, 1, 0, + 0, 0, 164, 946, 1, 0, 0, 0, 166, 948, 1, 0, + 0, 0, 168, 956, 1, 0, 0, 0, 170, 964, 1, 0, + 0, 0, 172, 967, 1, 0, 0, 0, 174, 969, 1, 0, + 0, 0, 176, 979, 1, 0, 0, 0, 178, 981, 1, 0, + 0, 0, 180, 983, 1, 0, 0, 0, 182, 986, 1, 0, + 0, 0, 184, 991, 1, 0, 0, 0, 186, 993, 1, 0, + 0, 0, 188, 1001, 1, 0, 0, 0, 190, 1003, 1, 0, + 0, 0, 192, 1005, 1, 0, 0, 0, 194, 1013, 1, 0, + 0, 0, 196, 1021, 1, 0, 0, 0, 198, 1028, 1, 0, + 0, 0, 200, 1030, 1, 0, 0, 0, 202, 1040, 1, 0, + 0, 0, 204, 1055, 1, 0, 0, 0, 206, 1064, 1, 0, + 0, 0, 208, 1066, 1, 0, 0, 0, 210, 1070, 1, 0, + 0, 0, 212, 1072, 1, 0, 0, 0, 214, 1078, 1, 0, + 0, 0, 216, 1080, 1, 0, 0, 0, 218, 1084, 1, 0, + 0, 0, 220, 1092, 1, 0, 0, 0, 222, 1102, 1, 0, + 0, 0, 224, 1106, 1, 0, 0, 0, 226, 1110, 1, 0, + 0, 0, 228, 1114, 1, 0, 0, 0, 230, 1116, 1, 0, + 0, 0, 232, 1124, 1, 0, 0, 0, 234, 1126, 1, 0, + 0, 0, 236, 1128, 1, 0, 0, 0, 238, 1136, 1, 0, + 0, 0, 240, 1144, 1, 0, 0, 0, 242, 1146, 1, 0, + 0, 0, 244, 1166, 1, 0, 0, 0, 246, 1168, 1, 0, + 0, 0, 248, 1180, 1, 0, 0, 0, 250, 1182, 1, 0, + 0, 0, 252, 1184, 1, 0, 0, 0, 254, 1188, 1, 0, + 0, 0, 256, 1196, 1, 0, 0, 0, 258, 1205, 1, 0, + 0, 0, 260, 1207, 1, 0, 0, 0, 262, 1210, 1, 0, + 0, 0, 264, 1220, 1, 0, 0, 0, 266, 1229, 1, 0, + 0, 0, 268, 1231, 1, 0, 0, 0, 270, 1489, 1, 0, + 0, 0, 272, 1491, 1, 0, 0, 0, 274, 1502, 1, 0, + 0, 0, 276, 1507, 1, 0, 0, 0, 278, 1518, 1, 0, + 0, 0, 280, 1531, 1, 0, 0, 0, 282, 1534, 1, 0, + 0, 0, 284, 1610, 1, 0, 0, 0, 286, 1612, 1, 0, + 0, 0, 288, 1616, 1, 0, 0, 0, 290, 1625, 1, 0, + 0, 0, 292, 1627, 1, 0, 0, 0, 294, 1629, 1, 0, + 0, 0, 296, 1631, 1, 0, 0, 0, 298, 1633, 1, 0, + 0, 0, 300, 1635, 1, 0, 0, 0, 302, 1638, 1, 0, + 0, 0, 304, 1646, 1, 0, 0, 0, 306, 1648, 1, 0, + 0, 0, 308, 1650, 1, 0, 0, 0, 310, 1652, 1, 0, + 0, 0, 312, 1654, 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, 143, + 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, 690, 5, 68, 0, 0, 685, 687, 5, 69, + 0, 0, 686, 685, 1, 0, 0, 0, 686, 687, 1, 0, + 0, 0, 687, 688, 1, 0, 0, 0, 688, 690, 3, 302, + 151, 0, 689, 684, 1, 0, 0, 0, 689, 686, 1, 0, + 0, 0, 690, 95, 1, 0, 0, 0, 691, 692, 5, 69, + 0, 0, 692, 693, 3, 302, 151, 0, 693, 97, 1, 0, + 0, 0, 694, 699, 3, 96, 48, 0, 695, 699, 5, 68, + 0, 0, 696, 699, 5, 41, 0, 0, 697, 699, 5, 70, + 0, 0, 698, 694, 1, 0, 0, 0, 698, 695, 1, 0, + 0, 0, 698, 696, 1, 0, 0, 0, 698, 697, 1, 0, + 0, 0, 699, 99, 1, 0, 0, 0, 700, 701, 5, 4, + 0, 0, 701, 702, 3, 104, 52, 0, 702, 703, 5, 5, + 0, 0, 703, 101, 1, 0, 0, 0, 704, 705, 5, 4, + 0, 0, 705, 706, 3, 104, 52, 0, 706, 707, 5, 5, + 0, 0, 707, 103, 1, 0, 0, 0, 708, 710, 3, 108, + 54, 0, 709, 708, 1, 0, 0, 0, 709, 710, 1, 0, + 0, 0, 710, 720, 1, 0, 0, 0, 711, 713, 3, 106, + 53, 0, 712, 714, 5, 7, 0, 0, 713, 712, 1, 0, + 0, 0, 713, 714, 1, 0, 0, 0, 714, 716, 1, 0, + 0, 0, 715, 717, 3, 108, 54, 0, 716, 715, 1, 0, + 0, 0, 716, 717, 1, 0, 0, 0, 717, 719, 1, 0, + 0, 0, 718, 711, 1, 0, 0, 0, 719, 722, 1, 0, + 0, 0, 720, 718, 1, 0, 0, 0, 720, 721, 1, 0, + 0, 0, 721, 105, 1, 0, 0, 0, 722, 720, 1, 0, + 0, 0, 723, 724, 5, 69, 0, 0, 724, 725, 3, 228, + 114, 0, 725, 727, 5, 4, 0, 0, 726, 728, 3, 108, + 54, 0, 727, 726, 1, 0, 0, 0, 727, 728, 1, 0, + 0, 0, 728, 729, 1, 0, 0, 0, 729, 730, 5, 5, + 0, 0, 730, 107, 1, 0, 0, 0, 731, 736, 3, 158, + 79, 0, 732, 734, 5, 7, 0, 0, 733, 735, 3, 108, + 54, 0, 734, 733, 1, 0, 0, 0, 734, 735, 1, 0, + 0, 0, 735, 737, 1, 0, 0, 0, 736, 732, 1, 0, + 0, 0, 736, 737, 1, 0, 0, 0, 737, 109, 1, 0, + 0, 0, 738, 741, 5, 4, 0, 0, 739, 742, 3, 12, + 6, 0, 740, 742, 3, 112, 56, 0, 741, 739, 1, 0, + 0, 0, 741, 740, 1, 0, 0, 0, 742, 743, 1, 0, + 0, 0, 743, 744, 5, 5, 0, 0, 744, 111, 1, 0, + 0, 0, 745, 747, 3, 116, 58, 0, 746, 745, 1, 0, + 0, 0, 746, 747, 1, 0, 0, 0, 747, 751, 1, 0, + 0, 0, 748, 750, 3, 114, 57, 0, 749, 748, 1, 0, + 0, 0, 750, 753, 1, 0, 0, 0, 751, 749, 1, 0, + 0, 0, 751, 752, 1, 0, 0, 0, 752, 113, 1, 0, + 0, 0, 753, 751, 1, 0, 0, 0, 754, 756, 3, 118, + 59, 0, 755, 757, 5, 7, 0, 0, 756, 755, 1, 0, + 0, 0, 756, 757, 1, 0, 0, 0, 757, 759, 1, 0, + 0, 0, 758, 760, 3, 116, 58, 0, 759, 758, 1, 0, + 0, 0, 759, 760, 1, 0, 0, 0, 760, 115, 1, 0, + 0, 0, 761, 766, 3, 170, 85, 0, 762, 764, 5, 7, + 0, 0, 763, 765, 3, 116, 58, 0, 764, 763, 1, 0, + 0, 0, 764, 765, 1, 0, 0, 0, 765, 767, 1, 0, + 0, 0, 766, 762, 1, 0, 0, 0, 766, 767, 1, 0, + 0, 0, 767, 117, 1, 0, 0, 0, 768, 777, 3, 142, + 71, 0, 769, 777, 3, 120, 60, 0, 770, 777, 3, 140, + 70, 0, 771, 777, 3, 122, 61, 0, 772, 777, 3, 124, + 62, 0, 773, 777, 3, 144, 72, 0, 774, 777, 3, 126, + 63, 0, 775, 777, 3, 128, 64, 0, 776, 768, 1, 0, + 0, 0, 776, 769, 1, 0, 0, 0, 776, 770, 1, 0, + 0, 0, 776, 771, 1, 0, 0, 0, 776, 772, 1, 0, + 0, 0, 776, 773, 1, 0, 0, 0, 776, 774, 1, 0, + 0, 0, 776, 775, 1, 0, 0, 0, 777, 119, 1, 0, + 0, 0, 778, 779, 5, 71, 0, 0, 779, 780, 3, 110, + 55, 0, 780, 121, 1, 0, 0, 0, 781, 782, 5, 69, + 0, 0, 782, 783, 3, 228, 114, 0, 783, 784, 3, 110, + 55, 0, 784, 123, 1, 0, 0, 0, 785, 787, 5, 72, + 0, 0, 786, 788, 5, 54, 0, 0, 787, 786, 1, 0, + 0, 0, 787, 788, 1, 0, 0, 0, 788, 789, 1, 0, + 0, 0, 789, 790, 3, 228, 114, 0, 790, 791, 3, 110, + 55, 0, 791, 125, 1, 0, 0, 0, 792, 793, 5, 73, + 0, 0, 793, 794, 5, 2, 0, 0, 794, 795, 3, 234, + 117, 0, 795, 796, 5, 35, 0, 0, 796, 797, 3, 230, + 115, 0, 797, 798, 5, 3, 0, 0, 798, 127, 1, 0, + 0, 0, 799, 800, 5, 52, 0, 0, 800, 801, 3, 130, + 65, 0, 801, 129, 1, 0, 0, 0, 802, 805, 3, 132, + 66, 0, 803, 805, 3, 134, 67, 0, 804, 802, 1, 0, + 0, 0, 804, 803, 1, 0, 0, 0, 805, 131, 1, 0, + 0, 0, 806, 807, 3, 230, 115, 0, 807, 811, 5, 4, + 0, 0, 808, 810, 3, 138, 69, 0, 809, 808, 1, 0, + 0, 0, 810, 813, 1, 0, 0, 0, 811, 809, 1, 0, + 0, 0, 811, 812, 1, 0, 0, 0, 812, 814, 1, 0, + 0, 0, 813, 811, 1, 0, 0, 0, 814, 815, 5, 5, + 0, 0, 815, 133, 1, 0, 0, 0, 816, 826, 5, 165, + 0, 0, 817, 821, 5, 2, 0, 0, 818, 820, 3, 230, + 115, 0, 819, 818, 1, 0, 0, 0, 820, 823, 1, 0, + 0, 0, 821, 819, 1, 0, 0, 0, 821, 822, 1, 0, + 0, 0, 822, 824, 1, 0, 0, 0, 823, 821, 1, 0, + 0, 0, 824, 826, 5, 3, 0, 0, 825, 816, 1, 0, + 0, 0, 825, 817, 1, 0, 0, 0, 826, 827, 1, 0, + 0, 0, 827, 831, 5, 4, 0, 0, 828, 830, 3, 136, + 68, 0, 829, 828, 1, 0, 0, 0, 830, 833, 1, 0, + 0, 0, 831, 829, 1, 0, 0, 0, 831, 832, 1, 0, + 0, 0, 832, 834, 1, 0, 0, 0, 833, 831, 1, 0, + 0, 0, 834, 835, 5, 5, 0, 0, 835, 135, 1, 0, + 0, 0, 836, 840, 5, 2, 0, 0, 837, 839, 3, 138, + 69, 0, 838, 837, 1, 0, 0, 0, 839, 842, 1, 0, + 0, 0, 840, 838, 1, 0, 0, 0, 840, 841, 1, 0, + 0, 0, 841, 843, 1, 0, 0, 0, 842, 840, 1, 0, + 0, 0, 843, 846, 5, 3, 0, 0, 844, 846, 5, 165, + 0, 0, 845, 836, 1, 0, 0, 0, 845, 844, 1, 0, + 0, 0, 846, 137, 1, 0, 0, 0, 847, 853, 3, 302, + 151, 0, 848, 853, 3, 288, 144, 0, 849, 853, 3, 290, + 145, 0, 850, 853, 3, 298, 149, 0, 851, 853, 5, 74, + 0, 0, 852, 847, 1, 0, 0, 0, 852, 848, 1, 0, + 0, 0, 852, 849, 1, 0, 0, 0, 852, 850, 1, 0, + 0, 0, 852, 851, 1, 0, 0, 0, 853, 139, 1, 0, + 0, 0, 854, 855, 5, 75, 0, 0, 855, 856, 3, 110, + 55, 0, 856, 141, 1, 0, 0, 0, 857, 862, 3, 110, + 55, 0, 858, 859, 5, 76, 0, 0, 859, 861, 3, 110, + 55, 0, 860, 858, 1, 0, 0, 0, 861, 864, 1, 0, + 0, 0, 862, 860, 1, 0, 0, 0, 862, 863, 1, 0, + 0, 0, 863, 143, 1, 0, 0, 0, 864, 862, 1, 0, + 0, 0, 865, 866, 5, 77, 0, 0, 866, 867, 3, 146, + 73, 0, 867, 145, 1, 0, 0, 0, 868, 872, 3, 268, + 134, 0, 869, 872, 3, 270, 135, 0, 870, 872, 3, 148, + 74, 0, 871, 868, 1, 0, 0, 0, 871, 869, 1, 0, + 0, 0, 871, 870, 1, 0, 0, 0, 872, 147, 1, 0, + 0, 0, 873, 874, 3, 302, 151, 0, 874, 875, 3, 150, + 75, 0, 875, 149, 1, 0, 0, 0, 876, 892, 5, 165, + 0, 0, 877, 879, 5, 2, 0, 0, 878, 880, 5, 33, + 0, 0, 879, 878, 1, 0, 0, 0, 879, 880, 1, 0, + 0, 0, 880, 881, 1, 0, 0, 0, 881, 886, 3, 234, + 117, 0, 882, 883, 5, 8, 0, 0, 883, 885, 3, 234, + 117, 0, 884, 882, 1, 0, 0, 0, 885, 888, 1, 0, + 0, 0, 886, 884, 1, 0, 0, 0, 886, 887, 1, 0, + 0, 0, 887, 889, 1, 0, 0, 0, 888, 886, 1, 0, + 0, 0, 889, 890, 5, 3, 0, 0, 890, 892, 1, 0, + 0, 0, 891, 876, 1, 0, 0, 0, 891, 877, 1, 0, + 0, 0, 892, 151, 1, 0, 0, 0, 893, 906, 5, 165, + 0, 0, 894, 895, 5, 2, 0, 0, 895, 900, 3, 234, + 117, 0, 896, 897, 5, 8, 0, 0, 897, 899, 3, 234, + 117, 0, 898, 896, 1, 0, 0, 0, 899, 902, 1, 0, + 0, 0, 900, 898, 1, 0, 0, 0, 900, 901, 1, 0, + 0, 0, 901, 903, 1, 0, 0, 0, 902, 900, 1, 0, + 0, 0, 903, 904, 5, 3, 0, 0, 904, 906, 1, 0, + 0, 0, 905, 893, 1, 0, 0, 0, 905, 894, 1, 0, + 0, 0, 906, 153, 1, 0, 0, 0, 907, 909, 5, 4, + 0, 0, 908, 910, 3, 156, 78, 0, 909, 908, 1, 0, + 0, 0, 909, 910, 1, 0, 0, 0, 910, 911, 1, 0, + 0, 0, 911, 912, 5, 5, 0, 0, 912, 155, 1, 0, + 0, 0, 913, 918, 3, 158, 79, 0, 914, 916, 5, 7, + 0, 0, 915, 917, 3, 156, 78, 0, 916, 915, 1, 0, + 0, 0, 916, 917, 1, 0, 0, 0, 917, 919, 1, 0, + 0, 0, 918, 914, 1, 0, 0, 0, 918, 919, 1, 0, + 0, 0, 919, 157, 1, 0, 0, 0, 920, 921, 3, 226, + 113, 0, 921, 922, 3, 162, 81, 0, 922, 927, 1, 0, + 0, 0, 923, 924, 3, 210, 105, 0, 924, 925, 3, 160, + 80, 0, 925, 927, 1, 0, 0, 0, 926, 920, 1, 0, + 0, 0, 926, 923, 1, 0, 0, 0, 927, 159, 1, 0, + 0, 0, 928, 930, 3, 162, 81, 0, 929, 928, 1, 0, + 0, 0, 929, 930, 1, 0, 0, 0, 930, 161, 1, 0, + 0, 0, 931, 932, 3, 164, 82, 0, 932, 941, 3, 166, + 83, 0, 933, 937, 5, 6, 0, 0, 934, 935, 3, 164, + 82, 0, 935, 936, 3, 166, 83, 0, 936, 938, 1, 0, + 0, 0, 937, 934, 1, 0, 0, 0, 937, 938, 1, 0, + 0, 0, 938, 940, 1, 0, 0, 0, 939, 933, 1, 0, + 0, 0, 940, 943, 1, 0, 0, 0, 941, 939, 1, 0, + 0, 0, 941, 942, 1, 0, 0, 0, 942, 163, 1, 0, + 0, 0, 943, 941, 1, 0, 0, 0, 944, 947, 3, 228, + 114, 0, 945, 947, 5, 9, 0, 0, 946, 944, 1, 0, + 0, 0, 946, 945, 1, 0, 0, 0, 947, 165, 1, 0, + 0, 0, 948, 953, 3, 168, 84, 0, 949, 950, 5, 8, + 0, 0, 950, 952, 3, 168, 84, 0, 951, 949, 1, 0, + 0, 0, 952, 955, 1, 0, 0, 0, 953, 951, 1, 0, + 0, 0, 953, 954, 1, 0, 0, 0, 954, 167, 1, 0, + 0, 0, 955, 953, 1, 0, 0, 0, 956, 957, 3, 222, + 111, 0, 957, 169, 1, 0, 0, 0, 958, 959, 3, 226, + 113, 0, 959, 960, 3, 174, 87, 0, 960, 965, 1, 0, + 0, 0, 961, 962, 3, 214, 107, 0, 962, 963, 3, 172, + 86, 0, 963, 965, 1, 0, 0, 0, 964, 958, 1, 0, + 0, 0, 964, 961, 1, 0, 0, 0, 965, 171, 1, 0, + 0, 0, 966, 968, 3, 174, 87, 0, 967, 966, 1, 0, + 0, 0, 967, 968, 1, 0, 0, 0, 968, 173, 1, 0, + 0, 0, 969, 976, 3, 182, 91, 0, 970, 972, 5, 6, + 0, 0, 971, 973, 3, 180, 90, 0, 972, 971, 1, 0, + 0, 0, 972, 973, 1, 0, 0, 0, 973, 975, 1, 0, + 0, 0, 974, 970, 1, 0, 0, 0, 975, 978, 1, 0, + 0, 0, 976, 974, 1, 0, 0, 0, 976, 977, 1, 0, + 0, 0, 977, 175, 1, 0, 0, 0, 978, 976, 1, 0, + 0, 0, 979, 980, 3, 190, 95, 0, 980, 177, 1, 0, + 0, 0, 981, 982, 3, 230, 115, 0, 982, 179, 1, 0, + 0, 0, 983, 984, 3, 184, 92, 0, 984, 985, 3, 166, + 83, 0, 985, 181, 1, 0, 0, 0, 986, 987, 3, 184, + 92, 0, 987, 988, 3, 186, 93, 0, 988, 183, 1, 0, + 0, 0, 989, 992, 3, 176, 88, 0, 990, 992, 3, 178, + 89, 0, 991, 989, 1, 0, 0, 0, 991, 990, 1, 0, + 0, 0, 992, 185, 1, 0, 0, 0, 993, 998, 3, 188, + 94, 0, 994, 995, 5, 8, 0, 0, 995, 997, 3, 188, + 94, 0, 996, 994, 1, 0, 0, 0, 997, 1000, 1, 0, + 0, 0, 998, 996, 1, 0, 0, 0, 998, 999, 1, 0, + 0, 0, 999, 187, 1, 0, 0, 0, 1000, 998, 1, 0, + 0, 0, 1001, 1002, 3, 224, 112, 0, 1002, 189, 1, 0, + 0, 0, 1003, 1004, 3, 192, 96, 0, 1004, 191, 1, 0, + 0, 0, 1005, 1010, 3, 194, 97, 0, 1006, 1007, 5, 10, + 0, 0, 1007, 1009, 3, 194, 97, 0, 1008, 1006, 1, 0, + 0, 0, 1009, 1012, 1, 0, 0, 0, 1010, 1008, 1, 0, + 0, 0, 1010, 1011, 1, 0, 0, 0, 1011, 193, 1, 0, + 0, 0, 1012, 1010, 1, 0, 0, 0, 1013, 1018, 3, 198, + 99, 0, 1014, 1015, 5, 11, 0, 0, 1015, 1017, 3, 198, + 99, 0, 1016, 1014, 1, 0, 0, 0, 1017, 1020, 1, 0, + 0, 0, 1018, 1016, 1, 0, 0, 0, 1018, 1019, 1, 0, + 0, 0, 1019, 195, 1, 0, 0, 0, 1020, 1018, 1, 0, + 0, 0, 1021, 1023, 3, 202, 101, 0, 1022, 1024, 3, 200, + 100, 0, 1023, 1022, 1, 0, 0, 0, 1023, 1024, 1, 0, + 0, 0, 1024, 197, 1, 0, 0, 0, 1025, 1029, 3, 196, + 98, 0, 1026, 1027, 5, 12, 0, 0, 1027, 1029, 3, 196, + 98, 0, 1028, 1025, 1, 0, 0, 0, 1028, 1026, 1, 0, + 0, 0, 1029, 199, 1, 0, 0, 0, 1030, 1031, 7, 2, + 0, 0, 1031, 201, 1, 0, 0, 0, 1032, 1041, 3, 302, + 151, 0, 1033, 1041, 5, 9, 0, 0, 1034, 1035, 5, 15, + 0, 0, 1035, 1041, 3, 204, 102, 0, 1036, 1037, 5, 2, + 0, 0, 1037, 1038, 3, 190, 95, 0, 1038, 1039, 5, 3, + 0, 0, 1039, 1041, 1, 0, 0, 0, 1040, 1032, 1, 0, + 0, 0, 1040, 1033, 1, 0, 0, 0, 1040, 1034, 1, 0, + 0, 0, 1040, 1036, 1, 0, 0, 0, 1041, 203, 1, 0, + 0, 0, 1042, 1056, 3, 206, 103, 0, 1043, 1052, 5, 2, + 0, 0, 1044, 1049, 3, 206, 103, 0, 1045, 1046, 5, 10, + 0, 0, 1046, 1048, 3, 206, 103, 0, 1047, 1045, 1, 0, + 0, 0, 1048, 1051, 1, 0, 0, 0, 1049, 1047, 1, 0, + 0, 0, 1049, 1050, 1, 0, 0, 0, 1050, 1053, 1, 0, + 0, 0, 1051, 1049, 1, 0, 0, 0, 1052, 1044, 1, 0, + 0, 0, 1052, 1053, 1, 0, 0, 0, 1053, 1054, 1, 0, + 0, 0, 1054, 1056, 5, 3, 0, 0, 1055, 1042, 1, 0, + 0, 0, 1055, 1043, 1, 0, 0, 0, 1056, 205, 1, 0, + 0, 0, 1057, 1065, 3, 302, 151, 0, 1058, 1065, 5, 9, + 0, 0, 1059, 1062, 5, 12, 0, 0, 1060, 1063, 3, 302, + 151, 0, 1061, 1063, 5, 9, 0, 0, 1062, 1060, 1, 0, + 0, 0, 1062, 1061, 1, 0, 0, 0, 1063, 1065, 1, 0, + 0, 0, 1064, 1057, 1, 0, 0, 0, 1064, 1058, 1, 0, + 0, 0, 1064, 1059, 1, 0, 0, 0, 1065, 207, 1, 0, + 0, 0, 1066, 1067, 5, 150, 0, 0, 1067, 209, 1, 0, + 0, 0, 1068, 1071, 3, 218, 109, 0, 1069, 1071, 3, 212, + 106, 0, 1070, 1068, 1, 0, 0, 0, 1070, 1069, 1, 0, + 0, 0, 1071, 211, 1, 0, 0, 0, 1072, 1073, 5, 16, + 0, 0, 1073, 1074, 3, 162, 81, 0, 1074, 1075, 5, 17, + 0, 0, 1075, 213, 1, 0, 0, 0, 1076, 1079, 3, 220, + 110, 0, 1077, 1079, 3, 216, 108, 0, 1078, 1076, 1, 0, + 0, 0, 1078, 1077, 1, 0, 0, 0, 1079, 215, 1, 0, + 0, 0, 1080, 1081, 5, 16, 0, 0, 1081, 1082, 3, 174, + 87, 0, 1082, 1083, 5, 17, 0, 0, 1083, 217, 1, 0, + 0, 0, 1084, 1086, 5, 2, 0, 0, 1085, 1087, 3, 222, + 111, 0, 1086, 1085, 1, 0, 0, 0, 1087, 1088, 1, 0, + 0, 0, 1088, 1086, 1, 0, 0, 0, 1088, 1089, 1, 0, + 0, 0, 1089, 1090, 1, 0, 0, 0, 1090, 1091, 5, 3, + 0, 0, 1091, 219, 1, 0, 0, 0, 1092, 1094, 5, 2, + 0, 0, 1093, 1095, 3, 224, 112, 0, 1094, 1093, 1, 0, + 0, 0, 1095, 1096, 1, 0, 0, 0, 1096, 1094, 1, 0, + 0, 0, 1096, 1097, 1, 0, 0, 0, 1097, 1098, 1, 0, + 0, 0, 1098, 1099, 5, 3, 0, 0, 1099, 221, 1, 0, + 0, 0, 1100, 1103, 3, 226, 113, 0, 1101, 1103, 3, 210, + 105, 0, 1102, 1100, 1, 0, 0, 0, 1102, 1101, 1, 0, + 0, 0, 1103, 223, 1, 0, 0, 0, 1104, 1107, 3, 226, + 113, 0, 1105, 1107, 3, 214, 107, 0, 1106, 1104, 1, 0, + 0, 0, 1106, 1105, 1, 0, 0, 0, 1107, 225, 1, 0, + 0, 0, 1108, 1111, 3, 230, 115, 0, 1109, 1111, 3, 232, + 116, 0, 1110, 1108, 1, 0, 0, 0, 1110, 1109, 1, 0, + 0, 0, 1111, 227, 1, 0, 0, 0, 1112, 1115, 3, 230, + 115, 0, 1113, 1115, 3, 302, 151, 0, 1114, 1112, 1, 0, + 0, 0, 1114, 1113, 1, 0, 0, 0, 1115, 229, 1, 0, + 0, 0, 1116, 1117, 7, 3, 0, 0, 1117, 231, 1, 0, + 0, 0, 1118, 1125, 3, 302, 151, 0, 1119, 1125, 3, 288, + 144, 0, 1120, 1125, 3, 290, 145, 0, 1121, 1125, 3, 298, + 149, 0, 1122, 1125, 3, 306, 153, 0, 1123, 1125, 5, 165, + 0, 0, 1124, 1118, 1, 0, 0, 0, 1124, 1119, 1, 0, + 0, 0, 1124, 1120, 1, 0, 0, 0, 1124, 1121, 1, 0, + 0, 0, 1124, 1122, 1, 0, 0, 0, 1124, 1123, 1, 0, + 0, 0, 1125, 233, 1, 0, 0, 0, 1126, 1127, 3, 236, + 118, 0, 1127, 235, 1, 0, 0, 0, 1128, 1133, 3, 238, + 119, 0, 1129, 1130, 5, 18, 0, 0, 1130, 1132, 3, 238, + 119, 0, 1131, 1129, 1, 0, 0, 0, 1132, 1135, 1, 0, + 0, 0, 1133, 1131, 1, 0, 0, 0, 1133, 1134, 1, 0, + 0, 0, 1134, 237, 1, 0, 0, 0, 1135, 1133, 1, 0, + 0, 0, 1136, 1141, 3, 240, 120, 0, 1137, 1138, 5, 19, + 0, 0, 1138, 1140, 3, 240, 120, 0, 1139, 1137, 1, 0, + 0, 0, 1140, 1143, 1, 0, 0, 0, 1141, 1139, 1, 0, + 0, 0, 1141, 1142, 1, 0, 0, 0, 1142, 239, 1, 0, + 0, 0, 1143, 1141, 1, 0, 0, 0, 1144, 1145, 3, 242, + 121, 0, 1145, 241, 1, 0, 0, 0, 1146, 1164, 3, 244, + 122, 0, 1147, 1148, 5, 20, 0, 0, 1148, 1165, 3, 244, + 122, 0, 1149, 1150, 5, 21, 0, 0, 1150, 1165, 3, 244, + 122, 0, 1151, 1152, 5, 22, 0, 0, 1152, 1165, 3, 244, + 122, 0, 1153, 1154, 5, 23, 0, 0, 1154, 1165, 3, 244, + 122, 0, 1155, 1156, 5, 24, 0, 0, 1156, 1165, 3, 244, + 122, 0, 1157, 1158, 5, 25, 0, 0, 1158, 1165, 3, 244, + 122, 0, 1159, 1160, 5, 79, 0, 0, 1160, 1165, 3, 152, + 76, 0, 1161, 1162, 5, 78, 0, 0, 1162, 1163, 5, 79, + 0, 0, 1163, 1165, 3, 152, 76, 0, 1164, 1147, 1, 0, + 0, 0, 1164, 1149, 1, 0, 0, 0, 1164, 1151, 1, 0, + 0, 0, 1164, 1153, 1, 0, 0, 0, 1164, 1155, 1, 0, + 0, 0, 1164, 1157, 1, 0, 0, 0, 1164, 1159, 1, 0, + 0, 0, 1164, 1161, 1, 0, 0, 0, 1164, 1165, 1, 0, + 0, 0, 1165, 243, 1, 0, 0, 0, 1166, 1167, 3, 246, + 123, 0, 1167, 245, 1, 0, 0, 0, 1168, 1172, 3, 256, + 128, 0, 1169, 1171, 3, 248, 124, 0, 1170, 1169, 1, 0, + 0, 0, 1171, 1174, 1, 0, 0, 0, 1172, 1170, 1, 0, + 0, 0, 1172, 1173, 1, 0, 0, 0, 1173, 247, 1, 0, + 0, 0, 1174, 1172, 1, 0, 0, 0, 1175, 1176, 5, 13, + 0, 0, 1176, 1181, 3, 250, 125, 0, 1177, 1178, 5, 26, + 0, 0, 1178, 1181, 3, 252, 126, 0, 1179, 1181, 3, 254, + 127, 0, 1180, 1175, 1, 0, 0, 0, 1180, 1177, 1, 0, + 0, 0, 1180, 1179, 1, 0, 0, 0, 1181, 249, 1, 0, + 0, 0, 1182, 1183, 3, 256, 128, 0, 1183, 251, 1, 0, + 0, 0, 1184, 1185, 3, 256, 128, 0, 1185, 253, 1, 0, + 0, 0, 1186, 1189, 3, 294, 147, 0, 1187, 1189, 3, 296, + 148, 0, 1188, 1186, 1, 0, 0, 0, 1188, 1187, 1, 0, + 0, 0, 1189, 1193, 1, 0, 0, 0, 1190, 1192, 3, 258, + 129, 0, 1191, 1190, 1, 0, 0, 0, 1192, 1195, 1, 0, + 0, 0, 1193, 1191, 1, 0, 0, 0, 1193, 1194, 1, 0, + 0, 0, 1194, 255, 1, 0, 0, 0, 1195, 1193, 1, 0, + 0, 0, 1196, 1200, 3, 264, 132, 0, 1197, 1199, 3, 258, + 129, 0, 1198, 1197, 1, 0, 0, 0, 1199, 1202, 1, 0, + 0, 0, 1200, 1198, 1, 0, 0, 0, 1200, 1201, 1, 0, + 0, 0, 1201, 257, 1, 0, 0, 0, 1202, 1200, 1, 0, + 0, 0, 1203, 1206, 3, 260, 130, 0, 1204, 1206, 3, 262, + 131, 0, 1205, 1203, 1, 0, 0, 0, 1205, 1204, 1, 0, + 0, 0, 1206, 259, 1, 0, 0, 0, 1207, 1208, 5, 1, + 0, 0, 1208, 1209, 3, 264, 132, 0, 1209, 261, 1, 0, + 0, 0, 1210, 1211, 5, 11, 0, 0, 1211, 1212, 3, 264, + 132, 0, 1212, 263, 1, 0, 0, 0, 1213, 1214, 5, 15, + 0, 0, 1214, 1221, 3, 266, 133, 0, 1215, 1216, 5, 13, + 0, 0, 1216, 1221, 3, 266, 133, 0, 1217, 1218, 5, 26, + 0, 0, 1218, 1221, 3, 266, 133, 0, 1219, 1221, 3, 266, + 133, 0, 1220, 1213, 1, 0, 0, 0, 1220, 1215, 1, 0, + 0, 0, 1220, 1217, 1, 0, 0, 0, 1220, 1219, 1, 0, + 0, 0, 1221, 265, 1, 0, 0, 0, 1222, 1230, 3, 268, + 134, 0, 1223, 1230, 3, 270, 135, 0, 1224, 1230, 3, 286, + 143, 0, 1225, 1230, 3, 288, 144, 0, 1226, 1230, 3, 290, + 145, 0, 1227, 1230, 3, 298, 149, 0, 1228, 1230, 3, 230, + 115, 0, 1229, 1222, 1, 0, 0, 0, 1229, 1223, 1, 0, + 0, 0, 1229, 1224, 1, 0, 0, 0, 1229, 1225, 1, 0, + 0, 0, 1229, 1226, 1, 0, 0, 0, 1229, 1227, 1, 0, + 0, 0, 1229, 1228, 1, 0, 0, 0, 1230, 267, 1, 0, + 0, 0, 1231, 1232, 5, 2, 0, 0, 1232, 1233, 3, 234, + 117, 0, 1233, 1234, 5, 3, 0, 0, 1234, 269, 1, 0, + 0, 0, 1235, 1490, 3, 284, 142, 0, 1236, 1237, 5, 80, + 0, 0, 1237, 1238, 5, 2, 0, 0, 1238, 1239, 3, 234, + 117, 0, 1239, 1240, 5, 3, 0, 0, 1240, 1490, 1, 0, + 0, 0, 1241, 1490, 3, 274, 137, 0, 1242, 1243, 5, 82, + 0, 0, 1243, 1244, 5, 2, 0, 0, 1244, 1245, 3, 234, + 117, 0, 1245, 1246, 5, 8, 0, 0, 1246, 1247, 3, 234, + 117, 0, 1247, 1248, 5, 3, 0, 0, 1248, 1490, 1, 0, + 0, 0, 1249, 1250, 5, 83, 0, 0, 1250, 1251, 5, 2, + 0, 0, 1251, 1252, 3, 234, 117, 0, 1252, 1253, 5, 3, + 0, 0, 1253, 1490, 1, 0, 0, 0, 1254, 1255, 5, 84, + 0, 0, 1255, 1256, 5, 2, 0, 0, 1256, 1257, 3, 230, + 115, 0, 1257, 1258, 5, 3, 0, 0, 1258, 1490, 1, 0, + 0, 0, 1259, 1260, 5, 85, 0, 0, 1260, 1261, 5, 2, + 0, 0, 1261, 1262, 3, 234, 117, 0, 1262, 1263, 5, 3, + 0, 0, 1263, 1490, 1, 0, 0, 0, 1264, 1265, 5, 86, + 0, 0, 1265, 1266, 5, 2, 0, 0, 1266, 1267, 3, 234, + 117, 0, 1267, 1268, 5, 3, 0, 0, 1268, 1490, 1, 0, + 0, 0, 1269, 1275, 5, 87, 0, 0, 1270, 1271, 5, 2, + 0, 0, 1271, 1272, 3, 234, 117, 0, 1272, 1273, 5, 3, + 0, 0, 1273, 1276, 1, 0, 0, 0, 1274, 1276, 5, 165, + 0, 0, 1275, 1270, 1, 0, 0, 0, 1275, 1274, 1, 0, + 0, 0, 1276, 1490, 1, 0, 0, 0, 1277, 1278, 5, 88, + 0, 0, 1278, 1490, 5, 165, 0, 0, 1279, 1280, 5, 89, + 0, 0, 1280, 1281, 5, 2, 0, 0, 1281, 1282, 3, 234, + 117, 0, 1282, 1283, 5, 3, 0, 0, 1283, 1490, 1, 0, + 0, 0, 1284, 1285, 5, 90, 0, 0, 1285, 1286, 5, 2, + 0, 0, 1286, 1287, 3, 234, 117, 0, 1287, 1288, 5, 3, + 0, 0, 1288, 1490, 1, 0, 0, 0, 1289, 1290, 5, 91, + 0, 0, 1290, 1291, 5, 2, 0, 0, 1291, 1292, 3, 234, + 117, 0, 1292, 1293, 5, 3, 0, 0, 1293, 1490, 1, 0, + 0, 0, 1294, 1295, 5, 92, 0, 0, 1295, 1296, 5, 2, + 0, 0, 1296, 1297, 3, 234, 117, 0, 1297, 1298, 5, 3, + 0, 0, 1298, 1490, 1, 0, 0, 0, 1299, 1300, 5, 93, + 0, 0, 1300, 1490, 3, 152, 76, 0, 1301, 1490, 3, 276, + 138, 0, 1302, 1303, 5, 94, 0, 0, 1303, 1304, 5, 2, + 0, 0, 1304, 1305, 3, 234, 117, 0, 1305, 1306, 5, 3, + 0, 0, 1306, 1490, 1, 0, 0, 0, 1307, 1490, 3, 278, + 139, 0, 1308, 1309, 5, 95, 0, 0, 1309, 1310, 5, 2, + 0, 0, 1310, 1311, 3, 234, 117, 0, 1311, 1312, 5, 3, + 0, 0, 1312, 1490, 1, 0, 0, 0, 1313, 1314, 5, 96, + 0, 0, 1314, 1315, 5, 2, 0, 0, 1315, 1316, 3, 234, + 117, 0, 1316, 1317, 5, 3, 0, 0, 1317, 1490, 1, 0, + 0, 0, 1318, 1319, 5, 97, 0, 0, 1319, 1320, 5, 2, + 0, 0, 1320, 1321, 3, 234, 117, 0, 1321, 1322, 5, 3, + 0, 0, 1322, 1490, 1, 0, 0, 0, 1323, 1324, 5, 99, + 0, 0, 1324, 1325, 5, 2, 0, 0, 1325, 1326, 3, 234, + 117, 0, 1326, 1327, 5, 8, 0, 0, 1327, 1328, 3, 234, + 117, 0, 1328, 1329, 5, 3, 0, 0, 1329, 1490, 1, 0, + 0, 0, 1330, 1331, 5, 100, 0, 0, 1331, 1332, 5, 2, + 0, 0, 1332, 1333, 3, 234, 117, 0, 1333, 1334, 5, 8, + 0, 0, 1334, 1335, 3, 234, 117, 0, 1335, 1336, 5, 3, + 0, 0, 1336, 1490, 1, 0, 0, 0, 1337, 1338, 5, 101, + 0, 0, 1338, 1339, 5, 2, 0, 0, 1339, 1340, 3, 234, + 117, 0, 1340, 1341, 5, 8, 0, 0, 1341, 1342, 3, 234, + 117, 0, 1342, 1343, 5, 3, 0, 0, 1343, 1490, 1, 0, + 0, 0, 1344, 1345, 5, 102, 0, 0, 1345, 1346, 5, 2, + 0, 0, 1346, 1347, 3, 234, 117, 0, 1347, 1348, 5, 8, + 0, 0, 1348, 1349, 3, 234, 117, 0, 1349, 1350, 5, 3, + 0, 0, 1350, 1490, 1, 0, 0, 0, 1351, 1352, 5, 103, + 0, 0, 1352, 1353, 5, 2, 0, 0, 1353, 1354, 3, 234, + 117, 0, 1354, 1355, 5, 8, 0, 0, 1355, 1356, 3, 234, + 117, 0, 1356, 1357, 5, 3, 0, 0, 1357, 1490, 1, 0, + 0, 0, 1358, 1359, 5, 104, 0, 0, 1359, 1360, 5, 2, + 0, 0, 1360, 1361, 3, 234, 117, 0, 1361, 1362, 5, 3, + 0, 0, 1362, 1490, 1, 0, 0, 0, 1363, 1364, 5, 105, + 0, 0, 1364, 1365, 5, 2, 0, 0, 1365, 1366, 3, 234, + 117, 0, 1366, 1367, 5, 3, 0, 0, 1367, 1490, 1, 0, + 0, 0, 1368, 1369, 5, 106, 0, 0, 1369, 1370, 5, 2, + 0, 0, 1370, 1371, 3, 234, 117, 0, 1371, 1372, 5, 3, + 0, 0, 1372, 1490, 1, 0, 0, 0, 1373, 1374, 5, 107, + 0, 0, 1374, 1375, 5, 2, 0, 0, 1375, 1376, 3, 234, + 117, 0, 1376, 1377, 5, 3, 0, 0, 1377, 1490, 1, 0, + 0, 0, 1378, 1379, 5, 108, 0, 0, 1379, 1380, 5, 2, + 0, 0, 1380, 1381, 3, 234, 117, 0, 1381, 1382, 5, 3, + 0, 0, 1382, 1490, 1, 0, 0, 0, 1383, 1384, 5, 109, + 0, 0, 1384, 1385, 5, 2, 0, 0, 1385, 1386, 3, 234, + 117, 0, 1386, 1387, 5, 3, 0, 0, 1387, 1490, 1, 0, + 0, 0, 1388, 1389, 5, 110, 0, 0, 1389, 1390, 5, 2, + 0, 0, 1390, 1391, 3, 234, 117, 0, 1391, 1392, 5, 3, + 0, 0, 1392, 1490, 1, 0, 0, 0, 1393, 1394, 5, 111, + 0, 0, 1394, 1395, 5, 2, 0, 0, 1395, 1396, 3, 234, + 117, 0, 1396, 1397, 5, 3, 0, 0, 1397, 1490, 1, 0, + 0, 0, 1398, 1399, 5, 112, 0, 0, 1399, 1490, 5, 165, + 0, 0, 1400, 1401, 5, 113, 0, 0, 1401, 1490, 5, 165, + 0, 0, 1402, 1403, 5, 114, 0, 0, 1403, 1490, 5, 165, + 0, 0, 1404, 1405, 5, 119, 0, 0, 1405, 1406, 5, 2, + 0, 0, 1406, 1407, 3, 234, 117, 0, 1407, 1408, 5, 3, + 0, 0, 1408, 1490, 1, 0, 0, 0, 1409, 1410, 5, 115, + 0, 0, 1410, 1411, 5, 2, 0, 0, 1411, 1412, 3, 234, + 117, 0, 1412, 1413, 5, 3, 0, 0, 1413, 1490, 1, 0, + 0, 0, 1414, 1415, 5, 116, 0, 0, 1415, 1416, 5, 2, + 0, 0, 1416, 1417, 3, 234, 117, 0, 1417, 1418, 5, 3, + 0, 0, 1418, 1490, 1, 0, 0, 0, 1419, 1420, 5, 117, + 0, 0, 1420, 1421, 5, 2, 0, 0, 1421, 1422, 3, 234, + 117, 0, 1422, 1423, 5, 3, 0, 0, 1423, 1490, 1, 0, + 0, 0, 1424, 1425, 5, 118, 0, 0, 1425, 1426, 5, 2, + 0, 0, 1426, 1427, 3, 234, 117, 0, 1427, 1428, 5, 3, + 0, 0, 1428, 1490, 1, 0, 0, 0, 1429, 1430, 5, 120, + 0, 0, 1430, 1490, 3, 152, 76, 0, 1431, 1432, 5, 121, + 0, 0, 1432, 1433, 5, 2, 0, 0, 1433, 1434, 3, 234, + 117, 0, 1434, 1435, 5, 8, 0, 0, 1435, 1436, 3, 234, + 117, 0, 1436, 1437, 5, 8, 0, 0, 1437, 1438, 3, 234, + 117, 0, 1438, 1439, 5, 3, 0, 0, 1439, 1490, 1, 0, + 0, 0, 1440, 1441, 5, 122, 0, 0, 1441, 1442, 5, 2, + 0, 0, 1442, 1443, 3, 234, 117, 0, 1443, 1444, 5, 8, + 0, 0, 1444, 1445, 3, 234, 117, 0, 1445, 1446, 5, 3, + 0, 0, 1446, 1490, 1, 0, 0, 0, 1447, 1448, 5, 123, + 0, 0, 1448, 1449, 5, 2, 0, 0, 1449, 1450, 3, 234, + 117, 0, 1450, 1451, 5, 8, 0, 0, 1451, 1452, 3, 234, + 117, 0, 1452, 1453, 5, 3, 0, 0, 1453, 1490, 1, 0, + 0, 0, 1454, 1455, 5, 124, 0, 0, 1455, 1456, 5, 2, + 0, 0, 1456, 1457, 3, 234, 117, 0, 1457, 1458, 5, 8, + 0, 0, 1458, 1459, 3, 234, 117, 0, 1459, 1460, 5, 3, + 0, 0, 1460, 1490, 1, 0, 0, 0, 1461, 1462, 5, 125, + 0, 0, 1462, 1463, 5, 2, 0, 0, 1463, 1464, 3, 234, + 117, 0, 1464, 1465, 5, 3, 0, 0, 1465, 1490, 1, 0, + 0, 0, 1466, 1467, 5, 126, 0, 0, 1467, 1468, 5, 2, + 0, 0, 1468, 1469, 3, 234, 117, 0, 1469, 1470, 5, 3, + 0, 0, 1470, 1490, 1, 0, 0, 0, 1471, 1472, 5, 127, + 0, 0, 1472, 1473, 5, 2, 0, 0, 1473, 1474, 3, 234, + 117, 0, 1474, 1475, 5, 3, 0, 0, 1475, 1490, 1, 0, + 0, 0, 1476, 1477, 5, 128, 0, 0, 1477, 1478, 5, 2, + 0, 0, 1478, 1479, 3, 234, 117, 0, 1479, 1480, 5, 3, + 0, 0, 1480, 1490, 1, 0, 0, 0, 1481, 1482, 5, 129, + 0, 0, 1482, 1483, 5, 2, 0, 0, 1483, 1484, 3, 234, + 117, 0, 1484, 1485, 5, 3, 0, 0, 1485, 1490, 1, 0, + 0, 0, 1486, 1490, 3, 272, 136, 0, 1487, 1490, 3, 280, + 140, 0, 1488, 1490, 3, 282, 141, 0, 1489, 1235, 1, 0, + 0, 0, 1489, 1236, 1, 0, 0, 0, 1489, 1241, 1, 0, + 0, 0, 1489, 1242, 1, 0, 0, 0, 1489, 1249, 1, 0, + 0, 0, 1489, 1254, 1, 0, 0, 0, 1489, 1259, 1, 0, + 0, 0, 1489, 1264, 1, 0, 0, 0, 1489, 1269, 1, 0, + 0, 0, 1489, 1277, 1, 0, 0, 0, 1489, 1279, 1, 0, + 0, 0, 1489, 1284, 1, 0, 0, 0, 1489, 1289, 1, 0, + 0, 0, 1489, 1294, 1, 0, 0, 0, 1489, 1299, 1, 0, + 0, 0, 1489, 1301, 1, 0, 0, 0, 1489, 1302, 1, 0, + 0, 0, 1489, 1307, 1, 0, 0, 0, 1489, 1308, 1, 0, + 0, 0, 1489, 1313, 1, 0, 0, 0, 1489, 1318, 1, 0, + 0, 0, 1489, 1323, 1, 0, 0, 0, 1489, 1330, 1, 0, + 0, 0, 1489, 1337, 1, 0, 0, 0, 1489, 1344, 1, 0, + 0, 0, 1489, 1351, 1, 0, 0, 0, 1489, 1358, 1, 0, + 0, 0, 1489, 1363, 1, 0, 0, 0, 1489, 1368, 1, 0, + 0, 0, 1489, 1373, 1, 0, 0, 0, 1489, 1378, 1, 0, + 0, 0, 1489, 1383, 1, 0, 0, 0, 1489, 1388, 1, 0, + 0, 0, 1489, 1393, 1, 0, 0, 0, 1489, 1398, 1, 0, + 0, 0, 1489, 1400, 1, 0, 0, 0, 1489, 1402, 1, 0, + 0, 0, 1489, 1404, 1, 0, 0, 0, 1489, 1409, 1, 0, + 0, 0, 1489, 1414, 1, 0, 0, 0, 1489, 1419, 1, 0, + 0, 0, 1489, 1424, 1, 0, 0, 0, 1489, 1429, 1, 0, + 0, 0, 1489, 1431, 1, 0, 0, 0, 1489, 1440, 1, 0, + 0, 0, 1489, 1447, 1, 0, 0, 0, 1489, 1454, 1, 0, + 0, 0, 1489, 1461, 1, 0, 0, 0, 1489, 1466, 1, 0, + 0, 0, 1489, 1471, 1, 0, 0, 0, 1489, 1476, 1, 0, + 0, 0, 1489, 1481, 1, 0, 0, 0, 1489, 1486, 1, 0, + 0, 0, 1489, 1487, 1, 0, 0, 0, 1489, 1488, 1, 0, + 0, 0, 1490, 271, 1, 0, 0, 0, 1491, 1492, 5, 130, + 0, 0, 1492, 1493, 5, 2, 0, 0, 1493, 1494, 3, 234, + 117, 0, 1494, 1495, 5, 8, 0, 0, 1495, 1498, 3, 234, + 117, 0, 1496, 1497, 5, 8, 0, 0, 1497, 1499, 3, 234, + 117, 0, 1498, 1496, 1, 0, 0, 0, 1498, 1499, 1, 0, + 0, 0, 1499, 1500, 1, 0, 0, 0, 1500, 1501, 5, 3, + 0, 0, 1501, 273, 1, 0, 0, 0, 1502, 1503, 5, 81, + 0, 0, 1503, 1504, 5, 2, 0, 0, 1504, 1505, 3, 234, + 117, 0, 1505, 1506, 5, 3, 0, 0, 1506, 275, 1, 0, + 0, 0, 1507, 1508, 5, 131, 0, 0, 1508, 1509, 5, 2, + 0, 0, 1509, 1510, 3, 234, 117, 0, 1510, 1511, 5, 8, + 0, 0, 1511, 1514, 3, 234, 117, 0, 1512, 1513, 5, 8, + 0, 0, 1513, 1515, 3, 234, 117, 0, 1514, 1512, 1, 0, + 0, 0, 1514, 1515, 1, 0, 0, 0, 1515, 1516, 1, 0, + 0, 0, 1516, 1517, 5, 3, 0, 0, 1517, 277, 1, 0, + 0, 0, 1518, 1519, 5, 132, 0, 0, 1519, 1520, 5, 2, + 0, 0, 1520, 1521, 3, 234, 117, 0, 1521, 1522, 5, 8, + 0, 0, 1522, 1523, 3, 234, 117, 0, 1523, 1524, 5, 8, + 0, 0, 1524, 1527, 3, 234, 117, 0, 1525, 1526, 5, 8, + 0, 0, 1526, 1528, 3, 234, 117, 0, 1527, 1525, 1, 0, + 0, 0, 1527, 1528, 1, 0, 0, 0, 1528, 1529, 1, 0, + 0, 0, 1529, 1530, 5, 3, 0, 0, 1530, 279, 1, 0, + 0, 0, 1531, 1532, 5, 133, 0, 0, 1532, 1533, 3, 110, + 55, 0, 1533, 281, 1, 0, 0, 0, 1534, 1535, 5, 78, + 0, 0, 1535, 1536, 5, 133, 0, 0, 1536, 1537, 3, 110, + 55, 0, 1537, 283, 1, 0, 0, 0, 1538, 1539, 5, 134, + 0, 0, 1539, 1541, 5, 2, 0, 0, 1540, 1542, 5, 33, + 0, 0, 1541, 1540, 1, 0, 0, 0, 1541, 1542, 1, 0, + 0, 0, 1542, 1545, 1, 0, 0, 0, 1543, 1546, 5, 1, + 0, 0, 1544, 1546, 3, 234, 117, 0, 1545, 1543, 1, 0, + 0, 0, 1545, 1544, 1, 0, 0, 0, 1546, 1547, 1, 0, + 0, 0, 1547, 1611, 5, 3, 0, 0, 1548, 1549, 5, 135, + 0, 0, 1549, 1551, 5, 2, 0, 0, 1550, 1552, 5, 33, + 0, 0, 1551, 1550, 1, 0, 0, 0, 1551, 1552, 1, 0, + 0, 0, 1552, 1553, 1, 0, 0, 0, 1553, 1554, 3, 234, + 117, 0, 1554, 1555, 5, 3, 0, 0, 1555, 1611, 1, 0, + 0, 0, 1556, 1557, 5, 136, 0, 0, 1557, 1559, 5, 2, + 0, 0, 1558, 1560, 5, 33, 0, 0, 1559, 1558, 1, 0, + 0, 0, 1559, 1560, 1, 0, 0, 0, 1560, 1561, 1, 0, + 0, 0, 1561, 1562, 3, 234, 117, 0, 1562, 1563, 5, 3, + 0, 0, 1563, 1611, 1, 0, 0, 0, 1564, 1565, 5, 137, + 0, 0, 1565, 1567, 5, 2, 0, 0, 1566, 1568, 5, 33, + 0, 0, 1567, 1566, 1, 0, 0, 0, 1567, 1568, 1, 0, + 0, 0, 1568, 1569, 1, 0, 0, 0, 1569, 1570, 3, 234, + 117, 0, 1570, 1571, 5, 3, 0, 0, 1571, 1611, 1, 0, + 0, 0, 1572, 1573, 5, 138, 0, 0, 1573, 1575, 5, 2, + 0, 0, 1574, 1576, 5, 33, 0, 0, 1575, 1574, 1, 0, + 0, 0, 1575, 1576, 1, 0, 0, 0, 1576, 1577, 1, 0, + 0, 0, 1577, 1578, 3, 234, 117, 0, 1578, 1579, 5, 3, + 0, 0, 1579, 1611, 1, 0, 0, 0, 1580, 1581, 5, 139, + 0, 0, 1581, 1583, 5, 2, 0, 0, 1582, 1584, 5, 33, + 0, 0, 1583, 1582, 1, 0, 0, 0, 1583, 1584, 1, 0, + 0, 0, 1584, 1585, 1, 0, 0, 0, 1585, 1586, 3, 234, + 117, 0, 1586, 1587, 5, 3, 0, 0, 1587, 1611, 1, 0, + 0, 0, 1588, 1589, 5, 140, 0, 0, 1589, 1591, 5, 2, + 0, 0, 1590, 1592, 5, 33, 0, 0, 1591, 1590, 1, 0, + 0, 0, 1591, 1592, 1, 0, 0, 0, 1592, 1593, 1, 0, + 0, 0, 1593, 1594, 3, 234, 117, 0, 1594, 1595, 5, 3, + 0, 0, 1595, 1611, 1, 0, 0, 0, 1596, 1597, 5, 43, + 0, 0, 1597, 1599, 5, 2, 0, 0, 1598, 1600, 5, 33, + 0, 0, 1599, 1598, 1, 0, 0, 0, 1599, 1600, 1, 0, + 0, 0, 1600, 1601, 1, 0, 0, 0, 1601, 1606, 3, 234, + 117, 0, 1602, 1603, 5, 6, 0, 0, 1603, 1604, 5, 141, + 0, 0, 1604, 1605, 5, 20, 0, 0, 1605, 1607, 3, 300, + 150, 0, 1606, 1602, 1, 0, 0, 0, 1606, 1607, 1, 0, + 0, 0, 1607, 1608, 1, 0, 0, 0, 1608, 1609, 5, 3, + 0, 0, 1609, 1611, 1, 0, 0, 0, 1610, 1538, 1, 0, + 0, 0, 1610, 1548, 1, 0, 0, 0, 1610, 1556, 1, 0, + 0, 0, 1610, 1564, 1, 0, 0, 0, 1610, 1572, 1, 0, + 0, 0, 1610, 1580, 1, 0, 0, 0, 1610, 1588, 1, 0, + 0, 0, 1610, 1596, 1, 0, 0, 0, 1611, 285, 1, 0, + 0, 0, 1612, 1614, 3, 302, 151, 0, 1613, 1615, 3, 150, + 75, 0, 1614, 1613, 1, 0, 0, 0, 1614, 1615, 1, 0, + 0, 0, 1615, 287, 1, 0, 0, 0, 1616, 1620, 3, 300, + 150, 0, 1617, 1621, 5, 148, 0, 0, 1618, 1619, 5, 27, + 0, 0, 1619, 1621, 3, 302, 151, 0, 1620, 1617, 1, 0, + 0, 0, 1620, 1618, 1, 0, 0, 0, 1620, 1621, 1, 0, + 0, 0, 1621, 289, 1, 0, 0, 0, 1622, 1626, 3, 292, + 146, 0, 1623, 1626, 3, 294, 147, 0, 1624, 1626, 3, 296, + 148, 0, 1625, 1622, 1, 0, 0, 0, 1625, 1623, 1, 0, + 0, 0, 1625, 1624, 1, 0, 0, 0, 1626, 291, 1, 0, + 0, 0, 1627, 1628, 7, 4, 0, 0, 1628, 293, 1, 0, + 0, 0, 1629, 1630, 7, 5, 0, 0, 1630, 295, 1, 0, + 0, 0, 1631, 1632, 7, 6, 0, 0, 1632, 297, 1, 0, + 0, 0, 1633, 1634, 7, 7, 0, 0, 1634, 299, 1, 0, + 0, 0, 1635, 1636, 7, 8, 0, 0, 1636, 301, 1, 0, + 0, 0, 1637, 1639, 5, 149, 0, 0, 1638, 1637, 1, 0, + 0, 0, 1638, 1639, 1, 0, 0, 0, 1639, 1642, 1, 0, + 0, 0, 1640, 1643, 3, 308, 154, 0, 1641, 1643, 3, 304, + 152, 0, 1642, 1640, 1, 0, 0, 0, 1642, 1641, 1, 0, + 0, 0, 1643, 303, 1, 0, 0, 0, 1644, 1647, 3, 310, + 155, 0, 1645, 1647, 3, 312, 156, 0, 1646, 1644, 1, 0, + 0, 0, 1646, 1645, 1, 0, 0, 0, 1647, 305, 1, 0, + 0, 0, 1648, 1649, 7, 9, 0, 0, 1649, 307, 1, 0, + 0, 0, 1650, 1651, 5, 142, 0, 0, 1651, 309, 1, 0, + 0, 0, 1652, 1653, 5, 144, 0, 0, 1653, 311, 1, 0, + 0, 0, 1654, 1655, 5, 143, 0, 0, 1655, 313, 1, 0, + 0, 0, 163, 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, 686, 689, 698, 709, 713, 716, 720, + 727, 734, 736, 741, 746, 751, 756, 759, 764, 766, 776, 787, + 804, 811, 821, 825, 831, 840, 845, 852, 862, 871, 879, 886, + 891, 900, 905, 909, 916, 918, 926, 929, 937, 941, 946, 953, + 964, 967, 972, 976, 991, 998, 1010, 1018, 1023, 1028, 1040, 1049, + 1052, 1055, 1062, 1064, 1070, 1078, 1088, 1096, 1102, 1106, 1110, 1114, + 1124, 1133, 1141, 1164, 1172, 1180, 1188, 1193, 1200, 1205, 1220, 1229, + 1275, 1489, 1498, 1514, 1527, 1541, 1545, 1551, 1559, 1567, 1575, 1583, + 1591, 1599, 1606, 1610, 1614, 1620, 1625, 1638, 1642, 1646}; staticData->serializedATN = antlr4::atn::SerializedATNView( serializedATNSegment, sizeof(serializedATNSegment) / sizeof(serializedATNSegment[0])); @@ -6340,15 +6342,15 @@ tree::TerminalNode* SparqlAutomaticParser::GraphOrDefaultContext::DEFAULT() { return getToken(SparqlAutomaticParser::DEFAULT, 0); } -tree::TerminalNode* SparqlAutomaticParser::GraphOrDefaultContext::GRAPH() { - return getToken(SparqlAutomaticParser::GRAPH, 0); -} - SparqlAutomaticParser::IriContext* SparqlAutomaticParser::GraphOrDefaultContext::iri() { return getRuleContext(0); } +tree::TerminalNode* SparqlAutomaticParser::GraphOrDefaultContext::GRAPH() { + return getToken(SparqlAutomaticParser::GRAPH, 0); +} + size_t SparqlAutomaticParser::GraphOrDefaultContext::getRuleIndex() const { return SparqlAutomaticParser::RuleGraphOrDefault; } @@ -6378,6 +6380,7 @@ SparqlAutomaticParser::graphOrDefault() { GraphOrDefaultContext* _localctx = _tracker.createInstance(_ctx, getState()); enterRule(_localctx, 94, SparqlAutomaticParser::RuleGraphOrDefault); + size_t _la = 0; #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -6387,7 +6390,7 @@ SparqlAutomaticParser::graphOrDefault() { exitRule(); }); try { - setState(687); + setState(689); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::DEFAULT: { @@ -6397,11 +6400,21 @@ SparqlAutomaticParser::graphOrDefault() { break; } - case SparqlAutomaticParser::GRAPH: { + case SparqlAutomaticParser::GRAPH: + case SparqlAutomaticParser::IRI_REF: + case SparqlAutomaticParser::PNAME_NS: + case SparqlAutomaticParser::PNAME_LN: + case SparqlAutomaticParser::PREFIX_LANGTAG: { enterOuterAlt(_localctx, 2); - setState(685); - match(SparqlAutomaticParser::GRAPH); setState(686); + _errHandler->sync(this); + + _la = _input->LA(1); + if (_la == SparqlAutomaticParser::GRAPH) { + setState(685); + match(SparqlAutomaticParser::GRAPH); + } + setState(688); iri(); break; } @@ -6473,9 +6486,9 @@ SparqlAutomaticParser::GraphRefContext* SparqlAutomaticParser::graphRef() { }); try { enterOuterAlt(_localctx, 1); - setState(689); + setState(691); match(SparqlAutomaticParser::GRAPH); - setState(690); + setState(692); iri(); } catch (RecognitionException& e) { @@ -6549,33 +6562,33 @@ SparqlAutomaticParser::graphRefAll() { exitRule(); }); try { - setState(696); + setState(698); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::GRAPH: { enterOuterAlt(_localctx, 1); - setState(692); + setState(694); graphRef(); break; } case SparqlAutomaticParser::DEFAULT: { enterOuterAlt(_localctx, 2); - setState(693); + setState(695); match(SparqlAutomaticParser::DEFAULT); break; } case SparqlAutomaticParser::NAMED: { enterOuterAlt(_localctx, 3); - setState(694); + setState(696); match(SparqlAutomaticParser::NAMED); break; } case SparqlAutomaticParser::ALL: { enterOuterAlt(_localctx, 4); - setState(695); + setState(697); match(SparqlAutomaticParser::ALL); break; } @@ -6644,11 +6657,11 @@ SparqlAutomaticParser::quadPattern() { }); try { enterOuterAlt(_localctx, 1); - setState(698); + setState(700); match(SparqlAutomaticParser::T__3); - setState(699); + setState(701); quads(); - setState(700); + setState(702); match(SparqlAutomaticParser::T__4); } catch (RecognitionException& e) { @@ -6710,11 +6723,11 @@ SparqlAutomaticParser::QuadDataContext* SparqlAutomaticParser::quadData() { }); try { enterOuterAlt(_localctx, 1); - setState(702); + setState(704); match(SparqlAutomaticParser::T__3); - setState(703); + setState(705); quads(); - setState(704); + setState(706); match(SparqlAutomaticParser::T__4); } catch (RecognitionException& e) { @@ -6792,41 +6805,41 @@ SparqlAutomaticParser::QuadsContext* SparqlAutomaticParser::quads() { }); try { enterOuterAlt(_localctx, 1); - setState(707); + setState(709); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 29228991) != 0) { - setState(706); + setState(708); triplesTemplate(); } - setState(718); + setState(720); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::GRAPH) { - setState(709); - quadsNotTriples(); setState(711); + quadsNotTriples(); + setState(713); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__6) { - setState(710); + setState(712); match(SparqlAutomaticParser::T__6); } - setState(714); + setState(716); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 29228991) != 0) { - setState(713); + setState(715); triplesTemplate(); } - setState(720); + setState(722); _errHandler->sync(this); _la = _input->LA(1); } @@ -6901,23 +6914,23 @@ SparqlAutomaticParser::quadsNotTriples() { }); try { enterOuterAlt(_localctx, 1); - setState(721); + setState(723); match(SparqlAutomaticParser::GRAPH); - setState(722); + setState(724); varOrIri(); - setState(723); - match(SparqlAutomaticParser::T__3); setState(725); + match(SparqlAutomaticParser::T__3); + setState(727); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 29228991) != 0) { - setState(724); + setState(726); triplesTemplate(); } - setState(727); + setState(729); match(SparqlAutomaticParser::T__4); } catch (RecognitionException& e) { @@ -6986,23 +6999,23 @@ SparqlAutomaticParser::triplesTemplate() { }); try { enterOuterAlt(_localctx, 1); - setState(729); + setState(731); triplesSameSubject(); - setState(734); + setState(736); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__6) { - setState(730); - match(SparqlAutomaticParser::T__6); setState(732); + match(SparqlAutomaticParser::T__6); + setState(734); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 29228991) != 0) { - setState(731); + setState(733); triplesTemplate(); } } @@ -7072,13 +7085,13 @@ SparqlAutomaticParser::groupGraphPattern() { }); try { enterOuterAlt(_localctx, 1); - setState(736); + setState(738); match(SparqlAutomaticParser::T__3); - setState(739); + setState(741); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::SELECT: { - setState(737); + setState(739); subSelect(); break; } @@ -7118,7 +7131,7 @@ SparqlAutomaticParser::groupGraphPattern() { case SparqlAutomaticParser::STRING_LITERAL_LONG2: case SparqlAutomaticParser::NIL: case SparqlAutomaticParser::ANON: { - setState(738); + setState(740); groupGraphPatternSub(); break; } @@ -7126,7 +7139,7 @@ SparqlAutomaticParser::groupGraphPattern() { default: throw NoViableAltException(this); } - setState(741); + setState(743); match(SparqlAutomaticParser::T__4); } catch (RecognitionException& e) { @@ -7207,17 +7220,17 @@ SparqlAutomaticParser::groupGraphPatternSub() { }); try { enterOuterAlt(_localctx, 1); - setState(744); + setState(746); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 29228991) != 0) { - setState(743); + setState(745); triplesBlock(); } - setState(749); + setState(751); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__3 @@ -7225,9 +7238,9 @@ SparqlAutomaticParser::groupGraphPatternSub() { || _la == SparqlAutomaticParser::VALUES || (((_la - 69) & ~0x3fULL) == 0) && ((1ULL << (_la - 69)) & 349) != 0) { - setState(746); + setState(748); graphPatternNotTriplesAndMaybeTriples(); - setState(751); + setState(753); _errHandler->sync(this); _la = _input->LA(1); } @@ -7306,24 +7319,24 @@ SparqlAutomaticParser::graphPatternNotTriplesAndMaybeTriples() { }); try { enterOuterAlt(_localctx, 1); - setState(752); - graphPatternNotTriples(); setState(754); + graphPatternNotTriples(); + setState(756); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__6) { - setState(753); + setState(755); match(SparqlAutomaticParser::T__6); } - setState(757); + setState(759); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 29228991) != 0) { - setState(756); + setState(758); triplesBlock(); } @@ -7394,23 +7407,23 @@ SparqlAutomaticParser::triplesBlock() { }); try { enterOuterAlt(_localctx, 1); - setState(759); + setState(761); triplesSameSubjectPath(); - setState(764); + setState(766); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__6) { - setState(760); - match(SparqlAutomaticParser::T__6); setState(762); + match(SparqlAutomaticParser::T__6); + setState(764); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 29228991) != 0) { - setState(761); + setState(763); triplesBlock(); } } @@ -7514,61 +7527,61 @@ SparqlAutomaticParser::graphPatternNotTriples() { exitRule(); }); try { - setState(774); + setState(776); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__3: { enterOuterAlt(_localctx, 1); - setState(766); + setState(768); groupOrUnionGraphPattern(); break; } case SparqlAutomaticParser::OPTIONAL: { enterOuterAlt(_localctx, 2); - setState(767); + setState(769); optionalGraphPattern(); break; } case SparqlAutomaticParser::MINUS: { enterOuterAlt(_localctx, 3); - setState(768); + setState(770); minusGraphPattern(); break; } case SparqlAutomaticParser::GRAPH: { enterOuterAlt(_localctx, 4); - setState(769); + setState(771); graphGraphPattern(); break; } case SparqlAutomaticParser::SERVICE: { enterOuterAlt(_localctx, 5); - setState(770); + setState(772); serviceGraphPattern(); break; } case SparqlAutomaticParser::FILTER: { enterOuterAlt(_localctx, 6); - setState(771); + setState(773); filterR(); break; } case SparqlAutomaticParser::BIND: { enterOuterAlt(_localctx, 7); - setState(772); + setState(774); bind(); break; } case SparqlAutomaticParser::VALUES: { enterOuterAlt(_localctx, 8); - setState(773); + setState(775); inlineData(); break; } @@ -7644,9 +7657,9 @@ SparqlAutomaticParser::optionalGraphPattern() { }); try { enterOuterAlt(_localctx, 1); - setState(776); + setState(778); match(SparqlAutomaticParser::OPTIONAL); - setState(777); + setState(779); groupGraphPattern(); } catch (RecognitionException& e) { @@ -7718,11 +7731,11 @@ SparqlAutomaticParser::graphGraphPattern() { }); try { enterOuterAlt(_localctx, 1); - setState(779); + setState(781); match(SparqlAutomaticParser::GRAPH); - setState(780); + setState(782); varOrIri(); - setState(781); + setState(783); groupGraphPattern(); } catch (RecognitionException& e) { @@ -7801,19 +7814,19 @@ SparqlAutomaticParser::serviceGraphPattern() { }); try { enterOuterAlt(_localctx, 1); - setState(783); - match(SparqlAutomaticParser::SERVICE); setState(785); + match(SparqlAutomaticParser::SERVICE); + setState(787); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::SILENT) { - setState(784); + setState(786); match(SparqlAutomaticParser::SILENT); } - setState(787); + setState(789); varOrIri(); - setState(788); + setState(790); groupGraphPattern(); } catch (RecognitionException& e) { @@ -7887,17 +7900,17 @@ SparqlAutomaticParser::BindContext* SparqlAutomaticParser::bind() { }); try { enterOuterAlt(_localctx, 1); - setState(790); + setState(792); match(SparqlAutomaticParser::BIND); - setState(791); + setState(793); match(SparqlAutomaticParser::T__1); - setState(792); + setState(794); expression(); - setState(793); + setState(795); match(SparqlAutomaticParser::AS); - setState(794); + setState(796); var(); - setState(795); + setState(797); match(SparqlAutomaticParser::T__2); } catch (RecognitionException& e) { @@ -7963,9 +7976,9 @@ SparqlAutomaticParser::InlineDataContext* SparqlAutomaticParser::inlineData() { }); try { enterOuterAlt(_localctx, 1); - setState(797); + setState(799); match(SparqlAutomaticParser::VALUES); - setState(798); + setState(800); dataBlock(); } catch (RecognitionException& e) { @@ -8031,13 +8044,13 @@ SparqlAutomaticParser::DataBlockContext* SparqlAutomaticParser::dataBlock() { exitRule(); }); try { - setState(802); + setState(804); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::VAR1: case SparqlAutomaticParser::VAR2: { enterOuterAlt(_localctx, 1); - setState(800); + setState(802); inlineDataOneVar(); break; } @@ -8045,7 +8058,7 @@ SparqlAutomaticParser::DataBlockContext* SparqlAutomaticParser::dataBlock() { case SparqlAutomaticParser::T__1: case SparqlAutomaticParser::NIL: { enterOuterAlt(_localctx, 2); - setState(801); + setState(803); inlineDataFull(); break; } @@ -8125,24 +8138,24 @@ SparqlAutomaticParser::inlineDataOneVar() { }); try { enterOuterAlt(_localctx, 1); - setState(804); + setState(806); var(); - setState(805); + setState(807); match(SparqlAutomaticParser::T__3); - setState(809); + setState(811); _errHandler->sync(this); _la = _input->LA(1); while ((((_la - 28) & ~0x3fULL) == 0) && ((1ULL << (_la - 28)) & 70368744177667) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 4063111) != 0) { - setState(806); + setState(808); dataBlockValue(); - setState(811); + setState(813); _errHandler->sync(this); _la = _input->LA(1); } - setState(812); + setState(814); match(SparqlAutomaticParser::T__4); } catch (RecognitionException& e) { @@ -8225,31 +8238,31 @@ SparqlAutomaticParser::inlineDataFull() { }); try { enterOuterAlt(_localctx, 1); - setState(823); + setState(825); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::NIL: { - setState(814); + setState(816); match(SparqlAutomaticParser::NIL); break; } case SparqlAutomaticParser::T__1: { - setState(815); + setState(817); match(SparqlAutomaticParser::T__1); - setState(819); + setState(821); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::VAR1 || _la == SparqlAutomaticParser::VAR2) { - setState(816); + setState(818); var(); - setState(821); + setState(823); _errHandler->sync(this); _la = _input->LA(1); } - setState(822); + setState(824); match(SparqlAutomaticParser::T__2); break; } @@ -8257,20 +8270,20 @@ SparqlAutomaticParser::inlineDataFull() { default: throw NoViableAltException(this); } - setState(825); + setState(827); match(SparqlAutomaticParser::T__3); - setState(829); + setState(831); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__1 || _la == SparqlAutomaticParser::NIL) { - setState(826); + setState(828); dataBlockSingle(); - setState(831); + setState(833); _errHandler->sync(this); _la = _input->LA(1); } - setState(832); + setState(834); match(SparqlAutomaticParser::T__4); } catch (RecognitionException& e) { @@ -8343,32 +8356,32 @@ SparqlAutomaticParser::dataBlockSingle() { }); try { enterOuterAlt(_localctx, 1); - setState(843); + setState(845); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__1: { - setState(834); + setState(836); match(SparqlAutomaticParser::T__1); - setState(838); + setState(840); _errHandler->sync(this); _la = _input->LA(1); while ((((_la - 28) & ~0x3fULL) == 0) && ((1ULL << (_la - 28)) & 70368744177667) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 4063111) != 0) { - setState(835); + setState(837); dataBlockValue(); - setState(840); + setState(842); _errHandler->sync(this); _la = _input->LA(1); } - setState(841); + setState(843); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::NIL: { - setState(842); + setState(844); match(SparqlAutomaticParser::NIL); break; } @@ -8455,7 +8468,7 @@ SparqlAutomaticParser::dataBlockValue() { exitRule(); }); try { - setState(850); + setState(852); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::IRI_REF: @@ -8463,7 +8476,7 @@ SparqlAutomaticParser::dataBlockValue() { case SparqlAutomaticParser::PNAME_LN: case SparqlAutomaticParser::PREFIX_LANGTAG: { enterOuterAlt(_localctx, 1); - setState(845); + setState(847); iri(); break; } @@ -8473,7 +8486,7 @@ SparqlAutomaticParser::dataBlockValue() { case SparqlAutomaticParser::STRING_LITERAL_LONG1: case SparqlAutomaticParser::STRING_LITERAL_LONG2: { enterOuterAlt(_localctx, 2); - setState(846); + setState(848); rdfLiteral(); break; } @@ -8488,7 +8501,7 @@ SparqlAutomaticParser::dataBlockValue() { case SparqlAutomaticParser::DECIMAL_NEGATIVE: case SparqlAutomaticParser::DOUBLE_NEGATIVE: { enterOuterAlt(_localctx, 3); - setState(847); + setState(849); numericLiteral(); break; } @@ -8496,14 +8509,14 @@ SparqlAutomaticParser::dataBlockValue() { case SparqlAutomaticParser::T__27: case SparqlAutomaticParser::T__28: { enterOuterAlt(_localctx, 4); - setState(848); + setState(850); booleanLiteral(); break; } case SparqlAutomaticParser::UNDEF: { enterOuterAlt(_localctx, 5); - setState(849); + setState(851); match(SparqlAutomaticParser::UNDEF); break; } @@ -8576,9 +8589,9 @@ SparqlAutomaticParser::minusGraphPattern() { }); try { enterOuterAlt(_localctx, 1); - setState(852); + setState(854); match(SparqlAutomaticParser::MINUS); - setState(853); + setState(855); groupGraphPattern(); } catch (RecognitionException& e) { @@ -8664,17 +8677,17 @@ SparqlAutomaticParser::groupOrUnionGraphPattern() { }); try { enterOuterAlt(_localctx, 1); - setState(855); + setState(857); groupGraphPattern(); - setState(860); + setState(862); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::UNION) { - setState(856); + setState(858); match(SparqlAutomaticParser::UNION); - setState(857); + setState(859); groupGraphPattern(); - setState(862); + setState(864); _errHandler->sync(this); _la = _input->LA(1); } @@ -8742,9 +8755,9 @@ SparqlAutomaticParser::FilterRContext* SparqlAutomaticParser::filterR() { }); try { enterOuterAlt(_localctx, 1); - setState(863); + setState(865); match(SparqlAutomaticParser::FILTER); - setState(864); + setState(866); constraint(); } catch (RecognitionException& e) { @@ -8815,12 +8828,12 @@ SparqlAutomaticParser::ConstraintContext* SparqlAutomaticParser::constraint() { exitRule(); }); try { - setState(869); + setState(871); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__1: { enterOuterAlt(_localctx, 1); - setState(866); + setState(868); brackettedExpression(); break; } @@ -8888,7 +8901,7 @@ SparqlAutomaticParser::ConstraintContext* SparqlAutomaticParser::constraint() { case SparqlAutomaticParser::STDEV: case SparqlAutomaticParser::SAMPLE: { enterOuterAlt(_localctx, 2); - setState(867); + setState(869); builtInCall(); break; } @@ -8898,7 +8911,7 @@ SparqlAutomaticParser::ConstraintContext* SparqlAutomaticParser::constraint() { case SparqlAutomaticParser::PNAME_LN: case SparqlAutomaticParser::PREFIX_LANGTAG: { enterOuterAlt(_localctx, 3); - setState(868); + setState(870); functionCall(); break; } @@ -8972,9 +8985,9 @@ SparqlAutomaticParser::functionCall() { }); try { enterOuterAlt(_localctx, 1); - setState(871); + setState(873); iri(); - setState(872); + setState(874); argList(); } catch (RecognitionException& e) { @@ -9049,43 +9062,43 @@ SparqlAutomaticParser::ArgListContext* SparqlAutomaticParser::argList() { exitRule(); }); try { - setState(889); + setState(891); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::NIL: { enterOuterAlt(_localctx, 1); - setState(874); + setState(876); match(SparqlAutomaticParser::NIL); break; } case SparqlAutomaticParser::T__1: { enterOuterAlt(_localctx, 2); - setState(875); - match(SparqlAutomaticParser::T__1); setState(877); + match(SparqlAutomaticParser::T__1); + setState(879); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::DISTINCT) { - setState(876); + setState(878); match(SparqlAutomaticParser::DISTINCT); } - setState(879); + setState(881); expression(); - setState(884); + setState(886); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__7) { - setState(880); + setState(882); match(SparqlAutomaticParser::T__7); - setState(881); + setState(883); expression(); - setState(886); + setState(888); _errHandler->sync(this); _la = _input->LA(1); } - setState(887); + setState(889); match(SparqlAutomaticParser::T__2); break; } @@ -9163,35 +9176,35 @@ SparqlAutomaticParser::expressionList() { exitRule(); }); try { - setState(903); + setState(905); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::NIL: { enterOuterAlt(_localctx, 1); - setState(891); + setState(893); match(SparqlAutomaticParser::NIL); break; } case SparqlAutomaticParser::T__1: { enterOuterAlt(_localctx, 2); - setState(892); + setState(894); match(SparqlAutomaticParser::T__1); - setState(893); + setState(895); expression(); - setState(898); + setState(900); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__7) { - setState(894); + setState(896); match(SparqlAutomaticParser::T__7); - setState(895); + setState(897); expression(); - setState(900); + setState(902); _errHandler->sync(this); _la = _input->LA(1); } - setState(901); + setState(903); match(SparqlAutomaticParser::T__2); break; } @@ -9261,19 +9274,19 @@ SparqlAutomaticParser::constructTemplate() { }); try { enterOuterAlt(_localctx, 1); - setState(905); - match(SparqlAutomaticParser::T__3); setState(907); + match(SparqlAutomaticParser::T__3); + setState(909); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 29228991) != 0) { - setState(906); + setState(908); constructTriples(); } - setState(909); + setState(911); match(SparqlAutomaticParser::T__4); } catch (RecognitionException& e) { @@ -9342,23 +9355,23 @@ SparqlAutomaticParser::constructTriples() { }); try { enterOuterAlt(_localctx, 1); - setState(911); + setState(913); triplesSameSubject(); - setState(916); + setState(918); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__6) { - setState(912); - match(SparqlAutomaticParser::T__6); setState(914); + match(SparqlAutomaticParser::T__6); + setState(916); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 29228991) != 0) { - setState(913); + setState(915); constructTriples(); } } @@ -9437,7 +9450,7 @@ SparqlAutomaticParser::triplesSameSubject() { exitRule(); }); try { - setState(924); + setState(926); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__27: @@ -9465,9 +9478,9 @@ SparqlAutomaticParser::triplesSameSubject() { case SparqlAutomaticParser::NIL: case SparqlAutomaticParser::ANON: { enterOuterAlt(_localctx, 1); - setState(918); + setState(920); varOrTerm(); - setState(919); + setState(921); propertyListNotEmpty(); break; } @@ -9475,9 +9488,9 @@ SparqlAutomaticParser::triplesSameSubject() { case SparqlAutomaticParser::T__1: case SparqlAutomaticParser::T__15: { enterOuterAlt(_localctx, 2); - setState(921); + setState(923); triplesNode(); - setState(922); + setState(924); propertyList(); break; } @@ -9547,13 +9560,13 @@ SparqlAutomaticParser::propertyList() { }); try { enterOuterAlt(_localctx, 1); - setState(927); + setState(929); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__8 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 183) != 0) { - setState(926); + setState(928); propertyListNotEmpty(); } @@ -9635,29 +9648,29 @@ SparqlAutomaticParser::propertyListNotEmpty() { }); try { enterOuterAlt(_localctx, 1); - setState(929); + setState(931); verb(); - setState(930); + setState(932); objectList(); - setState(939); + setState(941); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__5) { - setState(931); + setState(933); match(SparqlAutomaticParser::T__5); - setState(935); + setState(937); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__8 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 183) != 0) { - setState(932); + setState(934); verb(); - setState(933); + setState(935); objectList(); } - setState(941); + setState(943); _errHandler->sync(this); _la = _input->LA(1); } @@ -9720,7 +9733,7 @@ SparqlAutomaticParser::VerbContext* SparqlAutomaticParser::verb() { exitRule(); }); try { - setState(944); + setState(946); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::IRI_REF: @@ -9730,14 +9743,14 @@ SparqlAutomaticParser::VerbContext* SparqlAutomaticParser::verb() { case SparqlAutomaticParser::VAR2: case SparqlAutomaticParser::PREFIX_LANGTAG: { enterOuterAlt(_localctx, 1); - setState(942); + setState(944); varOrIri(); break; } case SparqlAutomaticParser::T__8: { enterOuterAlt(_localctx, 2); - setState(943); + setState(945); match(SparqlAutomaticParser::T__8); break; } @@ -9811,17 +9824,17 @@ SparqlAutomaticParser::ObjectListContext* SparqlAutomaticParser::objectList() { }); try { enterOuterAlt(_localctx, 1); - setState(946); + setState(948); objectR(); - setState(951); + setState(953); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__7) { - setState(947); + setState(949); match(SparqlAutomaticParser::T__7); - setState(948); + setState(950); objectR(); - setState(953); + setState(955); _errHandler->sync(this); _la = _input->LA(1); } @@ -9885,7 +9898,7 @@ SparqlAutomaticParser::ObjectRContext* SparqlAutomaticParser::objectR() { }); try { enterOuterAlt(_localctx, 1); - setState(954); + setState(956); graphNode(); } catch (RecognitionException& e) { @@ -9967,7 +9980,7 @@ SparqlAutomaticParser::triplesSameSubjectPath() { exitRule(); }); try { - setState(962); + setState(964); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__27: @@ -9995,9 +10008,9 @@ SparqlAutomaticParser::triplesSameSubjectPath() { case SparqlAutomaticParser::NIL: case SparqlAutomaticParser::ANON: { enterOuterAlt(_localctx, 1); - setState(956); + setState(958); varOrTerm(); - setState(957); + setState(959); propertyListPathNotEmpty(); break; } @@ -10005,9 +10018,9 @@ SparqlAutomaticParser::triplesSameSubjectPath() { case SparqlAutomaticParser::T__1: case SparqlAutomaticParser::T__15: { enterOuterAlt(_localctx, 2); - setState(959); + setState(961); triplesNodePath(); - setState(960); + setState(962); propertyListPath(); break; } @@ -10078,13 +10091,13 @@ SparqlAutomaticParser::propertyListPath() { }); try { enterOuterAlt(_localctx, 1); - setState(965); + setState(967); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 37380) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 183) != 0) { - setState(964); + setState(966); propertyListPathNotEmpty(); } @@ -10166,25 +10179,25 @@ SparqlAutomaticParser::propertyListPathNotEmpty() { }); try { enterOuterAlt(_localctx, 1); - setState(967); + setState(969); tupleWithPath(); - setState(974); + setState(976); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__5) { - setState(968); - match(SparqlAutomaticParser::T__5); setState(970); + match(SparqlAutomaticParser::T__5); + setState(972); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 37380) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 183) != 0) { - setState(969); + setState(971); tupleWithoutPath(); } - setState(976); + setState(978); _errHandler->sync(this); _la = _input->LA(1); } @@ -10248,7 +10261,7 @@ SparqlAutomaticParser::VerbPathContext* SparqlAutomaticParser::verbPath() { }); try { enterOuterAlt(_localctx, 1); - setState(977); + setState(979); path(); } catch (RecognitionException& e) { @@ -10310,7 +10323,7 @@ SparqlAutomaticParser::VerbSimpleContext* SparqlAutomaticParser::verbSimple() { }); try { enterOuterAlt(_localctx, 1); - setState(979); + setState(981); var(); } catch (RecognitionException& e) { @@ -10378,9 +10391,9 @@ SparqlAutomaticParser::tupleWithoutPath() { }); try { enterOuterAlt(_localctx, 1); - setState(981); + setState(983); verbPathOrSimple(); - setState(982); + setState(984); objectList(); } catch (RecognitionException& e) { @@ -10448,9 +10461,9 @@ SparqlAutomaticParser::tupleWithPath() { }); try { enterOuterAlt(_localctx, 1); - setState(984); + setState(986); verbPathOrSimple(); - setState(985); + setState(987); objectListPath(); } catch (RecognitionException& e) { @@ -10518,7 +10531,7 @@ SparqlAutomaticParser::verbPathOrSimple() { }); try { enterOuterAlt(_localctx, 1); - setState(989); + setState(991); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__1: @@ -10529,14 +10542,14 @@ SparqlAutomaticParser::verbPathOrSimple() { case SparqlAutomaticParser::PNAME_NS: case SparqlAutomaticParser::PNAME_LN: case SparqlAutomaticParser::PREFIX_LANGTAG: { - setState(987); + setState(989); verbPath(); break; } case SparqlAutomaticParser::VAR1: case SparqlAutomaticParser::VAR2: { - setState(988); + setState(990); verbSimple(); break; } @@ -10611,17 +10624,17 @@ SparqlAutomaticParser::objectListPath() { }); try { enterOuterAlt(_localctx, 1); - setState(991); + setState(993); objectPath(); - setState(996); + setState(998); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__7) { - setState(992); + setState(994); match(SparqlAutomaticParser::T__7); - setState(993); + setState(995); objectPath(); - setState(998); + setState(1000); _errHandler->sync(this); _la = _input->LA(1); } @@ -10685,7 +10698,7 @@ SparqlAutomaticParser::ObjectPathContext* SparqlAutomaticParser::objectPath() { }); try { enterOuterAlt(_localctx, 1); - setState(999); + setState(1001); graphNodePath(); } catch (RecognitionException& e) { @@ -10747,7 +10760,7 @@ SparqlAutomaticParser::PathContext* SparqlAutomaticParser::path() { }); try { enterOuterAlt(_localctx, 1); - setState(1001); + setState(1003); pathAlternative(); } catch (RecognitionException& e) { @@ -10816,17 +10829,17 @@ SparqlAutomaticParser::pathAlternative() { }); try { enterOuterAlt(_localctx, 1); - setState(1003); + setState(1005); pathSequence(); - setState(1008); + setState(1010); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__9) { - setState(1004); + setState(1006); match(SparqlAutomaticParser::T__9); - setState(1005); + setState(1007); pathSequence(); - setState(1010); + setState(1012); _errHandler->sync(this); _la = _input->LA(1); } @@ -10897,17 +10910,17 @@ SparqlAutomaticParser::pathSequence() { }); try { enterOuterAlt(_localctx, 1); - setState(1011); + setState(1013); pathEltOrInverse(); - setState(1016); + setState(1018); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__10) { - setState(1012); + setState(1014); match(SparqlAutomaticParser::T__10); - setState(1013); + setState(1015); pathEltOrInverse(); - setState(1018); + setState(1020); _errHandler->sync(this); _la = _input->LA(1); } @@ -10977,14 +10990,14 @@ SparqlAutomaticParser::PathEltContext* SparqlAutomaticParser::pathElt() { }); try { enterOuterAlt(_localctx, 1); - setState(1019); - pathPrimary(); setState(1021); + pathPrimary(); + setState(1023); _errHandler->sync(this); _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 24578) != 0) { - setState(1020); + setState(1022); pathMod(); } @@ -11047,7 +11060,7 @@ SparqlAutomaticParser::pathEltOrInverse() { exitRule(); }); try { - setState(1026); + setState(1028); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__1: @@ -11058,17 +11071,17 @@ SparqlAutomaticParser::pathEltOrInverse() { case SparqlAutomaticParser::PNAME_LN: case SparqlAutomaticParser::PREFIX_LANGTAG: { enterOuterAlt(_localctx, 1); - setState(1023); + setState(1025); pathElt(); break; } case SparqlAutomaticParser::T__11: { enterOuterAlt(_localctx, 2); - setState(1024); + setState(1026); antlrcpp::downCast(_localctx) ->negationOperator = match(SparqlAutomaticParser::T__11); - setState(1025); + setState(1027); pathElt(); break; } @@ -11132,7 +11145,7 @@ SparqlAutomaticParser::PathModContext* SparqlAutomaticParser::pathMod() { }); try { enterOuterAlt(_localctx, 1); - setState(1028); + setState(1030); _la = _input->LA(1); if (!(((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 24578) != 0)) { _errHandler->recoverInline(this); @@ -11211,7 +11224,7 @@ SparqlAutomaticParser::pathPrimary() { exitRule(); }); try { - setState(1038); + setState(1040); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::IRI_REF: @@ -11219,34 +11232,34 @@ SparqlAutomaticParser::pathPrimary() { case SparqlAutomaticParser::PNAME_LN: case SparqlAutomaticParser::PREFIX_LANGTAG: { enterOuterAlt(_localctx, 1); - setState(1030); + setState(1032); iri(); break; } case SparqlAutomaticParser::T__8: { enterOuterAlt(_localctx, 2); - setState(1031); + setState(1033); match(SparqlAutomaticParser::T__8); break; } case SparqlAutomaticParser::T__14: { enterOuterAlt(_localctx, 3); - setState(1032); + setState(1034); match(SparqlAutomaticParser::T__14); - setState(1033); + setState(1035); pathNegatedPropertySet(); break; } case SparqlAutomaticParser::T__1: { enterOuterAlt(_localctx, 4); - setState(1034); + setState(1036); match(SparqlAutomaticParser::T__1); - setState(1035); + setState(1037); path(); - setState(1036); + setState(1038); match(SparqlAutomaticParser::T__2); break; } @@ -11325,7 +11338,7 @@ SparqlAutomaticParser::pathNegatedPropertySet() { exitRule(); }); try { - setState(1053); + setState(1055); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__8: @@ -11335,16 +11348,16 @@ SparqlAutomaticParser::pathNegatedPropertySet() { case SparqlAutomaticParser::PNAME_LN: case SparqlAutomaticParser::PREFIX_LANGTAG: { enterOuterAlt(_localctx, 1); - setState(1040); + setState(1042); pathOneInPropertySet(); break; } case SparqlAutomaticParser::T__1: { enterOuterAlt(_localctx, 2); - setState(1041); + setState(1043); match(SparqlAutomaticParser::T__1); - setState(1050); + setState(1052); _errHandler->sync(this); _la = _input->LA(1); @@ -11353,22 +11366,22 @@ SparqlAutomaticParser::pathNegatedPropertySet() { || _la == SparqlAutomaticParser::T__11 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 135) != 0) { - setState(1042); + setState(1044); pathOneInPropertySet(); - setState(1047); + setState(1049); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__9) { - setState(1043); + setState(1045); match(SparqlAutomaticParser::T__9); - setState(1044); + setState(1046); pathOneInPropertySet(); - setState(1049); + setState(1051); _errHandler->sync(this); _la = _input->LA(1); } } - setState(1052); + setState(1054); match(SparqlAutomaticParser::T__2); break; } @@ -11438,7 +11451,7 @@ SparqlAutomaticParser::pathOneInPropertySet() { exitRule(); }); try { - setState(1062); + setState(1064); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::IRI_REF: @@ -11446,36 +11459,36 @@ SparqlAutomaticParser::pathOneInPropertySet() { case SparqlAutomaticParser::PNAME_LN: case SparqlAutomaticParser::PREFIX_LANGTAG: { enterOuterAlt(_localctx, 1); - setState(1055); + setState(1057); iri(); break; } case SparqlAutomaticParser::T__8: { enterOuterAlt(_localctx, 2); - setState(1056); + setState(1058); match(SparqlAutomaticParser::T__8); break; } case SparqlAutomaticParser::T__11: { enterOuterAlt(_localctx, 3); - setState(1057); + setState(1059); match(SparqlAutomaticParser::T__11); - setState(1060); + setState(1062); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::IRI_REF: case SparqlAutomaticParser::PNAME_NS: case SparqlAutomaticParser::PNAME_LN: case SparqlAutomaticParser::PREFIX_LANGTAG: { - setState(1058); + setState(1060); iri(); break; } case SparqlAutomaticParser::T__8: { - setState(1059); + setState(1061); match(SparqlAutomaticParser::T__8); break; } @@ -11548,7 +11561,7 @@ SparqlAutomaticParser::IntegerContext* SparqlAutomaticParser::integer() { }); try { enterOuterAlt(_localctx, 1); - setState(1064); + setState(1066); match(SparqlAutomaticParser::INTEGER); } catch (RecognitionException& e) { @@ -11615,19 +11628,19 @@ SparqlAutomaticParser::triplesNode() { exitRule(); }); try { - setState(1068); + setState(1070); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__1: { enterOuterAlt(_localctx, 1); - setState(1066); + setState(1068); collection(); break; } case SparqlAutomaticParser::T__15: { enterOuterAlt(_localctx, 2); - setState(1067); + setState(1069); blankNodePropertyList(); break; } @@ -11700,11 +11713,11 @@ SparqlAutomaticParser::blankNodePropertyList() { }); try { enterOuterAlt(_localctx, 1); - setState(1070); + setState(1072); match(SparqlAutomaticParser::T__15); - setState(1071); + setState(1073); propertyListNotEmpty(); - setState(1072); + setState(1074); match(SparqlAutomaticParser::T__16); } catch (RecognitionException& e) { @@ -11772,19 +11785,19 @@ SparqlAutomaticParser::triplesNodePath() { exitRule(); }); try { - setState(1076); + setState(1078); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__1: { enterOuterAlt(_localctx, 1); - setState(1074); + setState(1076); collectionPath(); break; } case SparqlAutomaticParser::T__15: { enterOuterAlt(_localctx, 2); - setState(1075); + setState(1077); blankNodePropertyListPath(); break; } @@ -11860,11 +11873,11 @@ SparqlAutomaticParser::blankNodePropertyListPath() { }); try { enterOuterAlt(_localctx, 1); - setState(1078); + setState(1080); match(SparqlAutomaticParser::T__15); - setState(1079); + setState(1081); propertyListPathNotEmpty(); - setState(1080); + setState(1082); match(SparqlAutomaticParser::T__16); } catch (RecognitionException& e) { @@ -11932,21 +11945,21 @@ SparqlAutomaticParser::CollectionContext* SparqlAutomaticParser::collection() { }); try { enterOuterAlt(_localctx, 1); - setState(1082); - match(SparqlAutomaticParser::T__1); setState(1084); + match(SparqlAutomaticParser::T__1); + setState(1086); _errHandler->sync(this); _la = _input->LA(1); do { - setState(1083); + setState(1085); graphNode(); - setState(1086); + setState(1088); _errHandler->sync(this); _la = _input->LA(1); } while (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 29228991) != 0); - setState(1088); + setState(1090); match(SparqlAutomaticParser::T__2); } catch (RecognitionException& e) { @@ -12015,21 +12028,21 @@ SparqlAutomaticParser::collectionPath() { }); try { enterOuterAlt(_localctx, 1); - setState(1090); - match(SparqlAutomaticParser::T__1); setState(1092); + match(SparqlAutomaticParser::T__1); + setState(1094); _errHandler->sync(this); _la = _input->LA(1); do { - setState(1091); + setState(1093); graphNodePath(); - setState(1094); + setState(1096); _errHandler->sync(this); _la = _input->LA(1); } while (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 29228991) != 0); - setState(1096); + setState(1098); match(SparqlAutomaticParser::T__2); } catch (RecognitionException& e) { @@ -12095,7 +12108,7 @@ SparqlAutomaticParser::GraphNodeContext* SparqlAutomaticParser::graphNode() { exitRule(); }); try { - setState(1100); + setState(1102); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__27: @@ -12123,7 +12136,7 @@ SparqlAutomaticParser::GraphNodeContext* SparqlAutomaticParser::graphNode() { case SparqlAutomaticParser::NIL: case SparqlAutomaticParser::ANON: { enterOuterAlt(_localctx, 1); - setState(1098); + setState(1100); varOrTerm(); break; } @@ -12131,7 +12144,7 @@ SparqlAutomaticParser::GraphNodeContext* SparqlAutomaticParser::graphNode() { case SparqlAutomaticParser::T__1: case SparqlAutomaticParser::T__15: { enterOuterAlt(_localctx, 2); - setState(1099); + setState(1101); triplesNode(); break; } @@ -12204,7 +12217,7 @@ SparqlAutomaticParser::graphNodePath() { exitRule(); }); try { - setState(1104); + setState(1106); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__27: @@ -12232,7 +12245,7 @@ SparqlAutomaticParser::graphNodePath() { case SparqlAutomaticParser::NIL: case SparqlAutomaticParser::ANON: { enterOuterAlt(_localctx, 1); - setState(1102); + setState(1104); varOrTerm(); break; } @@ -12240,7 +12253,7 @@ SparqlAutomaticParser::graphNodePath() { case SparqlAutomaticParser::T__1: case SparqlAutomaticParser::T__15: { enterOuterAlt(_localctx, 2); - setState(1103); + setState(1105); triplesNodePath(); break; } @@ -12312,13 +12325,13 @@ SparqlAutomaticParser::VarOrTermContext* SparqlAutomaticParser::varOrTerm() { exitRule(); }); try { - setState(1108); + setState(1110); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::VAR1: case SparqlAutomaticParser::VAR2: { enterOuterAlt(_localctx, 1); - setState(1106); + setState(1108); var(); break; } @@ -12346,7 +12359,7 @@ SparqlAutomaticParser::VarOrTermContext* SparqlAutomaticParser::varOrTerm() { case SparqlAutomaticParser::NIL: case SparqlAutomaticParser::ANON: { enterOuterAlt(_localctx, 2); - setState(1107); + setState(1109); graphTerm(); break; } @@ -12418,13 +12431,13 @@ SparqlAutomaticParser::VarOrIriContext* SparqlAutomaticParser::varOrIri() { exitRule(); }); try { - setState(1112); + setState(1114); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::VAR1: case SparqlAutomaticParser::VAR2: { enterOuterAlt(_localctx, 1); - setState(1110); + setState(1112); var(); break; } @@ -12434,7 +12447,7 @@ SparqlAutomaticParser::VarOrIriContext* SparqlAutomaticParser::varOrIri() { case SparqlAutomaticParser::PNAME_LN: case SparqlAutomaticParser::PREFIX_LANGTAG: { enterOuterAlt(_localctx, 2); - setState(1111); + setState(1113); iri(); break; } @@ -12505,7 +12518,7 @@ SparqlAutomaticParser::VarContext* SparqlAutomaticParser::var() { }); try { enterOuterAlt(_localctx, 1); - setState(1114); + setState(1116); _la = _input->LA(1); if (!(_la == SparqlAutomaticParser::VAR1 @@ -12598,7 +12611,7 @@ SparqlAutomaticParser::GraphTermContext* SparqlAutomaticParser::graphTerm() { exitRule(); }); try { - setState(1122); + setState(1124); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::IRI_REF: @@ -12606,7 +12619,7 @@ SparqlAutomaticParser::GraphTermContext* SparqlAutomaticParser::graphTerm() { case SparqlAutomaticParser::PNAME_LN: case SparqlAutomaticParser::PREFIX_LANGTAG: { enterOuterAlt(_localctx, 1); - setState(1116); + setState(1118); iri(); break; } @@ -12616,7 +12629,7 @@ SparqlAutomaticParser::GraphTermContext* SparqlAutomaticParser::graphTerm() { case SparqlAutomaticParser::STRING_LITERAL_LONG1: case SparqlAutomaticParser::STRING_LITERAL_LONG2: { enterOuterAlt(_localctx, 2); - setState(1117); + setState(1119); rdfLiteral(); break; } @@ -12631,7 +12644,7 @@ SparqlAutomaticParser::GraphTermContext* SparqlAutomaticParser::graphTerm() { case SparqlAutomaticParser::DECIMAL_NEGATIVE: case SparqlAutomaticParser::DOUBLE_NEGATIVE: { enterOuterAlt(_localctx, 3); - setState(1118); + setState(1120); numericLiteral(); break; } @@ -12639,7 +12652,7 @@ SparqlAutomaticParser::GraphTermContext* SparqlAutomaticParser::graphTerm() { case SparqlAutomaticParser::T__27: case SparqlAutomaticParser::T__28: { enterOuterAlt(_localctx, 4); - setState(1119); + setState(1121); booleanLiteral(); break; } @@ -12647,14 +12660,14 @@ SparqlAutomaticParser::GraphTermContext* SparqlAutomaticParser::graphTerm() { case SparqlAutomaticParser::BLANK_NODE_LABEL: case SparqlAutomaticParser::ANON: { enterOuterAlt(_localctx, 5); - setState(1120); + setState(1122); blankNode(); break; } case SparqlAutomaticParser::NIL: { enterOuterAlt(_localctx, 6); - setState(1121); + setState(1123); match(SparqlAutomaticParser::NIL); break; } @@ -12723,7 +12736,7 @@ SparqlAutomaticParser::ExpressionContext* SparqlAutomaticParser::expression() { }); try { enterOuterAlt(_localctx, 1); - setState(1124); + setState(1126); conditionalOrExpression(); } catch (RecognitionException& e) { @@ -12800,17 +12813,17 @@ SparqlAutomaticParser::conditionalOrExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1126); + setState(1128); conditionalAndExpression(); - setState(1131); + setState(1133); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__17) { - setState(1127); + setState(1129); match(SparqlAutomaticParser::T__17); - setState(1128); + setState(1130); conditionalAndExpression(); - setState(1133); + setState(1135); _errHandler->sync(this); _la = _input->LA(1); } @@ -12887,17 +12900,17 @@ SparqlAutomaticParser::conditionalAndExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1134); + setState(1136); valueLogical(); - setState(1139); + setState(1141); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__18) { - setState(1135); + setState(1137); match(SparqlAutomaticParser::T__18); - setState(1136); + setState(1138); valueLogical(); - setState(1141); + setState(1143); _errHandler->sync(this); _la = _input->LA(1); } @@ -12962,7 +12975,7 @@ SparqlAutomaticParser::valueLogical() { }); try { enterOuterAlt(_localctx, 1); - setState(1142); + setState(1144); relationalExpression(); } catch (RecognitionException& e) { @@ -13046,74 +13059,74 @@ SparqlAutomaticParser::relationalExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1144); + setState(1146); numericExpression(); - setState(1162); + setState(1164); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__19: { - setState(1145); + setState(1147); match(SparqlAutomaticParser::T__19); - setState(1146); + setState(1148); numericExpression(); break; } case SparqlAutomaticParser::T__20: { - setState(1147); + setState(1149); match(SparqlAutomaticParser::T__20); - setState(1148); + setState(1150); numericExpression(); break; } case SparqlAutomaticParser::T__21: { - setState(1149); + setState(1151); match(SparqlAutomaticParser::T__21); - setState(1150); + setState(1152); numericExpression(); break; } case SparqlAutomaticParser::T__22: { - setState(1151); + setState(1153); match(SparqlAutomaticParser::T__22); - setState(1152); + setState(1154); numericExpression(); break; } case SparqlAutomaticParser::T__23: { - setState(1153); + setState(1155); match(SparqlAutomaticParser::T__23); - setState(1154); + setState(1156); numericExpression(); break; } case SparqlAutomaticParser::T__24: { - setState(1155); + setState(1157); match(SparqlAutomaticParser::T__24); - setState(1156); + setState(1158); numericExpression(); break; } case SparqlAutomaticParser::IN: { - setState(1157); + setState(1159); match(SparqlAutomaticParser::IN); - setState(1158); + setState(1160); expressionList(); break; } case SparqlAutomaticParser::NOT: { - setState(1159); + setState(1161); antlrcpp::downCast(_localctx)->notToken = match(SparqlAutomaticParser::NOT); - setState(1160); + setState(1162); match(SparqlAutomaticParser::IN); - setState(1161); + setState(1163); expressionList(); break; } @@ -13191,7 +13204,7 @@ SparqlAutomaticParser::numericExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1164); + setState(1166); additiveExpression(); } catch (RecognitionException& e) { @@ -13270,9 +13283,9 @@ SparqlAutomaticParser::additiveExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1166); + setState(1168); multiplicativeExpression(); - setState(1170); + setState(1172); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__12 @@ -13280,9 +13293,9 @@ SparqlAutomaticParser::additiveExpression() { || _la == SparqlAutomaticParser::T__25 || (((_la - 153) & ~0x3fULL) == 0) && ((1ULL << (_la - 153)) & 63) != 0) { - setState(1167); + setState(1169); multiplicativeExpressionWithSign(); - setState(1172); + setState(1174); _errHandler->sync(this); _la = _input->LA(1); } @@ -13366,23 +13379,23 @@ SparqlAutomaticParser::multiplicativeExpressionWithSign() { exitRule(); }); try { - setState(1178); + setState(1180); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__12: { enterOuterAlt(_localctx, 1); - setState(1173); + setState(1175); match(SparqlAutomaticParser::T__12); - setState(1174); + setState(1176); plusSubexpression(); break; } case SparqlAutomaticParser::T__25: { enterOuterAlt(_localctx, 2); - setState(1175); + setState(1177); match(SparqlAutomaticParser::T__25); - setState(1176); + setState(1178); minusSubexpression(); break; } @@ -13394,7 +13407,7 @@ SparqlAutomaticParser::multiplicativeExpressionWithSign() { case SparqlAutomaticParser::DECIMAL_NEGATIVE: case SparqlAutomaticParser::DOUBLE_NEGATIVE: { enterOuterAlt(_localctx, 3); - setState(1177); + setState(1179); multiplicativeExpressionWithLeadingSignButNoSpace(); break; } @@ -13464,7 +13477,7 @@ SparqlAutomaticParser::plusSubexpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1180); + setState(1182); multiplicativeExpression(); } catch (RecognitionException& e) { @@ -13528,7 +13541,7 @@ SparqlAutomaticParser::minusSubexpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1182); + setState(1184); multiplicativeExpression(); } catch (RecognitionException& e) { @@ -13633,13 +13646,13 @@ SparqlAutomaticParser::multiplicativeExpressionWithLeadingSignButNoSpace() { }); try { enterOuterAlt(_localctx, 1); - setState(1186); + setState(1188); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::INTEGER_POSITIVE: case SparqlAutomaticParser::DECIMAL_POSITIVE: case SparqlAutomaticParser::DOUBLE_POSITIVE: { - setState(1184); + setState(1186); numericLiteralPositive(); break; } @@ -13647,7 +13660,7 @@ SparqlAutomaticParser::multiplicativeExpressionWithLeadingSignButNoSpace() { case SparqlAutomaticParser::INTEGER_NEGATIVE: case SparqlAutomaticParser::DECIMAL_NEGATIVE: case SparqlAutomaticParser::DOUBLE_NEGATIVE: { - setState(1185); + setState(1187); numericLiteralNegative(); break; } @@ -13655,15 +13668,15 @@ SparqlAutomaticParser::multiplicativeExpressionWithLeadingSignButNoSpace() { default: throw NoViableAltException(this); } - setState(1191); + setState(1193); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__0 || _la == SparqlAutomaticParser::T__10) { - setState(1188); + setState(1190); multiplyOrDivideExpression(); - setState(1193); + setState(1195); _errHandler->sync(this); _la = _input->LA(1); } @@ -13749,17 +13762,17 @@ SparqlAutomaticParser::multiplicativeExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1194); + setState(1196); unaryExpression(); - setState(1198); + setState(1200); _errHandler->sync(this); _la = _input->LA(1); while (_la == SparqlAutomaticParser::T__0 || _la == SparqlAutomaticParser::T__10) { - setState(1195); + setState(1197); multiplyOrDivideExpression(); - setState(1200); + setState(1202); _errHandler->sync(this); _la = _input->LA(1); } @@ -13834,19 +13847,19 @@ SparqlAutomaticParser::multiplyOrDivideExpression() { exitRule(); }); try { - setState(1203); + setState(1205); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__0: { enterOuterAlt(_localctx, 1); - setState(1201); + setState(1203); multiplyExpression(); break; } case SparqlAutomaticParser::T__10: { enterOuterAlt(_localctx, 2); - setState(1202); + setState(1204); divideExpression(); break; } @@ -13915,9 +13928,9 @@ SparqlAutomaticParser::multiplyExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1205); + setState(1207); match(SparqlAutomaticParser::T__0); - setState(1206); + setState(1208); unaryExpression(); } catch (RecognitionException& e) { @@ -13980,9 +13993,9 @@ SparqlAutomaticParser::divideExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1208); + setState(1210); match(SparqlAutomaticParser::T__10); - setState(1209); + setState(1211); unaryExpression(); } catch (RecognitionException& e) { @@ -14044,32 +14057,32 @@ SparqlAutomaticParser::unaryExpression() { exitRule(); }); try { - setState(1218); + setState(1220); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__14: { enterOuterAlt(_localctx, 1); - setState(1211); + setState(1213); match(SparqlAutomaticParser::T__14); - setState(1212); + setState(1214); primaryExpression(); break; } case SparqlAutomaticParser::T__12: { enterOuterAlt(_localctx, 2); - setState(1213); + setState(1215); match(SparqlAutomaticParser::T__12); - setState(1214); + setState(1216); primaryExpression(); break; } case SparqlAutomaticParser::T__25: { enterOuterAlt(_localctx, 3); - setState(1215); + setState(1217); match(SparqlAutomaticParser::T__25); - setState(1216); + setState(1218); primaryExpression(); break; } @@ -14159,7 +14172,7 @@ SparqlAutomaticParser::unaryExpression() { case SparqlAutomaticParser::STRING_LITERAL_LONG1: case SparqlAutomaticParser::STRING_LITERAL_LONG2: { enterOuterAlt(_localctx, 4); - setState(1217); + setState(1219); primaryExpression(); break; } @@ -14257,12 +14270,12 @@ SparqlAutomaticParser::primaryExpression() { exitRule(); }); try { - setState(1227); + setState(1229); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__1: { enterOuterAlt(_localctx, 1); - setState(1220); + setState(1222); brackettedExpression(); break; } @@ -14330,7 +14343,7 @@ SparqlAutomaticParser::primaryExpression() { case SparqlAutomaticParser::STDEV: case SparqlAutomaticParser::SAMPLE: { enterOuterAlt(_localctx, 2); - setState(1221); + setState(1223); builtInCall(); break; } @@ -14340,7 +14353,7 @@ SparqlAutomaticParser::primaryExpression() { case SparqlAutomaticParser::PNAME_LN: case SparqlAutomaticParser::PREFIX_LANGTAG: { enterOuterAlt(_localctx, 3); - setState(1222); + setState(1224); iriOrFunction(); break; } @@ -14350,7 +14363,7 @@ SparqlAutomaticParser::primaryExpression() { case SparqlAutomaticParser::STRING_LITERAL_LONG1: case SparqlAutomaticParser::STRING_LITERAL_LONG2: { enterOuterAlt(_localctx, 4); - setState(1223); + setState(1225); rdfLiteral(); break; } @@ -14365,7 +14378,7 @@ SparqlAutomaticParser::primaryExpression() { case SparqlAutomaticParser::DECIMAL_NEGATIVE: case SparqlAutomaticParser::DOUBLE_NEGATIVE: { enterOuterAlt(_localctx, 5); - setState(1224); + setState(1226); numericLiteral(); break; } @@ -14373,7 +14386,7 @@ SparqlAutomaticParser::primaryExpression() { case SparqlAutomaticParser::T__27: case SparqlAutomaticParser::T__28: { enterOuterAlt(_localctx, 6); - setState(1225); + setState(1227); booleanLiteral(); break; } @@ -14381,7 +14394,7 @@ SparqlAutomaticParser::primaryExpression() { case SparqlAutomaticParser::VAR1: case SparqlAutomaticParser::VAR2: { enterOuterAlt(_localctx, 7); - setState(1226); + setState(1228); var(); break; } @@ -14452,11 +14465,11 @@ SparqlAutomaticParser::brackettedExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1229); + setState(1231); match(SparqlAutomaticParser::T__1); - setState(1230); + setState(1232); expression(); - setState(1231); + setState(1233); match(SparqlAutomaticParser::T__2); } catch (RecognitionException& e) { @@ -14765,7 +14778,7 @@ SparqlAutomaticParser::builtInCall() { exitRule(); }); try { - setState(1487); + setState(1489); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::GROUP_CONCAT: @@ -14777,119 +14790,119 @@ SparqlAutomaticParser::builtInCall() { case SparqlAutomaticParser::STDEV: case SparqlAutomaticParser::SAMPLE: { enterOuterAlt(_localctx, 1); - setState(1233); + setState(1235); aggregate(); break; } case SparqlAutomaticParser::STR: { enterOuterAlt(_localctx, 2); - setState(1234); + setState(1236); match(SparqlAutomaticParser::STR); - setState(1235); + setState(1237); match(SparqlAutomaticParser::T__1); - setState(1236); + setState(1238); expression(); - setState(1237); + setState(1239); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::LANG: { enterOuterAlt(_localctx, 3); - setState(1239); + setState(1241); langExpression(); break; } case SparqlAutomaticParser::LANGMATCHES: { enterOuterAlt(_localctx, 4); - setState(1240); - match(SparqlAutomaticParser::LANGMATCHES); - setState(1241); - match(SparqlAutomaticParser::T__1); setState(1242); - expression(); + match(SparqlAutomaticParser::LANGMATCHES); setState(1243); - match(SparqlAutomaticParser::T__7); + match(SparqlAutomaticParser::T__1); setState(1244); expression(); setState(1245); + match(SparqlAutomaticParser::T__7); + setState(1246); + expression(); + setState(1247); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::DATATYPE: { enterOuterAlt(_localctx, 5); - setState(1247); + setState(1249); match(SparqlAutomaticParser::DATATYPE); - setState(1248); + setState(1250); match(SparqlAutomaticParser::T__1); - setState(1249); + setState(1251); expression(); - setState(1250); + setState(1252); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::BOUND: { enterOuterAlt(_localctx, 6); - setState(1252); + setState(1254); match(SparqlAutomaticParser::BOUND); - setState(1253); + setState(1255); match(SparqlAutomaticParser::T__1); - setState(1254); + setState(1256); var(); - setState(1255); + setState(1257); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::IRI: { enterOuterAlt(_localctx, 7); - setState(1257); + setState(1259); match(SparqlAutomaticParser::IRI); - setState(1258); + setState(1260); match(SparqlAutomaticParser::T__1); - setState(1259); + setState(1261); expression(); - setState(1260); + setState(1262); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::URI: { enterOuterAlt(_localctx, 8); - setState(1262); + setState(1264); match(SparqlAutomaticParser::URI); - setState(1263); + setState(1265); match(SparqlAutomaticParser::T__1); - setState(1264); + setState(1266); expression(); - setState(1265); + setState(1267); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::BNODE: { enterOuterAlt(_localctx, 9); - setState(1267); + setState(1269); match(SparqlAutomaticParser::BNODE); - setState(1273); + setState(1275); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__1: { - setState(1268); + setState(1270); match(SparqlAutomaticParser::T__1); - setState(1269); + setState(1271); expression(); - setState(1270); + setState(1272); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::NIL: { - setState(1272); + setState(1274); match(SparqlAutomaticParser::NIL); break; } @@ -14902,440 +14915,436 @@ SparqlAutomaticParser::builtInCall() { case SparqlAutomaticParser::RAND: { enterOuterAlt(_localctx, 10); - setState(1275); + setState(1277); match(SparqlAutomaticParser::RAND); - setState(1276); + setState(1278); match(SparqlAutomaticParser::NIL); break; } case SparqlAutomaticParser::ABS: { enterOuterAlt(_localctx, 11); - setState(1277); + setState(1279); match(SparqlAutomaticParser::ABS); - setState(1278); + setState(1280); match(SparqlAutomaticParser::T__1); - setState(1279); + setState(1281); expression(); - setState(1280); + setState(1282); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::CEIL: { enterOuterAlt(_localctx, 12); - setState(1282); + setState(1284); match(SparqlAutomaticParser::CEIL); - setState(1283); + setState(1285); match(SparqlAutomaticParser::T__1); - setState(1284); + setState(1286); expression(); - setState(1285); + setState(1287); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::FLOOR: { enterOuterAlt(_localctx, 13); - setState(1287); + setState(1289); match(SparqlAutomaticParser::FLOOR); - setState(1288); + setState(1290); match(SparqlAutomaticParser::T__1); - setState(1289); + setState(1291); expression(); - setState(1290); + setState(1292); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::ROUND: { enterOuterAlt(_localctx, 14); - setState(1292); + setState(1294); match(SparqlAutomaticParser::ROUND); - setState(1293); + setState(1295); match(SparqlAutomaticParser::T__1); - setState(1294); + setState(1296); expression(); - setState(1295); + setState(1297); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::CONCAT: { enterOuterAlt(_localctx, 15); - setState(1297); + setState(1299); match(SparqlAutomaticParser::CONCAT); - setState(1298); + setState(1300); expressionList(); break; } case SparqlAutomaticParser::SUBSTR: { enterOuterAlt(_localctx, 16); - setState(1299); + setState(1301); substringExpression(); break; } case SparqlAutomaticParser::STRLEN: { enterOuterAlt(_localctx, 17); - setState(1300); + setState(1302); match(SparqlAutomaticParser::STRLEN); - setState(1301); + setState(1303); match(SparqlAutomaticParser::T__1); - setState(1302); + setState(1304); expression(); - setState(1303); + setState(1305); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::REPLACE: { enterOuterAlt(_localctx, 18); - setState(1305); + setState(1307); strReplaceExpression(); break; } case SparqlAutomaticParser::UCASE: { enterOuterAlt(_localctx, 19); - setState(1306); + setState(1308); match(SparqlAutomaticParser::UCASE); - setState(1307); + setState(1309); match(SparqlAutomaticParser::T__1); - setState(1308); + setState(1310); expression(); - setState(1309); + setState(1311); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::LCASE: { enterOuterAlt(_localctx, 20); - setState(1311); + setState(1313); match(SparqlAutomaticParser::LCASE); - setState(1312); + setState(1314); match(SparqlAutomaticParser::T__1); - setState(1313); + setState(1315); expression(); - setState(1314); + setState(1316); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::ENCODE_FOR_URI: { enterOuterAlt(_localctx, 21); - setState(1316); + setState(1318); match(SparqlAutomaticParser::ENCODE_FOR_URI); - setState(1317); + setState(1319); match(SparqlAutomaticParser::T__1); - setState(1318); + setState(1320); expression(); - setState(1319); + setState(1321); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::CONTAINS: { enterOuterAlt(_localctx, 22); - setState(1321); - match(SparqlAutomaticParser::CONTAINS); - setState(1322); - match(SparqlAutomaticParser::T__1); setState(1323); - expression(); + match(SparqlAutomaticParser::CONTAINS); setState(1324); - match(SparqlAutomaticParser::T__7); + match(SparqlAutomaticParser::T__1); setState(1325); expression(); setState(1326); + match(SparqlAutomaticParser::T__7); + setState(1327); + expression(); + setState(1328); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::STRSTARTS: { enterOuterAlt(_localctx, 23); - setState(1328); - match(SparqlAutomaticParser::STRSTARTS); - setState(1329); - match(SparqlAutomaticParser::T__1); setState(1330); - expression(); + match(SparqlAutomaticParser::STRSTARTS); setState(1331); - match(SparqlAutomaticParser::T__7); + match(SparqlAutomaticParser::T__1); setState(1332); expression(); setState(1333); + match(SparqlAutomaticParser::T__7); + setState(1334); + expression(); + setState(1335); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::STRENDS: { enterOuterAlt(_localctx, 24); - setState(1335); - match(SparqlAutomaticParser::STRENDS); - setState(1336); - match(SparqlAutomaticParser::T__1); setState(1337); - expression(); + match(SparqlAutomaticParser::STRENDS); setState(1338); - match(SparqlAutomaticParser::T__7); + match(SparqlAutomaticParser::T__1); setState(1339); expression(); setState(1340); + match(SparqlAutomaticParser::T__7); + setState(1341); + expression(); + setState(1342); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::STRBEFORE: { enterOuterAlt(_localctx, 25); - setState(1342); - match(SparqlAutomaticParser::STRBEFORE); - setState(1343); - match(SparqlAutomaticParser::T__1); setState(1344); - expression(); + match(SparqlAutomaticParser::STRBEFORE); setState(1345); - match(SparqlAutomaticParser::T__7); + match(SparqlAutomaticParser::T__1); setState(1346); expression(); setState(1347); + match(SparqlAutomaticParser::T__7); + setState(1348); + expression(); + setState(1349); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::STRAFTER: { enterOuterAlt(_localctx, 26); - setState(1349); - match(SparqlAutomaticParser::STRAFTER); - setState(1350); - match(SparqlAutomaticParser::T__1); setState(1351); - expression(); + match(SparqlAutomaticParser::STRAFTER); setState(1352); - match(SparqlAutomaticParser::T__7); + match(SparqlAutomaticParser::T__1); setState(1353); expression(); setState(1354); - match(SparqlAutomaticParser::T__2); + match(SparqlAutomaticParser::T__7); + setState(1355); + expression(); + setState(1356); + match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::YEAR: { enterOuterAlt(_localctx, 27); - setState(1356); + setState(1358); match(SparqlAutomaticParser::YEAR); - setState(1357); + setState(1359); match(SparqlAutomaticParser::T__1); - setState(1358); + setState(1360); expression(); - setState(1359); + setState(1361); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::MONTH: { enterOuterAlt(_localctx, 28); - setState(1361); + setState(1363); match(SparqlAutomaticParser::MONTH); - setState(1362); + setState(1364); match(SparqlAutomaticParser::T__1); - setState(1363); + setState(1365); expression(); - setState(1364); + setState(1366); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::DAY: { enterOuterAlt(_localctx, 29); - setState(1366); + setState(1368); match(SparqlAutomaticParser::DAY); - setState(1367); + setState(1369); match(SparqlAutomaticParser::T__1); - setState(1368); + setState(1370); expression(); - setState(1369); + setState(1371); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::HOURS: { enterOuterAlt(_localctx, 30); - setState(1371); + setState(1373); match(SparqlAutomaticParser::HOURS); - setState(1372); + setState(1374); match(SparqlAutomaticParser::T__1); - setState(1373); + setState(1375); expression(); - setState(1374); + setState(1376); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::MINUTES: { enterOuterAlt(_localctx, 31); - setState(1376); + setState(1378); match(SparqlAutomaticParser::MINUTES); - setState(1377); + setState(1379); match(SparqlAutomaticParser::T__1); - setState(1378); + setState(1380); expression(); - setState(1379); + setState(1381); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::SECONDS: { enterOuterAlt(_localctx, 32); - setState(1381); + setState(1383); match(SparqlAutomaticParser::SECONDS); - setState(1382); + setState(1384); match(SparqlAutomaticParser::T__1); - setState(1383); + setState(1385); expression(); - setState(1384); + setState(1386); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::TIMEZONE: { enterOuterAlt(_localctx, 33); - setState(1386); + setState(1388); match(SparqlAutomaticParser::TIMEZONE); - setState(1387); + setState(1389); match(SparqlAutomaticParser::T__1); - setState(1388); + setState(1390); expression(); - setState(1389); + setState(1391); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::TZ: { enterOuterAlt(_localctx, 34); - setState(1391); + setState(1393); match(SparqlAutomaticParser::TZ); - setState(1392); + setState(1394); match(SparqlAutomaticParser::T__1); - setState(1393); + setState(1395); expression(); - setState(1394); + setState(1396); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::NOW: { enterOuterAlt(_localctx, 35); - setState(1396); + setState(1398); match(SparqlAutomaticParser::NOW); - setState(1397); + setState(1399); match(SparqlAutomaticParser::NIL); break; } case SparqlAutomaticParser::UUID: { enterOuterAlt(_localctx, 36); - setState(1398); + setState(1400); match(SparqlAutomaticParser::UUID); - setState(1399); + setState(1401); match(SparqlAutomaticParser::NIL); break; } case SparqlAutomaticParser::STRUUID: { enterOuterAlt(_localctx, 37); - setState(1400); + setState(1402); match(SparqlAutomaticParser::STRUUID); - setState(1401); + setState(1403); match(SparqlAutomaticParser::NIL); break; } case SparqlAutomaticParser::MD5: { enterOuterAlt(_localctx, 38); - setState(1402); + setState(1404); match(SparqlAutomaticParser::MD5); - setState(1403); + setState(1405); match(SparqlAutomaticParser::T__1); - setState(1404); + setState(1406); expression(); - setState(1405); + setState(1407); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::SHA1: { enterOuterAlt(_localctx, 39); - setState(1407); + setState(1409); match(SparqlAutomaticParser::SHA1); - setState(1408); + setState(1410); match(SparqlAutomaticParser::T__1); - setState(1409); + setState(1411); expression(); - setState(1410); + setState(1412); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::SHA256: { enterOuterAlt(_localctx, 40); - setState(1412); + setState(1414); match(SparqlAutomaticParser::SHA256); - setState(1413); + setState(1415); match(SparqlAutomaticParser::T__1); - setState(1414); + setState(1416); expression(); - setState(1415); + setState(1417); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::SHA384: { enterOuterAlt(_localctx, 41); - setState(1417); + setState(1419); match(SparqlAutomaticParser::SHA384); - setState(1418); + setState(1420); match(SparqlAutomaticParser::T__1); - setState(1419); + setState(1421); expression(); - setState(1420); + setState(1422); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::SHA512: { enterOuterAlt(_localctx, 42); - setState(1422); + setState(1424); match(SparqlAutomaticParser::SHA512); - setState(1423); + setState(1425); match(SparqlAutomaticParser::T__1); - setState(1424); + setState(1426); expression(); - setState(1425); + setState(1427); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::COALESCE: { enterOuterAlt(_localctx, 43); - setState(1427); + setState(1429); match(SparqlAutomaticParser::COALESCE); - setState(1428); + setState(1430); expressionList(); break; } case SparqlAutomaticParser::IF: { enterOuterAlt(_localctx, 44); - setState(1429); - match(SparqlAutomaticParser::IF); - setState(1430); - match(SparqlAutomaticParser::T__1); setState(1431); - expression(); + match(SparqlAutomaticParser::IF); setState(1432); - match(SparqlAutomaticParser::T__7); + match(SparqlAutomaticParser::T__1); setState(1433); expression(); setState(1434); @@ -15343,143 +15352,147 @@ SparqlAutomaticParser::builtInCall() { setState(1435); expression(); setState(1436); + match(SparqlAutomaticParser::T__7); + setState(1437); + expression(); + setState(1438); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::STRLANG: { enterOuterAlt(_localctx, 45); - setState(1438); - match(SparqlAutomaticParser::STRLANG); - setState(1439); - match(SparqlAutomaticParser::T__1); setState(1440); - expression(); + match(SparqlAutomaticParser::STRLANG); setState(1441); - match(SparqlAutomaticParser::T__7); + match(SparqlAutomaticParser::T__1); setState(1442); expression(); setState(1443); + match(SparqlAutomaticParser::T__7); + setState(1444); + expression(); + setState(1445); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::STRDT: { enterOuterAlt(_localctx, 46); - setState(1445); - match(SparqlAutomaticParser::STRDT); - setState(1446); - match(SparqlAutomaticParser::T__1); setState(1447); - expression(); + match(SparqlAutomaticParser::STRDT); setState(1448); - match(SparqlAutomaticParser::T__7); + match(SparqlAutomaticParser::T__1); setState(1449); expression(); setState(1450); + match(SparqlAutomaticParser::T__7); + setState(1451); + expression(); + setState(1452); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::SAMETERM: { enterOuterAlt(_localctx, 47); - setState(1452); - match(SparqlAutomaticParser::SAMETERM); - setState(1453); - match(SparqlAutomaticParser::T__1); setState(1454); - expression(); + match(SparqlAutomaticParser::SAMETERM); setState(1455); - match(SparqlAutomaticParser::T__7); + match(SparqlAutomaticParser::T__1); setState(1456); expression(); setState(1457); + match(SparqlAutomaticParser::T__7); + setState(1458); + expression(); + setState(1459); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::ISIRI: { enterOuterAlt(_localctx, 48); - setState(1459); + setState(1461); match(SparqlAutomaticParser::ISIRI); - setState(1460); + setState(1462); match(SparqlAutomaticParser::T__1); - setState(1461); + setState(1463); expression(); - setState(1462); + setState(1464); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::ISURI: { enterOuterAlt(_localctx, 49); - setState(1464); + setState(1466); match(SparqlAutomaticParser::ISURI); - setState(1465); + setState(1467); match(SparqlAutomaticParser::T__1); - setState(1466); + setState(1468); expression(); - setState(1467); + setState(1469); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::ISBLANK: { enterOuterAlt(_localctx, 50); - setState(1469); + setState(1471); match(SparqlAutomaticParser::ISBLANK); - setState(1470); + setState(1472); match(SparqlAutomaticParser::T__1); - setState(1471); + setState(1473); expression(); - setState(1472); + setState(1474); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::ISLITERAL: { enterOuterAlt(_localctx, 51); - setState(1474); + setState(1476); match(SparqlAutomaticParser::ISLITERAL); - setState(1475); + setState(1477); match(SparqlAutomaticParser::T__1); - setState(1476); + setState(1478); expression(); - setState(1477); + setState(1479); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::ISNUMERIC: { enterOuterAlt(_localctx, 52); - setState(1479); + setState(1481); match(SparqlAutomaticParser::ISNUMERIC); - setState(1480); + setState(1482); match(SparqlAutomaticParser::T__1); - setState(1481); + setState(1483); expression(); - setState(1482); + setState(1484); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::REGEX: { enterOuterAlt(_localctx, 53); - setState(1484); + setState(1486); regexExpression(); break; } case SparqlAutomaticParser::EXISTS: { enterOuterAlt(_localctx, 54); - setState(1485); + setState(1487); existsFunc(); break; } case SparqlAutomaticParser::NOT: { enterOuterAlt(_localctx, 55); - setState(1486); + setState(1488); notExistsFunc(); break; } @@ -15558,27 +15571,27 @@ SparqlAutomaticParser::regexExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1489); + setState(1491); match(SparqlAutomaticParser::REGEX); - setState(1490); + setState(1492); match(SparqlAutomaticParser::T__1); - setState(1491); + setState(1493); expression(); - setState(1492); + setState(1494); match(SparqlAutomaticParser::T__7); - setState(1493); + setState(1495); expression(); - setState(1496); + setState(1498); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__7) { - setState(1494); + setState(1496); match(SparqlAutomaticParser::T__7); - setState(1495); + setState(1497); expression(); } - setState(1498); + setState(1500); match(SparqlAutomaticParser::T__2); } catch (RecognitionException& e) { @@ -15645,13 +15658,13 @@ SparqlAutomaticParser::langExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1500); + setState(1502); match(SparqlAutomaticParser::LANG); - setState(1501); + setState(1503); match(SparqlAutomaticParser::T__1); - setState(1502); + setState(1504); expression(); - setState(1503); + setState(1505); match(SparqlAutomaticParser::T__2); } catch (RecognitionException& e) { @@ -15725,27 +15738,27 @@ SparqlAutomaticParser::substringExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1505); + setState(1507); match(SparqlAutomaticParser::SUBSTR); - setState(1506); + setState(1508); match(SparqlAutomaticParser::T__1); - setState(1507); + setState(1509); expression(); - setState(1508); + setState(1510); match(SparqlAutomaticParser::T__7); - setState(1509); + setState(1511); expression(); - setState(1512); + setState(1514); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__7) { - setState(1510); + setState(1512); match(SparqlAutomaticParser::T__7); - setState(1511); + setState(1513); expression(); } - setState(1514); + setState(1516); match(SparqlAutomaticParser::T__2); } catch (RecognitionException& e) { @@ -15821,31 +15834,31 @@ SparqlAutomaticParser::strReplaceExpression() { }); try { enterOuterAlt(_localctx, 1); - setState(1516); - match(SparqlAutomaticParser::REPLACE); - setState(1517); - match(SparqlAutomaticParser::T__1); setState(1518); - expression(); + match(SparqlAutomaticParser::REPLACE); setState(1519); - match(SparqlAutomaticParser::T__7); + match(SparqlAutomaticParser::T__1); setState(1520); expression(); setState(1521); match(SparqlAutomaticParser::T__7); setState(1522); expression(); - setState(1525); + setState(1523); + match(SparqlAutomaticParser::T__7); + setState(1524); + expression(); + setState(1527); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__7) { - setState(1523); + setState(1525); match(SparqlAutomaticParser::T__7); - setState(1524); + setState(1526); expression(); } - setState(1527); + setState(1529); match(SparqlAutomaticParser::T__2); } catch (RecognitionException& e) { @@ -15911,9 +15924,9 @@ SparqlAutomaticParser::ExistsFuncContext* SparqlAutomaticParser::existsFunc() { }); try { enterOuterAlt(_localctx, 1); - setState(1529); + setState(1531); match(SparqlAutomaticParser::EXISTS); - setState(1530); + setState(1532); groupGraphPattern(); } catch (RecognitionException& e) { @@ -15984,11 +15997,11 @@ SparqlAutomaticParser::notExistsFunc() { }); try { enterOuterAlt(_localctx, 1); - setState(1532); + setState(1534); match(SparqlAutomaticParser::NOT); - setState(1533); + setState(1535); match(SparqlAutomaticParser::EXISTS); - setState(1534); + setState(1536); groupGraphPattern(); } catch (RecognitionException& e) { @@ -16095,28 +16108,28 @@ SparqlAutomaticParser::AggregateContext* SparqlAutomaticParser::aggregate() { exitRule(); }); try { - setState(1608); + setState(1610); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::COUNT: { enterOuterAlt(_localctx, 1); - setState(1536); + setState(1538); match(SparqlAutomaticParser::COUNT); - setState(1537); - match(SparqlAutomaticParser::T__1); setState(1539); + match(SparqlAutomaticParser::T__1); + setState(1541); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::DISTINCT) { - setState(1538); + setState(1540); match(SparqlAutomaticParser::DISTINCT); } - setState(1543); + setState(1545); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::T__0: { - setState(1541); + setState(1543); match(SparqlAutomaticParser::T__0); break; } @@ -16208,7 +16221,7 @@ SparqlAutomaticParser::AggregateContext* SparqlAutomaticParser::aggregate() { case SparqlAutomaticParser::STRING_LITERAL2: case SparqlAutomaticParser::STRING_LITERAL_LONG1: case SparqlAutomaticParser::STRING_LITERAL_LONG2: { - setState(1542); + setState(1544); expression(); break; } @@ -16216,168 +16229,168 @@ SparqlAutomaticParser::AggregateContext* SparqlAutomaticParser::aggregate() { default: throw NoViableAltException(this); } - setState(1545); + setState(1547); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::SUM: { enterOuterAlt(_localctx, 2); - setState(1546); + setState(1548); match(SparqlAutomaticParser::SUM); - setState(1547); - match(SparqlAutomaticParser::T__1); setState(1549); + match(SparqlAutomaticParser::T__1); + setState(1551); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::DISTINCT) { - setState(1548); + setState(1550); match(SparqlAutomaticParser::DISTINCT); } - setState(1551); + setState(1553); expression(); - setState(1552); + setState(1554); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::MIN: { enterOuterAlt(_localctx, 3); - setState(1554); + setState(1556); match(SparqlAutomaticParser::MIN); - setState(1555); - match(SparqlAutomaticParser::T__1); setState(1557); + match(SparqlAutomaticParser::T__1); + setState(1559); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::DISTINCT) { - setState(1556); + setState(1558); match(SparqlAutomaticParser::DISTINCT); } - setState(1559); + setState(1561); expression(); - setState(1560); + setState(1562); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::MAX: { enterOuterAlt(_localctx, 4); - setState(1562); + setState(1564); match(SparqlAutomaticParser::MAX); - setState(1563); - match(SparqlAutomaticParser::T__1); setState(1565); + match(SparqlAutomaticParser::T__1); + setState(1567); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::DISTINCT) { - setState(1564); + setState(1566); match(SparqlAutomaticParser::DISTINCT); } - setState(1567); + setState(1569); expression(); - setState(1568); + setState(1570); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::AVG: { enterOuterAlt(_localctx, 5); - setState(1570); + setState(1572); match(SparqlAutomaticParser::AVG); - setState(1571); - match(SparqlAutomaticParser::T__1); setState(1573); + match(SparqlAutomaticParser::T__1); + setState(1575); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::DISTINCT) { - setState(1572); + setState(1574); match(SparqlAutomaticParser::DISTINCT); } - setState(1575); + setState(1577); expression(); - setState(1576); + setState(1578); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::STDEV: { enterOuterAlt(_localctx, 6); - setState(1578); + setState(1580); match(SparqlAutomaticParser::STDEV); - setState(1579); - match(SparqlAutomaticParser::T__1); setState(1581); + match(SparqlAutomaticParser::T__1); + setState(1583); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::DISTINCT) { - setState(1580); + setState(1582); match(SparqlAutomaticParser::DISTINCT); } - setState(1583); + setState(1585); expression(); - setState(1584); + setState(1586); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::SAMPLE: { enterOuterAlt(_localctx, 7); - setState(1586); + setState(1588); match(SparqlAutomaticParser::SAMPLE); - setState(1587); - match(SparqlAutomaticParser::T__1); setState(1589); + match(SparqlAutomaticParser::T__1); + setState(1591); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::DISTINCT) { - setState(1588); + setState(1590); match(SparqlAutomaticParser::DISTINCT); } - setState(1591); + setState(1593); expression(); - setState(1592); + setState(1594); match(SparqlAutomaticParser::T__2); break; } case SparqlAutomaticParser::GROUP_CONCAT: { enterOuterAlt(_localctx, 8); - setState(1594); + setState(1596); match(SparqlAutomaticParser::GROUP_CONCAT); - setState(1595); - match(SparqlAutomaticParser::T__1); setState(1597); + match(SparqlAutomaticParser::T__1); + setState(1599); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::DISTINCT) { - setState(1596); + setState(1598); match(SparqlAutomaticParser::DISTINCT); } - setState(1599); + setState(1601); expression(); - setState(1604); + setState(1606); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__5) { - setState(1600); + setState(1602); match(SparqlAutomaticParser::T__5); - setState(1601); + setState(1603); match(SparqlAutomaticParser::SEPARATOR); - setState(1602); + setState(1604); match(SparqlAutomaticParser::T__19); - setState(1603); + setState(1605); string(); } - setState(1606); + setState(1608); match(SparqlAutomaticParser::T__2); break; } @@ -16452,15 +16465,15 @@ SparqlAutomaticParser::iriOrFunction() { }); try { enterOuterAlt(_localctx, 1); - setState(1610); - iri(); setState(1612); + iri(); + setState(1614); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__1 || _la == SparqlAutomaticParser::NIL) { - setState(1611); + setState(1613); argList(); } @@ -16532,21 +16545,21 @@ SparqlAutomaticParser::RdfLiteralContext* SparqlAutomaticParser::rdfLiteral() { }); try { enterOuterAlt(_localctx, 1); - setState(1614); + setState(1616); string(); - setState(1618); + setState(1620); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::LANGTAG: { - setState(1615); + setState(1617); match(SparqlAutomaticParser::LANGTAG); break; } case SparqlAutomaticParser::T__26: { - setState(1616); + setState(1618); match(SparqlAutomaticParser::T__26); - setState(1617); + setState(1619); iri(); break; } @@ -16689,14 +16702,14 @@ SparqlAutomaticParser::numericLiteral() { exitRule(); }); try { - setState(1623); + setState(1625); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::INTEGER: case SparqlAutomaticParser::DECIMAL: case SparqlAutomaticParser::DOUBLE: { enterOuterAlt(_localctx, 1); - setState(1620); + setState(1622); numericLiteralUnsigned(); break; } @@ -16705,7 +16718,7 @@ SparqlAutomaticParser::numericLiteral() { case SparqlAutomaticParser::DECIMAL_POSITIVE: case SparqlAutomaticParser::DOUBLE_POSITIVE: { enterOuterAlt(_localctx, 2); - setState(1621); + setState(1623); numericLiteralPositive(); break; } @@ -16714,7 +16727,7 @@ SparqlAutomaticParser::numericLiteral() { case SparqlAutomaticParser::DECIMAL_NEGATIVE: case SparqlAutomaticParser::DOUBLE_NEGATIVE: { enterOuterAlt(_localctx, 3); - setState(1622); + setState(1624); numericLiteralNegative(); break; } @@ -16798,7 +16811,7 @@ SparqlAutomaticParser::numericLiteralUnsigned() { }); try { enterOuterAlt(_localctx, 1); - setState(1625); + setState(1627); _la = _input->LA(1); if (!((((_la - 150) & ~0x3fULL) == 0) && ((1ULL << (_la - 150)) & 7) != 0)) { @@ -16883,7 +16896,7 @@ SparqlAutomaticParser::numericLiteralPositive() { }); try { enterOuterAlt(_localctx, 1); - setState(1627); + setState(1629); _la = _input->LA(1); if (!((((_la - 153) & ~0x3fULL) == 0) && ((1ULL << (_la - 153)) & 7) != 0)) { @@ -16968,7 +16981,7 @@ SparqlAutomaticParser::numericLiteralNegative() { }); try { enterOuterAlt(_localctx, 1); - setState(1629); + setState(1631); _la = _input->LA(1); if (!((((_la - 156) & ~0x3fULL) == 0) && ((1ULL << (_la - 156)) & 7) != 0)) { @@ -17034,7 +17047,7 @@ SparqlAutomaticParser::booleanLiteral() { }); try { enterOuterAlt(_localctx, 1); - setState(1631); + setState(1633); _la = _input->LA(1); if (!(_la == SparqlAutomaticParser::T__27 @@ -17118,7 +17131,7 @@ SparqlAutomaticParser::StringContext* SparqlAutomaticParser::string() { }); try { enterOuterAlt(_localctx, 1); - setState(1633); + setState(1635); _la = _input->LA(1); if (!((((_la - 160) & ~0x3fULL) == 0) && ((1ULL << (_la - 160)) & 15) != 0)) { @@ -17196,26 +17209,26 @@ SparqlAutomaticParser::IriContext* SparqlAutomaticParser::iri() { }); try { enterOuterAlt(_localctx, 1); - setState(1636); + setState(1638); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::PREFIX_LANGTAG) { - setState(1635); + setState(1637); match(SparqlAutomaticParser::PREFIX_LANGTAG); } - setState(1640); + setState(1642); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::IRI_REF: { - setState(1638); + setState(1640); iriref(); break; } case SparqlAutomaticParser::PNAME_NS: case SparqlAutomaticParser::PNAME_LN: { - setState(1639); + setState(1641); prefixedName(); break; } @@ -17288,19 +17301,19 @@ SparqlAutomaticParser::prefixedName() { exitRule(); }); try { - setState(1644); + setState(1646); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::PNAME_LN: { enterOuterAlt(_localctx, 1); - setState(1642); + setState(1644); pnameLn(); break; } case SparqlAutomaticParser::PNAME_NS: { enterOuterAlt(_localctx, 2); - setState(1643); + setState(1645); pnameNs(); break; } @@ -17373,7 +17386,7 @@ SparqlAutomaticParser::BlankNodeContext* SparqlAutomaticParser::blankNode() { }); try { enterOuterAlt(_localctx, 1); - setState(1646); + setState(1648); _la = _input->LA(1); if (!(_la == SparqlAutomaticParser::BLANK_NODE_LABEL @@ -17442,7 +17455,7 @@ SparqlAutomaticParser::IrirefContext* SparqlAutomaticParser::iriref() { }); try { enterOuterAlt(_localctx, 1); - setState(1648); + setState(1650); match(SparqlAutomaticParser::IRI_REF); } catch (RecognitionException& e) { @@ -17503,7 +17516,7 @@ SparqlAutomaticParser::PnameLnContext* SparqlAutomaticParser::pnameLn() { }); try { enterOuterAlt(_localctx, 1); - setState(1650); + setState(1652); match(SparqlAutomaticParser::PNAME_LN); } catch (RecognitionException& e) { @@ -17564,7 +17577,7 @@ SparqlAutomaticParser::PnameNsContext* SparqlAutomaticParser::pnameNs() { }); try { enterOuterAlt(_localctx, 1); - setState(1652); + setState(1654); match(SparqlAutomaticParser::PNAME_NS); } catch (RecognitionException& e) { diff --git a/src/parser/sparqlParser/generated/SparqlAutomaticParser.h b/src/parser/sparqlParser/generated/SparqlAutomaticParser.h index 9b1fce885b..88a6c91c60 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomaticParser.h +++ b/src/parser/sparqlParser/generated/SparqlAutomaticParser.h @@ -1331,8 +1331,8 @@ class SparqlAutomaticParser : public antlr4::Parser { size_t invokingState); virtual size_t getRuleIndex() const override; antlr4::tree::TerminalNode* DEFAULT(); - antlr4::tree::TerminalNode* GRAPH(); IriContext* iri(); + antlr4::tree::TerminalNode* GRAPH(); virtual void enterRule(antlr4::tree::ParseTreeListener* listener) override; virtual void exitRule(antlr4::tree::ParseTreeListener* listener) override; From a307781592842f82bdfef78fc47fc832fd37368d Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Thu, 6 Feb 2025 08:40:04 +0100 Subject: [PATCH 26/28] Implement `CONSTRUCT WHERE` correctly (#1757) Queries such as ` CONSTRUCT WHERE { ?s ?p ?o}` are now correctly treated equivalently to `CONSTRUCT { ?s ?p ?o} WHERE { ?s ?p ?o}`. Previously they returned an empty result because they were evaluated like `CONSTRUCT {} WHERE {?s ?p ?o}` Fixes #1349 --- .../sparqlParser/SparqlQleverVisitor.cpp | 49 +++++++++++++++++++ src/parser/sparqlParser/SparqlQleverVisitor.h | 11 +++++ test/SparqlAntlrParserTest.cpp | 37 ++++++++++++-- 3 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 088f463ec5..f3d051145d 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -278,6 +278,47 @@ Alias Visitor::visit(Parser::AliasWithoutBracketsContext* ctx) { return {visitExpressionPimpl(ctx->expression()), visit(ctx->var())}; } +// ____________________________________________________________________________________ +parsedQuery::BasicGraphPattern Visitor::toGraphPattern( + const ad_utility::sparql_types::Triples& triples) { + parsedQuery::BasicGraphPattern pattern{}; + pattern._triples.reserve(triples.size()); + auto toTripleComponent = [](const T& item) { + namespace tc = ad_utility::triple_component; + if constexpr (ad_utility::isSimilar) { + return TripleComponent{item}; + } else if constexpr (ad_utility::isSimilar) { + // Blank Nodes in the pattern are to be treated as internal variables + // inside WHERE. + return TripleComponent{ + ParsedQuery::blankNodeToInternalVariable(item.toSparql())}; + } else { + static_assert(ad_utility::SimilarToAny); + return RdfStringParser>::parseTripleObject( + item.toSparql()); + } + }; + auto toPropertyPath = [](const T& item) -> PropertyPath { + if constexpr (ad_utility::isSimilar) { + return PropertyPath::fromVariable(item); + } else if constexpr (ad_utility::isSimilar) { + return PropertyPath::fromIri(item.toSparql()); + } else { + static_assert(ad_utility::SimilarToAny); + // This case can only happen if there's a bug in the SPARQL parser. + AD_THROW("Literals or blank nodes are not valid predicates."); + } + }; + for (const auto& triple : triples) { + auto subject = std::visit(toTripleComponent, triple.at(0)); + auto predicate = std::visit(toPropertyPath, triple.at(1)); + auto object = std::visit(toTripleComponent, triple.at(2)); + pattern._triples.emplace_back(std::move(subject), std::move(predicate), + std::move(object)); + } + return pattern; +} + // ____________________________________________________________________________________ ParsedQuery Visitor::visit(Parser::ConstructQueryContext* ctx) { ParsedQuery query; @@ -288,8 +329,16 @@ ParsedQuery Visitor::visit(Parser::ConstructQueryContext* ctx) { .value_or(parsedQuery::ConstructClause{}); visitWhereClause(ctx->whereClause(), query); } else { + // For `CONSTRUCT WHERE`, the CONSTRUCT template and the WHERE clause are + // syntactically the same, so we set the flag to true to keep the blank + // nodes, and convert them into variables during `toGraphPattern`. + isInsideConstructTriples_ = true; + auto cleanup = + absl::Cleanup{[this]() { isInsideConstructTriples_ = false; }}; query._clause = parsedQuery::ConstructClause{ visitOptional(ctx->triplesTemplate()).value_or(Triples{})}; + query._rootGraphPattern._graphPatterns.emplace_back( + toGraphPattern(query.constructClause().triples_)); } query.addSolutionModifiers(visit(ctx->solutionModifier())); diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.h b/src/parser/sparqlParser/SparqlQleverVisitor.h index fb1cb9c05c..b3d629145a 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.h +++ b/src/parser/sparqlParser/SparqlQleverVisitor.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include "engine/sparqlExpressions/AggregateExpression.h" #include "engine/sparqlExpressions/NaryExpression.h" @@ -598,4 +599,14 @@ class SparqlQleverVisitor { void warnOrThrowIfUnboundVariables(auto* ctx, const SparqlExpressionPimpl& expression, std::string_view clauseName); + + // Convert an instance of `Triples` to a `BasicGraphPattern` so it can be used + // just like a WHERE clause. Most of the time this just changes the type and + // stays semantically the same, but for blank nodes, this step converts them + // into internal variables so they are interpreted correctly by the query + // planner. + static parsedQuery::BasicGraphPattern toGraphPattern( + const ad_utility::sparql_types::Triples& triples); + + FRIEND_TEST(SparqlParser, ensureExceptionOnInvalidGraphTerm); }; diff --git a/test/SparqlAntlrParserTest.cpp b/test/SparqlAntlrParserTest.cpp index a0e10d9b6f..cca4ebeddb 100644 --- a/test/SparqlAntlrParserTest.cpp +++ b/test/SparqlAntlrParserTest.cpp @@ -1215,9 +1215,30 @@ TEST(SparqlParser, ConstructQuery) { m::pq::LimitOffset({10}), m::pq::OrderKeys({{Var{"?a"}, false}}))); // This case of the grammar is not useful without Datasets, but we still // support it. - expectConstructQuery("CONSTRUCT WHERE { ?a ?b }", - m::ConstructQuery({{Var{"?a"}, Iri{""}, Var{"?b"}}}, - m::GraphPattern())); + expectConstructQuery( + "CONSTRUCT WHERE { ?a ?b }", + m::ConstructQuery( + {{Var{"?a"}, Iri{""}, Var{"?b"}}}, + m::GraphPattern(m::Triples({{Var{"?a"}, "", Var{"?b"}}})))); + + // Blank nodes turn into variables inside WHERE. + expectConstructQuery( + "CONSTRUCT WHERE { [] ?b }", + m::ConstructQuery( + {{BlankNode{true, "0"}, Iri{""}, Var{"?b"}}}, + m::GraphPattern(m::Triples( + {{Var{absl::StrCat(QLEVER_INTERNAL_BLANKNODE_VARIABLE_PREFIX, + "g_0")}, + "", Var{"?b"}}})))); + + // Test another variant to cover all cases. + expectConstructQuery( + "CONSTRUCT WHERE { ?foo \"Abc\"@en }", + m::ConstructQuery( + {{Iri{""}, Var{"?foo"}, Literal{"\"Abc\"@en"}}}, + m::GraphPattern(m::Triples( + {{iri(""), PropertyPath::fromVariable(Var{"?foo"}), + lit("\"Abc\"", "@en")}})))); // CONSTRUCT with datasets. expectConstructQuery( "CONSTRUCT { } FROM FROM NAMED FROM NAMED WHERE { }", @@ -1225,6 +1246,16 @@ TEST(SparqlParser, ConstructQuery) { m::Graphs{iri(""), iri("")})); } +// _____________________________________________________________________________ +TEST(SparqlParser, ensureExceptionOnInvalidGraphTerm) { + EXPECT_THROW(SparqlQleverVisitor::toGraphPattern( + {{Var{"?a"}, BlankNode{true, "0"}, Var{"?b"}}}), + ad_utility::Exception); + EXPECT_THROW(SparqlQleverVisitor::toGraphPattern( + {{Var{"?a"}, Literal{"\"Abc\""}, Var{"?b"}}}), + ad_utility::Exception); +} + // Test that ASK queries are parsed as they should. TEST(SparqlParser, AskQuery) { // Some helper functions and abbreviations. From 3e7df420d4c1ace0c2fd5d0c66b3a6c13449a51d Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Fri, 7 Feb 2025 13:28:50 +0100 Subject: [PATCH 27/28] Delete an unused function template (#1772) The function was never even instantiated. --- src/engine/Engine.cpp | 1 + src/engine/Engine.h | 84 ------------------------------------------- 2 files changed, 1 insertion(+), 84 deletions(-) diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index a9d8c80529..14ba11dae3 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -6,6 +6,7 @@ #include "engine/CallFixedSize.h" #include "util/ChunkedForLoop.h" +#include "util/Exception.h" // The actual implementation of sorting an `IdTable` according to the // `sortCols`. diff --git a/src/engine/Engine.h b/src/engine/Engine.h index 577e982f81..8d1fa6f9d9 100644 --- a/src/engine/Engine.h +++ b/src/engine/Engine.h @@ -3,99 +3,15 @@ // Author: Björn Buchhold (buchhold@informatik.uni-freiburg.de) #pragma once -#include #include #include -#include "backports/algorithm.h" -#include "engine/IndexSequence.h" #include "engine/idTable/IdTable.h" #include "global/Constants.h" -#include "global/Id.h" -#include "util/Exception.h" #include "util/Log.h" class Engine { public: - template - static void filter(const IdTableView& v, const Comp& comp, - IdTableStatic* result) { - AD_CONTRACT_CHECK(result); - AD_CONTRACT_CHECK(result->size() == 0); - LOG(DEBUG) << "Filtering " << v.size() << " elements.\n"; - for (const auto& e : v) { - if (comp(e)) { - result->push_back(e); - } - } - LOG(DEBUG) << "Filter done, size now: " << result->size() << " elements.\n"; - } - - template - static void filter(const IdTable& dynV, size_t fc1, size_t fc2, - const IdTable& dynFilter, IdTable* dynResult) { - AD_CONTRACT_CHECK(dynResult); - AD_CONTRACT_CHECK(dynResult->size() == 0); - LOG(DEBUG) << "Filtering " << dynV.size() - << " elements with a filter relation with " << dynFilter.size() - << "elements\n"; - - // Check trivial case. - if (dynV.size() == 0 || dynFilter.size() == 0) { - return; - } - - const IdTableView v = dynV.asStaticView(); - const IdTableView filter = - dynFilter.asStaticView(); - IdTableStatic result = std::move(*dynResult).toStatic(); - - // Intersect both lists. - size_t i = 0; - size_t j = 0; - - while (i < v.size() && j < filter.size()) { - while (v(i, fc1) < filter(j, 0)) { - ++i; - if (i >= v.size()) { - goto finish; - } - } - while (filter(j, 0) < v(i, fc1)) { - ++j; - if (j >= filter.size()) { - goto finish; - } - } - while (v(i, fc1) == filter(j, 0)) { - // fc1 match, create cross-product - // Check fc2 - if (v(i, fc2) == filter(j, 1)) { - result.push_back(v, i); - ++i; - if (i >= v.size()) { - break; - } - } else if (v(i, fc2) < filter(j, 1)) { - ++i; - if (i >= v.size()) { - break; - } - } else { - ++j; - if (j >= filter.size()) { - break; - } - } - } - } - finish: - *dynResult = std::move(result).toDynamic(); - - LOG(DEBUG) << "Filter done, size now: " << dynResult->size() - << " elements.\n"; - } - template static void sort(IdTable* tab, const size_t keyColumn) { LOG(DEBUG) << "Sorting " << tab->size() << " elements ..." << std::endl; From d7a70c7bc5579b17989b9d3049cb7dfd77944ce0 Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Fri, 7 Feb 2025 13:39:51 +0100 Subject: [PATCH 28/28] Unescape Unicode sequences in the SPARQL parser (#1770) This PR makes sure escape sequences are applied before passing the string to ANTLR for the real parsing step (see [the SPARQL 1.1 specification](https://www.w3.org/TR/sparql11-query/#codepointEscape) for details). UTF-16 surrogate pairs are correctly handled. Also the ctre version is incremented to use `search_all` (non-deprecated variant of `range`). --- .codespellrc | 2 +- CMakeLists.txt | 2 +- src/parser/SparqlParserHelpers.cpp | 91 ++++++++++++++++++++++++- src/parser/SparqlParserHelpers.h | 4 ++ src/util/StringUtilsImpl.h | 2 +- test/SparqlParserTest.cpp | 103 +++++++++++++++++++++++++++++ 6 files changed, 200 insertions(+), 4 deletions(-) diff --git a/.codespellrc b/.codespellrc index a1e8aa7c0f..0132eac027 100644 --- a/.codespellrc +++ b/.codespellrc @@ -3,6 +3,6 @@ skip = .git*,.codespellrc,*.pdf,generated check-hidden = true # Ignore mixedCase variables, lines with latin, lines with codespell-ignore pragma, etc -ignore-regex = \b([A-Z]*[a-z]+[A-Z][a-zA-Z]*)\b|.*(Lorem ipsum|eleifend|feugait|codespell-ignore).* +ignore-regex = \b([A-Z]*[a-z]+[A-Z][a-zA-Z]*)\b|.*(Lorem ipsum|eleifend|feugait|codespell-ignore).*|https?://\S+ # alph - is used frequently in tests, just ignore altogether ignore-words-list = ser,alph,inbetween,interm diff --git a/CMakeLists.txt b/CMakeLists.txt index 3679de4c51..0d541d8a32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -231,7 +231,7 @@ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") FetchContent_Declare( ctre GIT_REPOSITORY https://github.com/hanickadot/compile-time-regular-expressions.git - GIT_TAG b3d7788b559e34d985c8530c3e0e7260b67505a6 # v3.8.1 + GIT_TAG eb9577aae3515d14e6c5564f9aeb046d2e7c1124 # v3.9.0 ) ################################ diff --git a/src/parser/SparqlParserHelpers.cpp b/src/parser/SparqlParserHelpers.cpp index ed7717f35b..541725ec39 100644 --- a/src/parser/SparqlParserHelpers.cpp +++ b/src/parser/SparqlParserHelpers.cpp @@ -4,6 +4,11 @@ #include "SparqlParserHelpers.h" +#include + +#include +#include + #include "sparqlParser/generated/SparqlAutomaticLexer.h" namespace sparqlParserHelpers { @@ -13,7 +18,8 @@ using std::string; ParserAndVisitor::ParserAndVisitor( std::string input, SparqlQleverVisitor::DisableSomeChecksOnlyForTesting disableSomeChecks) - : input_{std::move(input)}, visitor_{{}, disableSomeChecks} { + : input_{unescapeUnicodeSequences(std::move(input))}, + visitor_{{}, disableSomeChecks} { // The default in ANTLR is to log all errors to the console and to continue // the parsing. We need to turn parse errors into exceptions instead to // propagate them to the user. @@ -30,4 +36,87 @@ ParserAndVisitor::ParserAndVisitor( : ParserAndVisitor{std::move(input), disableSomeChecks} { visitor_.setPrefixMapManually(std::move(prefixes)); } + +// _____________________________________________________________________________ +std::string ParserAndVisitor::unescapeUnicodeSequences(std::string input) { + std::string_view view{input}; + std::string output; + bool noEscapeSequenceFound = true; + size_t lastPos = 0; + UChar32 highSurrogate = 0; + + auto throwError = [](bool condition, std::string_view message) { + if (!condition) { + throw InvalidSparqlQueryException{ + absl::StrCat("Error in unicode escape sequence. ", message)}; + } + }; + + for (const auto& match : + ctre::search_all(view)) { + if (noEscapeSequenceFound) { + output.reserve(input.size()); + noEscapeSequenceFound = false; + } + auto inBetweenPart = + view.substr(lastPos, match.data() - (view.data() + lastPos)); + + throwError( + inBetweenPart.empty() || highSurrogate == 0, + "A high surrogate must be directly followed by a low surrogate."); + + output += inBetweenPart; + lastPos = match.data() + match.size() - view.data(); + + auto hexValue = match.to_view(); + hexValue.remove_prefix(std::string_view{"\\U"}.size()); + + UChar32 codePoint; + auto result = std::from_chars( + hexValue.data(), hexValue.data() + hexValue.size(), codePoint, 16); + AD_CORRECTNESS_CHECK(result.ec == std::errc{}); + AD_CORRECTNESS_CHECK( + hexValue.size() == 8 || hexValue.size() == 4, + "Unicode escape sequences must be either 8 or 4 characters long."); + + bool isFullCodePoint = hexValue.size() == 8; + + // See https://symbl.cc/en/unicode/blocks/high-surrogates/ for more + // information. + if (U16_IS_LEAD(codePoint)) { + throwError(!isFullCodePoint, + "Surrogates should not be encoded as full code points."); + throwError( + highSurrogate == 0, + "A high surrogate cannot be followed by another high surrogate."); + highSurrogate = codePoint; + continue; + } else if (U16_IS_TRAIL(codePoint)) { + throwError(!isFullCodePoint, + "Surrogates should not be encoded as full code points."); + throwError(highSurrogate != 0, + "A low surrogate cannot be the first surrogate."); + codePoint = U16_GET_SUPPLEMENTARY(highSurrogate, codePoint); + highSurrogate = 0; + } else { + throwError( + highSurrogate == 0, + "A high surrogate cannot be followed by a regular code point."); + } + + icu::UnicodeString helper{codePoint}; + helper.toUTF8String(output); + } + + // Avoid redundant copy if no escape sequences were found. + if (noEscapeSequenceFound) { + return input; + } + + throwError(highSurrogate == 0, + "A high surrogate must be followed by a low surrogate."); + + output += view.substr(lastPos); + return output; +} } // namespace sparqlParserHelpers diff --git a/src/parser/SparqlParserHelpers.h b/src/parser/SparqlParserHelpers.h index 5db09cc27e..55d597d0eb 100644 --- a/src/parser/SparqlParserHelpers.h +++ b/src/parser/SparqlParserHelpers.h @@ -35,6 +35,10 @@ struct ParserAndVisitor { ad_utility::antlr_utility::ThrowingErrorListener errorListener_{}; + // Unescapes unicode sequences like \U01234567 and \u0123 in the input string + // before beginning with actual parsing as the SPARQL standard mandates. + static std::string unescapeUnicodeSequences(std::string input); + public: SparqlAutomaticParser parser_{&tokens_}; SparqlQleverVisitor visitor_; diff --git a/src/util/StringUtilsImpl.h b/src/util/StringUtilsImpl.h index 87db3aa645..1e677bf729 100644 --- a/src/util/StringUtilsImpl.h +++ b/src/util/StringUtilsImpl.h @@ -74,7 +74,7 @@ std::string insertThousandSeparator(const std::string_view str, "])(?\\d{4,})"}; auto parseIterator = std::begin(str); ql::ranges::for_each( - ctre::range(str), + ctre::search_all(str), [&parseIterator, &ostream, &insertSeparator](const auto& match) { /* The digit sequence, that must be transformed. Note: The string view diff --git a/test/SparqlParserTest.cpp b/test/SparqlParserTest.cpp index d3f2352b1e..c232f2186a 100644 --- a/test/SparqlParserTest.cpp +++ b/test/SparqlParserTest.cpp @@ -23,6 +23,7 @@ auto lit = ad_utility::testing::tripleComponentLiteral; auto iri = ad_utility::testing::iri; } // namespace +// _____________________________________________________________________________ TEST(ParserTest, testParse) { { auto pq = SparqlParser::parseQuery("SELECT ?x WHERE {?x ?y ?z}"); @@ -698,6 +699,7 @@ TEST(ParserTest, testParse) { } } +// _____________________________________________________________________________ TEST(ParserTest, testFilterWithoutDot) { ParsedQuery pq = SparqlParser::parseQuery( "PREFIX fb: \n" @@ -726,6 +728,7 @@ TEST(ParserTest, testFilterWithoutDot) { ASSERT_EQ("(?1 != fb:m.018mts)", filters[2].expression_.getDescriptor()); } +// _____________________________________________________________________________ TEST(ParserTest, testExpandPrefixes) { ParsedQuery pq = SparqlParser::parseQuery( "PREFIX : \n" @@ -754,6 +757,7 @@ TEST(ParserTest, testExpandPrefixes) { ASSERT_EQ(0, pq._limitOffset._offset); } +// _____________________________________________________________________________ TEST(ParserTest, testLiterals) { ParsedQuery pq = SparqlParser::parseQuery( "PREFIX xsd: SELECT * WHERE { " @@ -772,6 +776,7 @@ TEST(ParserTest, testLiterals) { ASSERT_EQ(DateYearOrDuration{Date(2000, 1, 1, -1)}, c._triples[1].o_); } +// _____________________________________________________________________________ TEST(ParserTest, testSolutionModifiers) { { ParsedQuery pq = @@ -993,6 +998,7 @@ TEST(ParserTest, testSolutionModifiers) { } } +// _____________________________________________________________________________ TEST(ParserTest, testGroupByAndAlias) { ParsedQuery pq = SparqlParser::parseQuery( "SELECT (COUNT(?a) as ?count) WHERE { ?b ?a } GROUP BY ?b"); @@ -1008,6 +1014,7 @@ TEST(ParserTest, testGroupByAndAlias) { EXPECT_THAT(pq, m::GroupByVariables({Var{"?b"}})); } +// _____________________________________________________________________________ TEST(ParserTest, Bind) { ParsedQuery pq = SparqlParser::parseQuery("SELECT ?a WHERE { BIND (10 - 5 as ?a) . }"); @@ -1020,6 +1027,7 @@ TEST(ParserTest, Bind) { ASSERT_EQ(bind._expression.getDescriptor(), "10 - 5"); } +// _____________________________________________________________________________ TEST(ParserTest, Order) { { ParsedQuery pq = @@ -1101,6 +1109,7 @@ TEST(ParserTest, Order) { */ } +// _____________________________________________________________________________ TEST(ParserTest, Group) { { ParsedQuery pq = SparqlParser::parseQuery( @@ -1165,6 +1174,7 @@ TEST(ParserTest, Group) { } } +// _____________________________________________________________________________ TEST(ParserTest, LanguageFilterPostProcessing) { { ParsedQuery q = SparqlParser::parseQuery( @@ -1246,3 +1256,96 @@ TEST(ParserTest, LanguageFilterPostProcessing) { triples[2]); } } + +// _____________________________________________________________________________ +namespace { +std::string getFirstTriple(const ParsedQuery& q) { + return q._rootGraphPattern._graphPatterns.at(0) + .getBasic() + ._triples.at(0) + .asString(); +} +} // namespace + +// _____________________________________________________________________________ +TEST(ParserTest, HandlesBasicUnicodeEscapeSequences) { + ParsedQuery q1 = SparqlParser::parseQuery( + R"(SELECT * WHERE { ?s '\u0080\u07FF\u0800\u0FFF\u1000\uCFFF\uD000\uD7FF\uE000\uFFFD\U00010000\U0003FFFD\U00040000\U000FFFFD\U00100000\U0010FFFD'})"); + EXPECT_EQ(getFirstTriple(q1), + "{s: ?s, p: , o: " + "\"\u0080\u07FF\u0800\u0FFF\u1000\uCFFF\uD000\uD7FF\uE000\uFFFD" + "\U00010000\U0003FFFD\U00040000\U000FFFFD\U00100000\U0010FFFD\"}"); + + ParsedQuery q2 = + SparqlParser::parseQuery(R"(SELECT * WHERE { ?s ?p "\U0001f46a" . })"); + EXPECT_EQ(getFirstTriple(q2), "{s: ?s, p: ?p, o: \"\U0001f46a\"}"); + + ParsedQuery q3 = SparqlParser::parseQuery( + R"(PREFIX \u03B1: SELECT * WHERE { ?s ?p α\u003Aba . })"); + EXPECT_EQ(getFirstTriple(q3), + "{s: ?s, p: ?p, o: }"); + + ParsedQuery q4 = SparqlParser::parseQuery( + R"(SELECT * WHERE { ?p\u00201. })"); + EXPECT_EQ(getFirstTriple(q4), + "{s: , p: ?p, o: 1}"); + + // Ensure we don't double-unescape, \u sequences are not allowed in literals + EXPECT_THROW( + SparqlParser::parseQuery(R"(SELECT * WHERE { "\u005Cu2764" ?p 1. })"), + InvalidSparqlQueryException); +} + +// _____________________________________________________________________________ +TEST(ParserTest, HandlesSurrogatesCorrectly) { + using SP = SparqlParser; + using ::testing::HasSubstr; + ParsedQuery q = SP::parseQuery( + R"(SELECT * WHERE { "\uD83E\udD37\uD83C\uDFFD\u200D\u2642\uFE0F" ?p 1. })"); + EXPECT_EQ(getFirstTriple(q), "{s: \"🤷🏽‍♂️\", p: ?p, o: 1}"); + + AD_EXPECT_THROW_WITH_MESSAGE_AND_TYPE( + SP::parseQuery(R"(SELECT * WHERE { ?s ?p '\uD83C \uDFFD' })"), + HasSubstr( + "A high surrogate must be directly followed by a low surrogate."), + InvalidSparqlQueryException); + + AD_EXPECT_THROW_WITH_MESSAGE_AND_TYPE( + SP::parseQuery(R"(SELECT * WHERE { ?s ?p '\uD800' })"), + HasSubstr("A high surrogate must be followed by a low surrogate."), + InvalidSparqlQueryException); + + AD_EXPECT_THROW_WITH_MESSAGE_AND_TYPE( + SP::parseQuery(R"(SELECT * WHERE { ?s ?p '\U0000D800' })"), + HasSubstr("Surrogates should not be encoded as full code points."), + InvalidSparqlQueryException); + + AD_EXPECT_THROW_WITH_MESSAGE_AND_TYPE( + SP::parseQuery(R"(SELECT * WHERE { ?s ?p '\uD800\uD800' })"), + HasSubstr( + "A high surrogate cannot be followed by another high surrogate."), + InvalidSparqlQueryException); + + AD_EXPECT_THROW_WITH_MESSAGE_AND_TYPE( + SP::parseQuery(R"(SELECT * WHERE { ?s ?p '\U0000DFFD' })"), + HasSubstr("Surrogates should not be encoded as full code points."), + InvalidSparqlQueryException); + + AD_EXPECT_THROW_WITH_MESSAGE_AND_TYPE( + SP::parseQuery(R"(SELECT * WHERE { ?s ?p '\uDFFD' })"), + HasSubstr("A low surrogate cannot be the first surrogate."), + InvalidSparqlQueryException); + + AD_EXPECT_THROW_WITH_MESSAGE_AND_TYPE( + SP::parseQuery(R"(SELECT * WHERE { ?s ?p '\uD800\u0020' })"), + HasSubstr("A high surrogate cannot be followed by a regular code point."), + InvalidSparqlQueryException); + + // Note: We don't allow mixing escaped and unescape surrogates, that's just + // weird and the C++ compiler rightfully won't compile strings like these: + // SELECT * WHERE { ?s ?p '\\uD83C\uDFFD' } + // SELECT * WHERE { ?s ?p '\uD83C\\uDFFD' } + + // So writing unit tests for these cases is not possible without creating + // semi-invalid UTF-8 strings. +}