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/.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 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/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/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 b06202dbf4..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); } /* @@ -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)}, @@ -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/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/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/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/CartesianProductJoin.cpp b/src/engine/CartesianProductJoin.cpp index cedb832648..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 @@ -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( @@ -302,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/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; 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/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/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/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/Operation.cpp b/src/engine/Operation.cpp index 3a25e7521c..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) { @@ -598,7 +600,7 @@ const vector& Operation::getResultSortedOn() const { // _____________________________________________________________________________ void Operation::signalQueryUpdate() const { - if (_executionContext) { + if (_executionContext && _executionContext->areWebsocketUpdatesEnabled()) { _executionContext->signalQueryUpdate(*_rootRuntimeInfo); } } 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/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/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index 7f22de2020..49ceb340aa 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(); } // _____________________________________________________________________________ @@ -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/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..a8bf59d1c6 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); } } @@ -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,17 +1335,55 @@ 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); } // _____________________________________________________________________________ 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); - 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 + + // 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. @@ -1358,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); } @@ -1373,32 +1417,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; } // _____________________________________________________________________________ @@ -1418,13 +1505,14 @@ 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) { 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" @@ -1433,8 +1521,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(); @@ -1447,7 +1535,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. @@ -1478,7 +1567,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; } @@ -1793,7 +1882,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 +2350,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 +2661,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..5b4a1c67f3 100644 --- a/src/engine/QueryPlanner.h +++ b/src/engine/QueryPlanner.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include "engine/CheckUsePatternTrick.h" @@ -17,6 +18,8 @@ class QueryPlanner { using TextLimitMap = ad_utility::HashMap; + using TextLimitVec = + std::vector>; using CancellationHandle = ad_utility::SharedCancellationHandle; template using vector = std::vector; @@ -324,7 +327,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 @@ -400,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; /** @@ -470,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 @@ -478,15 +481,18 @@ 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`, // 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 diff --git a/src/engine/Result.h b/src/engine/Result.h index 10b7364a3e..c372cf7102 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) // @@ -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/Server.cpp b/src/engine/Server.cpp index 08fc6f9607..4f7fc97a4e 100644 --- a/src/engine/Server.cpp +++ b/src/engine/Server.cpp @@ -11,18 +11,23 @@ #include #include +#include "GraphStoreProtocol.h" #include "engine/ExecuteUpdate.h" #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" #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; @@ -164,31 +169,87 @@ 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) { + 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{}}; + 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 // 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); + }; + auto extractAccessTokenFromRequest = [&parsedRequest, &request]() { + parsedRequest.accessToken_ = + extractAccessToken(request, parsedRequest.parameters_); + }; if (request.method() == http::verb::get) { - setOperationIfSpecifiedInParams.template operator()("query"); + setOperationIfSpecifiedInParams(ti, "query"); + addDatasetClauses(); + extractAccessTokenFromRequest(); + if (parsedRequest.parameters_.contains("update")) { throw std::runtime_error("SPARQL Update is not allowed as GET request."); } @@ -256,17 +317,25 @@ 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(); + // 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()}; + parsedRequest.operation_ = Query{request.body(), {}}; + addDatasetClauses(); + extractAccessTokenFromRequest(); return parsedRequest; } if (contentType.starts_with(contentTypeSparqlUpdate)) { - parsedRequest.operation_ = Update{request.body()}; + parsedRequest.operation_ = Update{request.body(), {}}; + addDatasetClauses(); + extractAccessTokenFromRequest(); return parsedRequest; } throw std::runtime_error(absl::StrCat( @@ -335,33 +404,21 @@ 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(&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 // 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")); } }; @@ -474,14 +531,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. @@ -490,25 +546,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. @@ -537,19 +590,20 @@ 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}; } // ____________________________________________________________________________ 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_; @@ -729,17 +783,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, @@ -786,7 +834,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) { @@ -795,7 +843,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); @@ -805,7 +853,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); @@ -819,10 +867,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); @@ -883,9 +931,64 @@ 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( - const ad_utility::url_parser::ParamValueMap& params, const string& update, +json Server::processUpdateImpl( + const ad_utility::url_parser::ParamValueMap& params, const Update& update, ad_utility::Timer& requestTimer, TimeLimit timeLimit, auto& messageSender, ad_utility::SharedCancellationHandle cancellationHandle, DeltaTriples& deltaTriples) { @@ -893,12 +996,13 @@ void 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()) { @@ -907,8 +1011,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" @@ -920,57 +1027,61 @@ 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); } // ____________________________________________________________________________ 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); - // 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; } // ____________________________________________________________________________ -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 @@ -983,11 +1094,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) { @@ -997,7 +1109,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) { @@ -1028,8 +1140,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() @@ -1102,40 +1222,17 @@ 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; } } - -// _____________________________________________________________________________ -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..9558f8743e 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" @@ -34,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, @@ -114,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`. @@ -123,14 +131,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. @@ -138,21 +143,32 @@ 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); + // 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, + 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); @@ -170,7 +186,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; @@ -181,8 +197,9 @@ class Server { const string& operation); // Execute an update operation. The function must have exclusive access to the // DeltaTriples object. - void processUpdateImpl( - const ad_utility::url_parser::ParamValueMap& params, const string& update, + json processUpdateImpl( + 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); @@ -256,18 +273,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/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 9e57073602..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} { @@ -364,7 +365,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]; } @@ -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/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/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/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 3378ae7fdb..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 { @@ -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/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/global/RuntimeParameters.h b/src/global/RuntimeParameters.h index 8e60725ffe..f61705e7fc 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) { @@ -53,7 +54,13 @@ 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 + // value. + SizeT<"small-index-scan-size-estimate-divisor">{5}, + }; }(); return params; } 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/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/CompressedRelation.cpp b/src/index/CompressedRelation.cpp index fc306e892f..63c51bd464 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`. @@ -178,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() || @@ -194,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; } @@ -400,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&) @@ -422,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; } @@ -482,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; }; @@ -631,21 +666,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 +713,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 +722,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; } @@ -948,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; @@ -1366,10 +1422,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]); @@ -1522,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 8b69c8d363..e8d35fa3b3 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) { @@ -146,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); }); @@ -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 { @@ -224,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 89e57b05fe..3a2037768c 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); @@ -147,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 @@ -208,7 +225,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.Text.cpp b/src/index/IndexImpl.Text.cpp index 76c0015974..3b872eb39c 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 "index/TextIndexReadWrite.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) { @@ -353,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(); @@ -380,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(); @@ -400,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( @@ -637,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()); @@ -729,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)); }); - std::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; } @@ -757,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; } @@ -815,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.cpp b/src/index/IndexImpl.cpp index 1f7279cd3c..f11cab5b87 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)}; @@ -891,9 +892,9 @@ 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()); + p.metaData().blockDataShared()); }); }; @@ -1621,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; @@ -1653,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); @@ -1715,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 d9ec19eb14..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" @@ -25,13 +26,14 @@ #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" #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" @@ -106,7 +108,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; @@ -521,8 +522,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); @@ -576,10 +589,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, @@ -587,15 +596,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 @@ -630,31 +630,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; @@ -704,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/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/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/LocatedTriples.cpp b/src/index/LocatedTriples.cpp index c8d977f473..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)> @@ -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 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/StringSortComparator.h b/src/index/StringSortComparator.h index da0324ff5e..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`. @@ -313,7 +317,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/index/TextIndexReadWrite.cpp b/src/index/TextIndexReadWrite.cpp new file mode 100644 index 0000000000..d7af6320e3 --- /dev/null +++ b/src/index/TextIndexReadWrite.cpp @@ -0,0 +1,150 @@ +// 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 +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 +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..26d0327769 --- /dev/null +++ b/src/index/TextIndexReadWrite.h @@ -0,0 +1,360 @@ +// 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( + !ranges::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 +class GapEncode { + static_assert(std::is_arithmetic_v); + + 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( + !ranges::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>>; diff --git a/src/index/VocabularyMerger.h b/src/index/VocabularyMerger.h index 8b9322796c..ae72da46c6 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 = ranges::invocable; // Concept for a callable that compares to `string_view`s. template -concept WordComparator = std::predicate; +CPP_concept WordComparator = + ranges::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..6620248363 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); @@ -297,7 +314,7 @@ class CompressedVocabulary { // _________________________________________________________________ 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()); 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/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/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/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/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/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/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/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/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index f23530f820..f3d051145d 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. @@ -261,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; @@ -271,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())); @@ -311,8 +377,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(); @@ -438,7 +504,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 +512,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/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/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; 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/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/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 89e1b6611f..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); @@ -491,6 +497,9 @@ void specialOptionalJoin( elFromFirstNotFoundAction(it); } it1 = next1; + if (it1 == end1) { + break; + } checkCancellation(); @@ -669,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>; @@ -801,6 +810,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/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/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/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..0f37415ba4 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); @@ -44,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 33ebc86b1d..45b05e4334 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 @@ -42,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; }; @@ -49,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; }; @@ -62,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 @@ -79,8 +90,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/CMakeLists.txt b/test/CMakeLists.txt index bd375f4826..3a04b9d201 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) @@ -152,6 +152,8 @@ addLinkAndDiscoverTestSerial(IdTripleTest index) addLinkAndDiscoverTestSerial(DeltaTriplesTest index) +addLinkAndDiscoverTest(DeltaTriplesCountTest index) + addLinkAndDiscoverTest(EngineTest engine) addLinkAndDiscoverTest(JoinTest engine) @@ -452,3 +454,5 @@ addLinkAndDiscoverTest(UrlParserTest) addLinkAndDiscoverTest(ServerTest engine) addLinkAndDiscoverTest(ExecuteUpdateTest engine) + +addLinkAndDiscoverTest(GraphStoreProtocolTest engine) 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/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/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); } } 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/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/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/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}}}; diff --git a/test/OperationTest.cpp b/test/OperationTest.cpp index ec9ab35c7f..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" @@ -24,7 +25,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()) { @@ -242,12 +243,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); } } @@ -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/ServerTest.cpp b/test/ServerTest.cpp index 292d77f12b..0319b138d5 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 @@ -9,45 +10,33 @@ #include #include "util/GTestHelpers.h" +#include "util/HttpRequestHelpers.h" +#include "util/IndexTestHelpers.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, + 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_, 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,114 +44,277 @@ 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")), - ParsedRequestIs("/ping", {}, None{})); - EXPECT_THAT(parse(MakeGetRequest("/?cmd=stats")), - ParsedRequestIs("/", {{"cmd", {"stats"}}}, None{})); - EXPECT_THAT(parse(MakeGetRequest( + EXPECT_THAT(parse(makeGetRequest("/")), + ParsedRequestIs("/", std::nullopt, {}, None{})); + EXPECT_THAT(parse(makeGetRequest("/ping")), + ParsedRequestIs("/ping", std::nullopt, {}, None{})); + EXPECT_THAT(parse(makeGetRequest("/?cmd=stats")), + ParsedRequestIs("/", std::nullopt, {{"cmd", {"stats"}}}, None{})); + EXPECT_THAT(parse(makeGetRequest( "/?query=SELECT+%2A%20WHERE%20%7B%7D&action=csv_export")), - ParsedRequestIs("/", {{"action", {"csv_export"}}}, - Query{"SELECT * WHERE {}"})); + ParsedRequestIs("/", std::nullopt, {{"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 {}"})); + ParsedRequestIs("/", std::nullopt, {{"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, - "query=SELECT%20%2A%20WHERE%20%7B%7D")), - ParsedRequestIs("/", {}, Query{"SELECT * WHERE {}"})); + ParsedRequestIs("/", std::nullopt, {{"send", {"100"}}}, + 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( + 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-" "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 {}"})); + Query{"SELECT * WHERE {}", + {DatasetClause{Iri(""), false}, + DatasetClause{Iri(""), true}, + DatasetClause{Iri(""), true}}})); + ; 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")), - 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 {}"})); + 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("/", std::nullopt, {{"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 {}")), - 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 {}"})); -} - -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")); + 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("/", std::nullopt, + {{"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("/", std::nullopt, + {{"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("/", std::nullopt, + {{"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("/", std::nullopt, + { + {"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("/", std::nullopt, + { + {"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}}})); + 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) { @@ -226,9 +378,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 +402,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, @@ -284,3 +436,94 @@ 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)); +} + +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/SparqlAntlrParserTest.cpp b/test/SparqlAntlrParserTest.cpp index 0803f96f03..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. @@ -1798,8 +1829,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)")); @@ -2050,10 +2088,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 +2225,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) { 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/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 71a24908a2..891b990e92 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>; @@ -1242,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"}), 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. +} diff --git a/test/TripleComponentTest.cpp b/test/TripleComponentTest.cpp index 2cc5c74cb3..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" @@ -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; @@ -207,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")); +} diff --git a/test/UrlParserTest.cpp b/test/UrlParserTest.cpp index eb6aeebc4d..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,22 +109,51 @@ 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}})); } + +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/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/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 020c952670..9e3a4b74da 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); } } } 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 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_; } 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( 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); } // _____________________________________________________________________________ diff --git a/test/util/HttpRequestHelpers.h b/test/util/HttpRequestHelpers.h new file mode 100644 index 0000000000..cb57ead971 --- /dev/null +++ b/test/util/HttpRequestHelpers.h @@ -0,0 +1,47 @@ +// 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_view 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